Compare commits

..

1381 Commits

Author SHA1 Message Date
Nick O'Leary
27b54199f5 Merge pull request #4904 from node-red/rel404
Bump for 4.0.4 release
2024-10-09 11:10:29 +01:00
Nick O'Leary
90ea3c15b3 Bump for 4.0.4 release 2024-10-09 10:56:14 +01:00
Nick O'Leary
acd500fe3d Merge pull request #4893 from node-red/update-deps
Update dev dependencies
2024-10-09 10:30:56 +01:00
Nick O'Leary
8988176c22 Merge pull request #4900 from hardillb/allow-number-user-properties-mqtt
Allow msg.userProperties to have number values
2024-10-09 10:28:55 +01:00
Nick O'Leary
35f35705a5 Merge pull request #4889 from GogoVega/fix-4888
Fix wrong unlock state when event is triggered after deployment
2024-10-09 10:28:17 +01:00
Nick O'Leary
a0033697ea Update cookie 2024-10-09 10:25:35 +01:00
GogoVega
49a3eded59 Apply code review + add comments
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-10-08 18:10:28 +02:00
Ben Hardill
d50ccea017 Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-10-08 16:51:09 +01:00
Nick O'Leary
3ef205fb33 Merge pull request #4903 from joebordes/joebordes/i18n_002
i18n(App) update with latest language file changes
2024-10-08 16:49:14 +01:00
Nick O'Leary
b1bfba8b01 Merge pull request #4892 from GogoVega/fix-4891
Fix `link call` node can call out of a subflow
2024-10-08 16:39:19 +01:00
Ben Hardill
21832a0bd0 Merge remote-tracking branch 'refs/remotes/origin/allow-number-user-properties-mqtt' into allow-number-user-properties-mqtt 2024-10-08 13:59:01 +01:00
Ben Hardill
a0b4fc8372 Convert to string 2024-10-08 13:58:29 +01:00
GogoVega
3812ed5ed3 Link call node cannot call a link in a subflow
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-10-07 21:13:13 +02:00
Nick O'Leary
bdb545d6eb Update dependencies 2024-10-07 16:30:38 +01:00
Joe Bordes
7650620a78 i18n(App) update with latest language file changes 2024-10-04 18:06:01 +02:00
Ben Hardill
34d8b3ed5e Merge branch 'master' into allow-number-user-properties-mqtt 2024-10-01 16:35:56 +01:00
Ben Hardill
e3acc49d5e Allow msg.userProperties to have number values
fixes #4899
2024-10-01 16:31:28 +01:00
Nick O'Leary
c3da827222 Merge pull request #4895 from dxdc/patch-1
fix typo: depreciated
2024-09-27 10:40:14 +01:00
Daniel Caspi
1053fc5121 fix typo: depreciated 2024-09-27 04:35:51 -05:00
Nick O'Leary
cec7a86b54 Update dev dependencies 2024-09-27 09:42:07 +01:00
GogoVega
16c49306f3 Fix link call node can call out of a subflow 2024-09-26 19:01:44 +02:00
GogoVega
32540dd0e6 Fix wrong unlock state when event is triggered 2024-09-23 16:16:53 +02:00
Nick O'Leary
a3c5b75368 Merge pull request #4884 from node-red/rel403
Bump for 4.0.3
2024-09-17 15:24:13 +01:00
Nick O'Leary
f7a43f83e5 Bump for 4.0.3 2024-09-17 14:23:43 +01:00
Nick O'Leary
98ca0f163e Merge pull request #4850 from kazuhitoyokoi/master-fixpagetitle
Refresh page title after changing tab name
2024-09-17 14:19:56 +01:00
Nick O'Leary
5a3e6925e5 Update packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js 2024-09-17 14:15:42 +01:00
Nick O'Leary
d270da74b4 Merge pull request #4853 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v4.0.2 (again)
2024-09-17 13:53:22 +01:00
Nick O'Leary
204e0f636b Merge pull request #4874 from node-red/update-dependencies
Update dependencies
2024-09-17 13:52:08 +01:00
Nick O'Leary
1132d39118 Merge pull request #4883 from node-red/4881-fix-quickAdd-state
Stay in quick-add mode following context menu insert
2024-09-17 13:51:54 +01:00
Nick O'Leary
f2227c8f65 Merge pull request #4854 from kazuhitoyokoi/master-fixexample
Fix typo in flow example name
2024-09-17 13:49:05 +01:00
Nick O'Leary
55cfd6fa99 Merge pull request #4882 from hardillb/tls-update
Move SNI, ALPN and Verify Server cert out of check
2024-09-17 13:45:18 +01:00
Nick O'Leary
83b30f1c18 Fix linting 2024-09-16 16:45:22 +01:00
Nick O'Leary
aa74d8160a Stay in quick-add mode following context menu insert
Fixes #4881
2024-09-16 16:39:41 +01:00
Ben Hardill
f44868384e Move SNI, ALPN and Verify Server cert out of check
This moves the SNI, ALPN and the Verify Server Cert check out
of the check for if the supplied certs/key are actually valid
as these may be still required for correct behaviour.

part of #4877
2024-09-16 11:55:05 +01:00
Nick O'Leary
674eb36e15 Merge pull request #4861 from lobis/master
GitHub: Add citation file to enable "Cite this repository" feature
2024-09-13 15:00:00 +01:00
Nick O'Leary
e6882337c1 Merge pull request #4879 from node-red/fix-link-quick-add
Do not include Junction type in quick-add for virtual links
2024-09-13 14:58:47 +01:00
Nick O'Leary
d48594220e Merge pull request #4875 from node-red/remove-util-log
Remove use of util.log
2024-09-13 14:58:03 +01:00
Nick O'Leary
64cdee6d36 Update express 2024-09-13 14:57:09 +01:00
Nick O'Leary
a16c72b6a8 Merge pull request #4878 from node-red/4876-mqtt-status
Set status of mqtt nodes to "disconnected" when deregistered from broker
2024-09-13 14:41:16 +01:00
Nick O'Leary
89839f433b Do not include Junction type in quick-add for virtual links 2024-09-13 14:35:46 +01:00
Steve-Mcl
3597759692 set status of mqtt nodes when deregistered from broker 2024-09-12 14:40:39 +01:00
Nick O'Leary
d4b001a74e Fix tests for util.log removal 2024-09-11 18:28:35 +01:00
Nick O'Leary
d76b0d39cd Remove use of util.log 2024-09-11 17:47:26 +01:00
Nick O'Leary
92911f6714 Update tough-cookie 2024-09-11 17:37:50 +01:00
Nick O'Leary
380d3be819 Merge pull request #4845 from node-red/multiplayer-cursor
Multiplayer cursor tracking
2024-09-11 17:13:39 +01:00
Nick O'Leary
6dfc5e0a41 Merge pull request #4869 from node-red/4864-hide-flow-add-options
Hide add-flow options when disabled via editorTheme
2024-09-11 17:13:08 +01:00
Nick O'Leary
d4f18c1731 Merge pull request #4872 from node-red/4863-fix-multi-env-config-select
Fix env-var config select when multiple defined
2024-09-11 17:12:55 +01:00
Nick O'Leary
e5a0d4094f Merge pull request #4873 from node-red/4858-ensure-mqtt-will-payload-is-string
Ensure will payload is a string
2024-09-11 17:12:40 +01:00
Nick O'Leary
163c1c5b98 Merge pull request #4857 from GogoVega/refix-4749
Fix subflow outbound-link filter
2024-09-11 16:12:30 +01:00
Gauthier Dandele
912a30b28d Improve the filter to handle all cases + comments
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-09-11 17:02:18 +02:00
Nick O'Leary
de0546b251 Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2024-09-11 15:23:03 +01:00
Nick O'Leary
1b375b81f3 Merge pull request #4856 from GogoVega/french-translations
Add French translations for v4.0.2
2024-09-11 14:11:57 +01:00
Nick O'Leary
b4bbae247d Merge branch 'master' into master-addjpn 2024-09-11 14:11:46 +01:00
Nick O'Leary
20ae4a7272 Update dependencies 2024-09-11 13:58:16 +01:00
Nick O'Leary
7f77a414ec Ensure will payload is a string 2024-09-11 12:10:37 +01:00
Nick O'Leary
2e5a3f4949 Fix env-var config select when multiple defined 2024-09-11 11:28:42 +01:00
Nick O'Leary
593726ecb8 Hide actionList in context menu if disabled 2024-09-11 10:41:23 +01:00
Nick O'Leary
9745904c5b Remove duplicate key 2024-09-09 16:52:45 +01:00
Nick O'Leary
8c53d95610 Hide add-flow options when disabled via editorTheme 2024-09-09 16:49:17 +01:00
Luis Antonio Obis Aparicio
27604fef23 add citation file 2024-08-31 04:32:01 +02:00
GogoVega
2c4dc3334d Fix subflow outbound-link filter 2024-08-27 15:46:48 +02:00
GogoVega
1ebb66f6ca Add French translations for properties in info sidebar 2024-08-18 13:11:56 +02:00
GogoVega
aed9a868ab Add French translations for batch and join nodes 2024-08-18 12:55:28 +02:00
GogoVega
e6ca3af1ed Add French translations for Project feature 2024-08-18 12:53:55 +02:00
Kazuhito Yokoi
edc01552f9 Fix invalid property error in range node example (#4855) 2024-08-14 19:42:07 +01:00
Kazuhito Yokoi
b4d29d4d4a Fix typo in flow example name 2024-08-14 00:25:59 +09:00
Kazuhito Yokoi
b5785dab9c Add Japanese translations for v4.0.2 (again) 2024-08-13 23:48:28 +09:00
Nick O'Leary
e67afb2611 Merge pull request #4851 from node-red/4839-fix-moving-link-wires
Fix moving link wires
2024-08-12 19:38:53 +01:00
Nick O'Leary
f88e536ee0 Merge pull request #4844 from node-red/4843-quick-add-placement
Adjust type search dialog position to prevent x-overflow
2024-08-12 19:38:40 +01:00
Nick O'Leary
509d609020 Fix moving link wires 2024-08-12 16:53:23 +01:00
Nick O'Leary
a4ec0f7959 Merge pull request #4838 from lorenz-maurer/lorenz-maurer-patch-1
fix: modulesInUse might be undefined
2024-08-12 09:26:38 +01:00
Nick O'Leary
884c887e0d Merge pull request #4849 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v4.0.2
2024-08-12 09:18:44 +01:00
Kazuhito Yokoi
e0059943df Add Japanese translations for project feature 2024-08-12 00:19:21 +09:00
Kazuhito Yokoi
d11934beeb Add Japanese translations for batch and join nodes 2024-08-12 00:14:28 +09:00
Nick O'Leary
964271f9c7 Multiplayer: add real-time cursor tracking 2024-08-05 16:18:05 +01:00
Steve-Mcl
97b773d257 Merge branch '4843-quick-add-placement' of github.com:node-red/node-red into 4843-quick-add-placement 2024-08-04 11:38:17 +01:00
Stephen McLaughlin
b6a25518e3 Merge branch 'master' into 4843-quick-add-placement 2024-08-04 11:38:04 +01:00
Steve-Mcl
b62c17f94b use workspace width to contain the search box 2024-08-04 11:34:29 +01:00
Steve-Mcl
886fb45ad2 Adjust type search dialog position to prevent x-overflow
fixes #4843
2024-08-04 11:30:48 +01:00
Lorenz
7b95e64a60 fix: modulesInUse might be undefined 2024-07-22 14:13:09 +02:00
Kazuhito Yokoi
af42664d73 Refresh page title after changing tab name 2024-07-20 13:36:37 +09:00
Nick O'Leary
8af821d380 Merge pull request #4837 from node-red/knolleary-patch-1
Back off node 22.5
2024-07-18 17:22:32 +01:00
Nick O'Leary
97ee6c6745 Back off node 22.5 2024-07-18 17:17:50 +01:00
Nick O'Leary
b5fb15cd9b Merge pull request #4828 from GogoVega/fix-menu-selection
Fix menu to enable/disable selection when it's a group
2024-07-18 16:36:02 +01:00
Nick O'Leary
998219ae9a Merge pull request #4829 from node-red/Let-Batch-node-honour-parts-to-close-early
Let batch node terminate "early" if msg.parts set to end of sequence
2024-07-18 16:34:46 +01:00
Nick O'Leary
e8f0f80f65 Merge pull request #4835 from node-red/Fix-Split-Node-Name-Caps
Fix unintentional Capitalisation in Split node name
2024-07-18 16:33:48 +01:00
Dave Conway-Jones
ff35c46d5d Fix unintentianal Capitalisation in Split node
to close #4832
2024-07-16 14:37:16 +01:00
Dave Conway-Jones
05c924a9df Let batch node terminate "early" if msg.parts set to end of sequence
(ie index = count -1). Useful to close at end of files etc.
2024-07-04 15:09:55 +01:00
GogoVega
14ac309b6e Fix menu to enable/disable selection when it's a group 2024-07-02 11:38:56 +02:00
Nick O'Leary
29b128e5e0 Merge pull request #4825 from node-red/rel402
Bump for 4.0.2
2024-07-01 16:49:13 +01:00
Nick O'Leary
c0f1581370 Bump for 4.0.2 2024-07-01 16:38:28 +01:00
Nick O'Leary
9420a52ec7 Merge pull request #4818 from bonanitech/header-border
Use a more subtle border on the header
2024-07-01 12:52:09 +01:00
Mauricio Bonani
c113b3de13 Use a not-so-dark color for the header border 2024-07-01 06:18:17 -04:00
Mauricio Bonani
29058c163a Merge branch 'master' into header-border 2024-07-01 06:14:35 -04:00
Nick O'Leary
bb110ea230 Merge pull request #4824 from GogoVega/improve-french-translations
Improve the editor's French translations
2024-07-01 10:55:33 +01:00
Nick O'Leary
785f220cd8 Merge pull request #4821 from node-red/orphaned-monco-editors
Clean up orphaned editors
2024-07-01 10:48:49 +01:00
GogoVega
16570410a5 Improve French translations 2024-07-01 09:43:42 +02:00
GogoVega
8085eda431 Add missing French translations from v4 2024-06-30 22:27:38 +02:00
Steve-Mcl
6503498f0a Clean up orphaned editors
closes #4820
2024-06-29 18:14:54 +01:00
Nick O'Leary
da787a9993 Merge pull request #4812 from GogoVega/fix-required-prop
Fix node validation if the property is not required
2024-06-28 17:21:40 +01:00
Nick O'Leary
c873b57094 Merge pull request #4815 from node-red/update-cookie-auth
Allow auth cookie name to be customised
2024-06-28 16:58:36 +01:00
Nick O'Leary
93974ccd92 Merge pull request #4816 from node-red/4814-multiplayer-uncaught
Guard against undefined sessions in multiplayer
2024-06-28 16:58:25 +01:00
Nick O'Leary
d7aa792f97 Merge pull request #4817 from node-red/mermaid-caching
Ensure mermaid.min.js is cached properly between loads of the editor
2024-06-28 16:58:14 +01:00
Mauricio Bonani
375fa9da64 Use a more subtle border on the header 2024-06-28 09:51:23 -04:00
Nick O'Leary
28c41e17ad Ensure mermaid.min.js is cached properly between loads of the editor 2024-06-28 14:37:51 +01:00
Nick O'Leary
da3ad40968 Add more guards for undefined session 2024-06-28 14:19:18 +01:00
GogoVega
2464d9ad95 Check required prop for each case instead of top level 2024-06-28 14:15:51 +02:00
Nick O'Leary
011b47a108 Guard against undefined sessions in multiplayer 2024-06-28 11:41:05 +01:00
Nick O'Leary
ea747711c3 Allow auth cookie name to be customised 2024-06-28 10:24:51 +01:00
GogoVega
19a8fa09a8 Fix node validation if property is not required 2024-06-27 09:16:03 +02:00
Nick O'Leary
1b5b3f7f88 Merge pull request #4804 from node-red/rel401
Update for 4.0.1 release
2024-06-26 14:42:42 +01:00
Nick O'Leary
2123514c76 Update for 4.0.1 release 2024-06-26 14:38:28 +01:00
Nick O'Leary
379fbd7c7e Merge pull request #4803 from node-red/4798-include-groups-inflowapi
Ensure group nodes are properly exported in /flow api
2024-06-26 14:36:03 +01:00
Nick O'Leary
efdc1b1a1d Ensure group nodes are properly exported in /flow api 2024-06-26 14:30:33 +01:00
Nick O'Leary
52bbd82e18 Merge pull request #4802 from node-red/4801-fix-sf-instance-credentials
Ensure subflow instance credential property values are extracted
2024-06-26 11:57:31 +01:00
Nick O'Leary
20a9c051be Ensure subflow instance credential property values are extracted 2024-06-26 11:49:37 +01:00
Nick O'Leary
830e475969 Merge pull request #4800 from GogoVega/fix-config-select
Use `_ADD_` value for both `add new...` and `none` options
2024-06-26 10:51:39 +01:00
GogoVega
f75e2f221c Use _ADD_ value for add... and none options 2024-06-25 20:08:31 +02:00
Nick O'Leary
5a75440668 Merge pull request #4796 from node-red/Join-node-optional-use-of-parts
make using msg.parts optional in join node
2024-06-25 16:22:20 +01:00
Dave Conway-Jones
53d8b97fff join node - honour 3.x behaviour for old instances.
but don't use msg.parts for new instances in manual join mode unless set.
2024-06-25 12:57:09 +01:00
Nick O'Leary
c85667cc13 Merge pull request #4794 from node-red/4792-ui-proxy-fix
UI proxy should setup agents for both http_proxy and https_proxy
2024-06-25 10:56:44 +01:00
Dave Conway-Jones
1a8b37b4e3 make using msg.parts optional in join node 2024-06-24 21:05:00 +01:00
Steve-Mcl
40a2d90e08 remove .only 2024-06-24 20:06:22 +01:00
Steve-Mcl
ee269caa4a update and add tests to check correct proxy was returned for flow 2024-06-24 20:05:37 +01:00
Steve-Mcl
d820686e5a only initialise proxy if flow url is static 2024-06-24 20:04:26 +01:00
Steve-Mcl
aa2a585e00 should set https and http proxy agents for UI
closes #4792
2024-06-24 17:34:30 +01:00
Steve-Mcl
f9e6bccd46 Merge branch 'master' of github.com:node-red/node-red 2024-06-24 17:19:07 +01:00
Nick O'Leary
3230654ecd Merge pull request #4788 from GogoVega/fix-config-node-select
Fix the config node select value assignment
2024-06-24 16:59:16 +01:00
Gauthier Dandele
a5b53ee373 Fix the selected value if no config nodes available
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-06-24 17:37:28 +02:00
Nick O'Leary
ac420247ae Merge pull request #4786 from kazuhitoyokoi/master-addtooltip
Add tooltip for number of subflow instance on info tab
2024-06-24 16:25:54 +01:00
Steve-Mcl
61198bd7e3 Merge branch 'master' of github.com:node-red/node-red 2024-06-24 11:58:03 +01:00
GogoVega
9c511b6674 Revert the updateConfigNodeSelect changes 2024-06-24 12:39:22 +02:00
Nick O'Leary
9d054543a7 Merge pull request #4785 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v4.0.0
2024-06-24 11:33:00 +01:00
Nick O'Leary
fc3ec2a0d7 Merge pull request #4791 from node-red/4787-revert-default-user-agent
Remove default user agent
2024-06-24 11:30:54 +01:00
Stephen McLaughlin
e7ef73222f Remove default user agent
closes #4787
2024-06-24 11:21:51 +01:00
GogoVega
6623e56a1e Fix setting of config node select value 2024-06-23 21:41:30 +02:00
Kazuhito Yokoi
582eca1877 Add tooltip for number of subflow instance on info tab 2024-06-23 20:59:26 +09:00
Kazuhito Yokoi
2783100f84 Add Japanese translations for v4.0.0 2024-06-23 20:48:34 +09:00
Nick O'Leary
cb0c484579 Merge branch 'dev' 2024-06-20 13:26:22 +01:00
Nick O'Leary
a1bf270ba6 Update tour version 2024-06-20 13:23:34 +01:00
Nick O'Leary
be5694f149 Merge pull request #4777 from node-red/rel400
Update for 4.0 final release
2024-06-20 11:53:56 +01:00
Nick O'Leary
4ff364e2c3 Reorder tour features 2024-06-20 11:45:23 +01:00
Nick O'Leary
2fa6f35873 Update for 4.0 final 2024-06-20 11:41:25 +01:00
Nick O'Leary
2a4fb7123d Merge pull request #4772 from node-red/rel3111
Bump for 3.1.11 release
2024-06-18 11:44:55 +01:00
Nick O'Leary
38a77d2b78 Bump for 3.1.11 release 2024-06-18 11:37:59 +01:00
Nick O'Leary
dc239db256 Merge pull request #4762 from node-red/Update-German-delay-node-translations
Add/Update German Translations for delay node
2024-06-17 16:07:20 +01:00
Nick O'Leary
a622d19ba7 Merge pull request #4761 from node-red/4759-add-httpStaticCors
Add `httpStaticCors`
2024-06-17 16:06:58 +01:00
Nick O'Leary
9842d9116c Merge pull request #4763 from node-red/bump-nr-admin
Update dependencies
2024-06-17 11:46:02 +01:00
Nick O'Leary
19ea8f8515 Update dependencies 2024-06-17 11:24:59 +01:00
Dave Conway-Jones
4ba3c937a8 Add/Update German Translations for delay node
To close #4748
2024-06-14 15:36:25 +01:00
Nick O'Leary
dbd3f0f85b Add httpStaticCors to default settings file 2024-06-14 15:21:02 +01:00
Nick O'Leary
48a2876c48 Add support for httpStaticCors 2024-06-14 15:18:40 +01:00
Nick O'Leary
10398b05d8 Merge pull request #4756 from node-red/sync-dev
Sync master to dev
2024-06-11 14:34:24 +01:00
Nick O'Leary
3a91fc17fd Merge branch 'master' into sync-dev 2024-06-11 10:14:01 +01:00
Nick O'Leary
02893d3e78 Merge pull request #4755 from node-red/rel3110
Bump for 3.1.10 release
2024-06-11 09:22:16 +01:00
Nick O'Leary
5124bc6bf8 Bump for 3.1.10 release 2024-06-10 21:14:20 +01:00
Nick O'Leary
3952a23ba3 Merge pull request #4747 from GogoVega/tooltip-input-validation
Add tooltip and message validation to `typedInput`
2024-06-10 20:44:34 +01:00
Nick O'Leary
1048b16f3c Merge pull request #4754 from node-red/4752-add-rewired-to-stoplist
Include rewired nodes when calculating Modified Flows stop list
2024-06-10 20:44:20 +01:00
Nick O'Leary
bbbbb1b1e0 Merge pull request #4753 from node-red/4751-fix-group-json
Fix clone of group env var properties
2024-06-10 20:42:49 +01:00
Nick O'Leary
14b452c996 Merge pull request #4750 from GogoVega/fix-4749
Fix losing links when importing a copy of links into a subflow
2024-06-10 20:42:36 +01:00
GogoVega
bb91a08939 Just move the block before the Id is updated 2024-06-10 18:28:30 +02:00
GogoVega
bffa923f05 Revert all changes for RED.popover 2024-06-10 18:02:28 +02:00
Nick O'Leary
526b3fda91 Include rewired nodes when calculating Modified Flows stop list 2024-06-10 16:56:49 +01:00
Nick O'Leary
27fc89ba33 Merge pull request #4744 from node-red/bcrypt-bump
Replace bcrypt with @node-rs/bcrypt
2024-06-10 16:16:19 +01:00
Nick O'Leary
d70b7ea924 Fix clone of group env var properties
Closes #4751
2024-06-10 16:15:06 +01:00
GogoVega
1d342a778d Fix new_nodes should be used instead of node_map 2024-06-05 14:12:35 +02:00
GogoVega
476016cbcc Fix node_map does not contain as key the new id of a copied node 2024-06-05 12:22:22 +02:00
GogoVega
bd2c020e84 My bad, options can be undefined 2024-06-03 21:11:13 +02:00
GogoVega
da7c7ede02 Fix a String value is an error, so return the value 2024-06-03 18:57:41 +02:00
GogoVega
34ed9c5cd8 Don't use the typedInput tooltip to get the validation message 2024-06-03 18:41:40 +02:00
Nick O'Leary
47dd08e74a Merge pull request #4746 from node-red/export-flow-save-view
Export Nodes dialog refinement
2024-06-03 16:11:22 +01:00
GogoVega
6aae50294f Cleanup + fix popover.getContent can be a function and apply #4450 changes 2024-06-02 20:16:10 +02:00
Steve-Mcl
ec2f6ec46f remember and restore active export view 2024-06-02 14:41:41 +01:00
Steve-Mcl
36805b6872 move export format button to main dialog 2024-06-02 14:41:15 +01:00
Steve-Mcl
d78cb2fec7 new common i18n message "format" 2024-06-02 12:10:30 +01:00
Nick O'Leary
51edb1ef19 Fix bcrypt.compare usage 2024-05-31 15:31:41 +01:00
Nick O'Leary
2ad3af1864 Replace bcrypt with @node-rs/bcrypt 2024-05-31 15:16:32 +01:00
Nick O'Leary
525d7356fe Merge pull request #4743 from node-red/rel400-b4
Update for 4-beta.4
2024-05-30 16:54:39 +01:00
Nick O'Leary
bdf37b0546 Update changelog 2024-05-30 16:50:02 +01:00
Nick O'Leary
0d330332f1 Merge pull request #4739 from Rotzbua/feat_build_fail
feat(grunt): fail if files are missing
2024-05-30 16:47:38 +01:00
Nick O'Leary
a5f290fd47 Merge pull request #4736 from Rotzbua/fix_pi_script
fix(node-red-pi): node-red not started by path
2024-05-30 16:47:25 +01:00
Nick O'Leary
46f4144172 Merge pull request #4735 from Rotzbua/fix_html_meta
fix(editor): remove trailing slash
2024-05-30 16:47:02 +01:00
Nick O'Leary
356e332f66 Merge pull request #4616 from Steve-Mcl/proxy-logiv-dev-v4
Perform Proxy logic more like cURL
2024-05-30 16:46:42 +01:00
Nick O'Leary
d16060bdd9 Merge pull request #4733 from Rotzbua/chore_remove_deprecated
fix: remove deprecated mqtt.js
2024-05-30 16:45:58 +01:00
Nick O'Leary
3aeb4bd868 Update dependencies 2024-05-30 16:43:33 +01:00
Stephen McLaughlin
b8bcab109a Merge branch 'dev' into proxy-logiv-dev-v4 2024-05-30 16:33:44 +01:00
Nick O'Leary
b94045fd86 Update packages/node_modules/node-red/settings.js 2024-05-30 16:32:21 +01:00
Nick O'Leary
87992823d5 Update for 4.0 final release 2024-05-30 16:12:56 +01:00
Nick O'Leary
93b914d4b0 Merge pull request #4740 from node-red/resyn-dev
Resync master to dev
2024-05-30 14:15:26 +01:00
Nick O'Leary
61b12f6bbe Merge pull request #3743 from bonanitech/fix-build-script
Ensure all CSS variables are in the output file
2024-05-30 13:43:25 +01:00
Nick O'Leary
d71b22412b Merge pull request #4734 from GogoVega/fix-sidebar-config
Fix the Sidebar Config is not refreshed after a deploy
2024-05-30 13:40:12 +01:00
Rotzbua
d115d6c241 feat(grunt): fail if files are missing 2024-05-30 14:35:55 +02:00
Stephen McLaughlin
e561efb5c5 Merge branch 'dev' into proxy-logiv-dev-v4 2024-05-30 11:44:53 +01:00
Nick O'Leary
e408c6b376 Merge pull request #4729 from GogoVega/fix-4728
Fix checkboxes are not updated when calling `typedInput("value", "")`
2024-05-30 10:32:27 +01:00
Rotzbua
cd95c63daf fix(node-red-pi): node-red not started by path
Double quote to prevent globbing and word splitting.
https://github.com/koalaman/shellcheck/wiki/SC2086
2024-05-30 01:53:51 +02:00
Rotzbua
bc6a4a20d2 fix(editor): remove trailing slash
html validator:
Info: Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.
2024-05-30 01:42:16 +02:00
GogoVega
bf30c24e8e Fix the Sidebar Config is not refreshed after a deploy 2024-05-29 20:44:49 +02:00
Nick O'Leary
25205f7440 Merge pull request #4725 from node-red/monaco-0-49-0
Monaco-0-49-0
2024-05-29 17:37:46 +01:00
Stephen McLaughlin
6317420d4a Merge branch 'dev' into monaco-0-49-0 2024-05-29 11:14:33 +01:00
GogoVega
6c14ed0ef5 Revert and fix an undefined value returned instead of a false value 2024-05-28 22:08:42 +02:00
Rotzbua
f53bdc8257 fix: remove deprecated mqtt.js
deprecated since 2016 and node-red 0.13.4
2024-05-28 16:03:04 +02:00
Nick O'Leary
6d41ecdae0 Merge pull request #4716 from corentin-sodebo-voile/master
Fix panning with middle mouse button on windows 10/11
2024-05-28 10:28:57 +01:00
Nick O'Leary
7bd61f2c96 Merge pull request #4718 from node-red/4717-add-httpAdminCookieOptions
Add httpAdminCookieOptions
2024-05-28 10:15:46 +01:00
Nick O'Leary
76338d4d32 Merge pull request #4724 from Rotzbua/chore_isArray
chore: migrate deprecated `util.isArray`
2024-05-28 09:51:28 +01:00
Nick O'Leary
3f89bc2733 Merge pull request #4727 from kazuhitoyokoi/master-addjpn
Add Japanese translation for sidebar tooltip
2024-05-28 09:49:35 +01:00
Nick O'Leary
b9add237b2 Merge pull request #4726 from kazuhitoyokoi/dev-jpn
Add Japanese translations for 4.0.0-beta.3
2024-05-28 09:49:18 +01:00
Nick O'Leary
87a25df162 Merge pull request #4730 from GogoVega/translate-multiple-option-label
Translate the number of items selected in the options list
2024-05-28 09:49:03 +01:00
GogoVega
341f43610a Translate the number of items selected in the options list 2024-05-27 19:56:51 +02:00
GogoVega
67cdf3ef96 Fix checkboxes are not updated when calling typedInput("value", "") 2024-05-27 18:59:25 +02:00
Kazuhito Yokoi
cd98f448e9 Add Japanese translation for sidebar tooltip 2024-05-26 23:34:32 +09:00
Kazuhito Yokoi
b66af1c8e2 Fix typo 2024-05-26 23:00:52 +09:00
Kazuhito Yokoi
06bad61569 Add Japanese translations for 4.0.0-beta.3 2024-05-26 22:54:59 +09:00
Steve-Mcl
f58766eacf typo 2024-05-26 13:15:23 +01:00
Steve-Mcl
8e62b2a749 update monaco to latest 2024-05-26 13:15:10 +01:00
Rotzbua
9b86874c2d chore: migrate deprecated util.isArray
Deprecated: Use Array.isArray() instead.

Source: https://nodejs.org/docs/latest-v18.x/api/util.html#utilisarrayobject
2024-05-25 22:55:09 +02:00
Nick O'Leary
805ed593fb Apply httpAdminCookieOptions to session cookie 2024-05-23 17:01:48 +01:00
Nick O'Leary
c604ac2207 Allow session cookie options to be customised
Closes #4717
2024-05-23 16:56:43 +01:00
corentin-sodebo-voile
fac79fd068 Fix panning with middle mouse button on windows 10/11
Without preventDefault, when you try to drag the canvas with middle mouse button on Windows (e.g. in Chrome), the cursor change to a "scroll cursor" and the canvas scrolls endlessly instead of being dragged accurately.
2024-05-23 15:30:13 +02:00
Nick O'Leary
da97c5d558 Merge pull request #4715 from GogoVega/fix-validateNodeEditorProperty
Fix a checkbox should return a Boolean value and not the string `on`
2024-05-23 13:43:55 +01:00
Nick O'Leary
ae7b9fe62e Merge pull request #4714 from GogoVega/fix-4712
Deleting a grouped node should update the group
2024-05-23 13:43:26 +01:00
GogoVega
e52c2911da Fix a checkbox must return a boolean value and not on 2024-05-23 10:57:51 +02:00
GogoVega
ca37d1ec9d Merge branch 'dev' into tooltip-input-validation 2024-05-22 18:19:06 +02:00
Nick O'Leary
07a29ff779 Merge pull request #4711 from GogoVega/change-config-node-cursor
Change the Config Node cursor to `pointer`
2024-05-22 17:02:05 +01:00
Nick O'Leary
1b4a8ebe83 Merge branch 'master' into fix-4712 2024-05-22 17:01:29 +01:00
GogoVega
abf2eacf18 Revert and fix without using RED.group.removeFromGroup 2024-05-21 19:48:21 +02:00
Gauthier Dandele
f808f4e2e8 Apply suggestions from code review
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-05-21 19:01:57 +02:00
Nick O'Leary
ca33d6b799 Merge pull request #4713 from GogoVega/add-missing-sidebar-tooltips
Add missing tooltips to Sidebar
2024-05-21 17:15:52 +01:00
Nick O'Leary
3fd2d07c75 Merge pull request #4706 from node-red/4648-readonly-feedback
Show lock on deploy if user is read-only
2024-05-21 17:14:48 +01:00
Nick O'Leary
940740f15d Merge pull request #4710 from node-red/4704-node-edit-history
Allow nodes to return additional history entries in onEditSave
2024-05-21 17:10:23 +01:00
GogoVega
51208fcd0c Fix indexOf returns -1 2024-05-21 16:06:15 +02:00
GogoVega
707152d82f Deleting a grouped node should update the group 2024-05-21 15:07:26 +02:00
GogoVega
5538f6dd8a Add missing tooltips to Sidebar 2024-05-21 12:58:29 +02:00
GogoVega
d601e2caa4 Change the config node cursor to pointer 2024-05-21 11:37:01 +02:00
Nick O'Leary
46fdf56c79 Allow nodes to return additional history entries in onEditSave 2024-05-20 16:41:44 +01:00
Stephen McLaughlin
b76d692a65 Merge pull request #4707 from node-red/4569-cli-version
Add --version cli args
2024-05-17 17:36:41 +01:00
Nick O'Leary
6600910163 Add os details to --version output 2024-05-17 17:16:01 +01:00
Nick O'Leary
a6973bd7ed Add node.js version to --version output 2024-05-17 17:13:50 +01:00
Nick O'Leary
d58127730f Add --version cli arg 2024-05-17 17:10:37 +01:00
Nick O'Leary
5494c167fc Show lock on deploy if user is read-only 2024-05-17 17:04:08 +01:00
Nick O'Leary
c5ae0be7b1 Merge pull request #4705 from node-red/rel4b31
Bump for beta 4-beta3-1
2024-05-16 10:39:08 +01:00
Nick O'Leary
b653914ee0 Bump for beta3-1 repackage 2024-05-16 10:37:16 +01:00
Nick O'Leary
c107c5fc92 Merge pull request #4698 from node-red/rel4-beta3
Update for beta.3
2024-05-15 16:58:47 +01:00
Nick O'Leary
0980c03129 Merge pull request #4700 from GogoVega/addfrench-v4beta3
Add French translation of tour for 4.0.0-beta.3
2024-05-15 16:52:07 +01:00
GogoVega
f6c3fdc806 Add French translation of tour 2024-05-14 20:54:00 +02:00
Nick O'Leary
2c2628d816 Update changelog and tour 2024-05-14 17:44:26 +01:00
Nick O'Leary
56fe2801eb Merge branch 'master' into dev 2024-05-14 17:27:02 +01:00
Nick O'Leary
87b1ee9642 Bump package version 2024-05-14 17:24:28 +01:00
Nick O'Leary
e1c36d232b Merge pull request #4697 from node-red/sync-dev-2
Sync master branch to dev
2024-05-14 17:23:31 +01:00
Nick O'Leary
13ee8cec24 Merge branch 'master' into sync-dev-2 2024-05-14 17:22:42 +01:00
Nick O'Leary
a977b87cb3 Merge pull request #4692 from node-red/improve-conflict-handling
Improve background-deploy notification handling
2024-05-14 16:04:58 +01:00
Nick O'Leary
14dfb9aef8 Merge pull request #4695 from node-red/improve-merge-diff-view
Improve diff view display of nodes that have only moved
2024-05-14 16:04:27 +01:00
Nick O'Leary
d520cde57a Merge pull request #4314 from Rotzbua/remove_outdated_node_check
fix: remove outdated Node 11+ check
2024-05-14 13:54:15 +01:00
Nick O'Leary
70167d7d1d Merge pull request #4694 from Rotzbua/test_add_node_22
feat(ci): add new nodejs v22
2024-05-14 13:52:34 +01:00
Nick O'Leary
3389c8160b Handle group w/h properties as move rather than change 2024-05-14 13:51:52 +01:00
Nick O'Leary
c214710f8e Improve diff view display of nodes that have only moved 2024-05-14 13:35:05 +01:00
Nick O'Leary
ac6a4945cb Merge pull request #4690 from Rotzbua/fix_node_requirement
fix(node): increase required node >=18.5
2024-05-14 13:29:52 +01:00
Rotzbua
fd1a001a23 feat(ci): add new nodejs v22 2024-05-14 13:39:32 +02:00
Rotzbua
f3c561cd86 fix(node): increase required node >=18.5
Statement can be simplified by increasing the required minor version.
2024-05-14 12:27:38 +02:00
Nick O'Leary
4e33e785fb Handle multiple background deploys 2024-05-14 10:27:55 +01:00
Nick O'Leary
f55ee6e665 Merge pull request #4685 from node-red/4683-preserve-full-error-obj
Pass full error object in Function node and copy over cause property
2024-05-13 15:25:02 +01:00
Nick O'Leary
edc5e88d5a Merge pull request #4689 from Rotzbua/fix_workaroud_dns
fix(dns): remove outdated node check
2024-05-13 14:41:35 +01:00
Nick O'Leary
47bf166a6e Update packages/node_modules/node-red/lib/red.js 2024-05-13 14:41:24 +01:00
Nick O'Leary
cf26209790 Merge pull request #4688 from Rotzbua/remove_import_polyfill
fix(polyfill): remove import module polyfill
2024-05-13 14:39:33 +01:00
Nick O'Leary
e55ebde170 Merge pull request #4686 from Rotzbua/fix_typo
Fix typo
2024-05-13 14:38:35 +01:00
Nick O'Leary
a745ddc164 Fix linting 2024-05-13 14:33:35 +01:00
Nick O'Leary
18d0fa2259 Improve background conflict handling 2024-05-13 14:19:24 +01:00
Rotzbua
d706c9cb37 fix: remove outdated Node 11+ check 2024-05-12 22:51:05 +02:00
Rotzbua
20d2450cac fix(polyfill): remove import module polyfill
Was required for node <12.17.
2024-05-12 22:38:03 +02:00
Rotzbua
34345461f1 fix(dns): remove outdated node check
Add reference to issue for this workaround.
2024-05-12 22:25:39 +02:00
Rotzbua
aa372a1707 Fix typo in source code comment 2024-05-12 18:25:08 +02:00
Nick O'Leary
03648dc7e8 Update tests for changed function node low-level output 2024-05-09 17:25:47 +01:00
Nick O'Leary
66a667fe58 Pass full error object in Function node and copy over cause property
Fixes #4683
2024-05-09 16:48:51 +01:00
Nick O'Leary
1bb3a0eca5 Merge pull request #4534 from patlux/master
Replacing vm.createScript in favour of vm.Script
2024-05-09 15:19:45 +01:00
Nick O'Leary
0e0bba25c1 Merge pull request #4679 from node-red/use-rfdc
Use rfdc for cloning pure JSON values
2024-05-08 16:18:13 +01:00
Nick O'Leary
af701d65ac Merge pull request #4681 from GogoVega/addfrench-v4beta.2
Add French translations for 4.0.0-beta.2
2024-05-08 16:17:57 +01:00
Nick O'Leary
08927dfb55 Merge pull request #4684 from node-red/4363-autoLogin-redirect-loop
Avoid login loops when autoLogin enabled but login fails
2024-05-08 15:48:21 +01:00
Nick O'Leary
b27483de9c Avoid login loops when autoLogin enabled but login fails
Fixes #4363
2024-05-08 15:09:51 +01:00
GogoVega
b02f69b77a Add translations for 4.0.0-beta.2 2024-05-05 15:00:42 +02:00
Nick O'Leary
598b0c84ab Merge pull request #4657 from node-red/tab-close-middle-click
Hide workspace tab on middle mouse click
2024-05-03 16:59:28 +01:00
Nick O'Leary
22cc8da088 Apply suggestions from code review 2024-05-03 16:59:17 +01:00
Nick O'Leary
a70618cdef Merge pull request #4666 from node-red/multiplayer-2
[multiplayer] Add user presence indicators
2024-05-03 16:52:50 +01:00
Nick O'Leary
faf142cf66 Merge pull request #4676 from kazuhitoyokoi/dev-fixpulldependencies
Enable updating dependency node of package.json in project feature
2024-05-03 16:52:33 +01:00
Nick O'Leary
1a3cc06935 Use rfdc module for cloning when we know its pure JSON 2024-05-03 16:45:50 +01:00
Nick O'Leary
a712a9363b Merge pull request #4674 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for 4.0.0-beta.2
2024-05-03 16:18:50 +01:00
Steve-Mcl
67e716466f handle middle click hide-tab in onclick handler 2024-04-29 20:14:45 +01:00
Steve-Mcl
3fae03da98 Merge branch 'dev' into tab-close-middle-click 2024-04-29 19:04:47 +01:00
Kazuhito Yokoi
361391ceb8 Load the latest project files when retrieving project information 2024-04-29 16:02:56 +09:00
Kazuhito Yokoi
bf0ca38350 Enable updating dependencies of package.json in project feature 2024-04-29 02:12:08 +09:00
Kazuhito Yokoi
437c28e2b8 Fix typos in welcome tour for 4.0.0-beta.2 2024-04-28 21:27:25 +09:00
Kazuhito Yokoi
c05d18ada1 Add Japanese translations for 4.0.0-beta.2 2024-04-28 21:22:15 +09:00
Nick O'Leary
cfb300ec06 Merge pull request #4672 from node-red/allow-blank-env-var-substitutions
Allow blank strings to be used for env var property substitutions
2024-04-24 23:37:23 +02:00
Nick O'Leary
236e668201 Allow blank strings to be used for env var property substitutions
Fixes #4663
2024-04-24 22:58:11 +02:00
Nick O'Leary
211d420fb2 Merge pull request #4667 from node-red/fix-subflow-property-undo
Fix undo of subflow env property edits
2024-04-23 23:45:46 +02:00
Nick O'Leary
c9b902c2b4 Merge pull request #4658 from node-red/fix-subflow-conf-type
Fix saving of conf-type properties in module packaged subflows
2024-04-23 23:45:29 +02:00
Nick O'Leary
b8ca4665c1 Merge pull request #4660 from JoshuaCWebDeveloper/patch-1
Fix three error typos in monaco.js
2024-04-23 23:45:16 +02:00
Nick O'Leary
ac8b1e19b7 Merge pull request #4662 from hardillb/timeout-npm-install
Add npm install timeout notification
2024-04-23 23:44:51 +02:00
Nick O'Leary
960af87fb0 Ensure subflow change state is cleared after deploy 2024-04-23 21:17:35 +02:00
Nick O'Leary
de7339ae97 Fix undo of subflow env property edits 2024-04-23 20:39:14 +02:00
Nick O'Leary
595933d046 Fix linting 2024-04-23 09:40:01 +02:00
Nick O'Leary
789426f80e Add user presence indication to tabs and nodes 2024-04-23 09:27:35 +02:00
Stephen McLaughlin
0995af62b6 Merge pull request #4664 from ZJvandeWeg/patch-3
docs: Add closing paragraph tag
2024-04-20 13:54:37 +01:00
Zeger-Jan van de Weg
c2e03a40b4 docs: Add closing paragraph tag
Minor change that only improves xpath parsing.
2024-04-20 14:20:59 +02:00
Ben Hardill
148e64c3da Update packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-04-18 14:22:50 +01:00
Ben Hardill
c6289ebb2c Merge branch 'dev' into timeout-npm-install 2024-04-18 12:12:53 +01:00
Ben Hardill
5f4ece6813 Move translation 2024-04-18 11:47:49 +01:00
Ben Hardill
c990ec39d6 revert DELETE change 2024-04-18 11:35:51 +01:00
Ben Hardill
1fdc600ecd Add npm install timeout notification
part of https://github.com/node-red/node-red/issues/4622
2024-04-18 11:27:32 +01:00
Joshua Carter
c855050bcf Fix three error typos in monaco.js 2024-04-15 08:09:26 -07:00
Nick O'Leary
e354d2ce29 Fix saving of conf-type properties in module packaged subflows 2024-04-12 14:08:07 +01:00
Nick O'Leary
d218af8619 Merge branch 'master' into dev 2024-04-12 13:04:54 +01:00
Steve-Mcl
d938e5fb6b close tab on middle mouse click 2024-04-12 11:42:55 +01:00
Nick O'Leary
29ed5b2792 Merge pull request #4655 from node-red/rel319
Bump for 3.1.9 release
2024-04-11 19:22:24 +01:00
Nick O'Leary
e39216e65a Bump for 3.1.9 release 2024-04-11 19:15:46 +01:00
Nick O'Leary
7ac7f9b4c8 Merge pull request #4654 from node-red/fix-subflow-recursion-check
Prevent subflow being added to itself
2024-04-11 19:12:43 +01:00
Stephen McLaughlin
4709eb9d49 Merge pull request #4652 from node-red/fix-windows-spawn
Fix use of spawn on windows with cmd files
2024-04-11 17:51:13 +01:00
Nick O'Leary
c13b8266dd Prevent subflow being added to itself 2024-04-11 17:05:10 +01:00
Nick O'Leary
bd58431603 Fix use of spawn on windows with cmd files 2024-04-11 14:40:29 +01:00
Nick O'Leary
3075b82792 Add one more tour image 2024-04-05 11:18:17 +01:00
Nick O'Leary
240082481f Merge pull request #4646 from node-red/rel400b2
Bump for beta.2
2024-04-05 10:53:03 +01:00
Nick O'Leary
ea95552285 Bump for beta.2 2024-04-04 18:25:10 +01:00
Nick O'Leary
5358b06123 Merge pull request #4645 from node-red/fix-mp-tests
Add placeholder tests for multiplayer
2024-04-04 16:19:53 +01:00
Nick O'Leary
99391431da Add placeholder tests for multiplayer 2024-04-04 16:17:30 +01:00
Nick O'Leary
d396f50a9a Merge pull request #4627 from GogoVega/button-add-config-node
Separate the "add new config-node" option into a new (+) button
2024-04-04 16:11:52 +01:00
Nick O'Leary
affa8ea42b Apply suggestions from code review 2024-04-04 16:08:59 +01:00
Nick O'Leary
d711b01fe5 Merge pull request #4629 from node-red/multiplayer-1
Introduce multiplayer feature
2024-04-04 15:24:29 +01:00
Nick O'Leary
6e7fa6f921 Merge branch 'dev' into button-add-config-node 2024-04-03 14:02:40 +01:00
Nick O'Leary
343cde75a2 Merge pull request #4644 from node-red/resyn-dev
Resync recent fixes from master to dev
2024-04-03 14:02:17 +01:00
Nick O'Leary
2dc446e45b Merge branch 'dev' into resyn-dev 2024-04-03 13:57:10 +01:00
Nick O'Leary
884b7fa16a Merge pull request #4643 from node-red/fix-subflow-mod-config-select
Fix handling of subflow config-node select type in sf module
2024-04-03 13:54:41 +01:00
Nick O'Leary
173e065b68 Merge pull request #4639 from node-red/4638-fix-change-replace-bool
Fix change node handling of replacing with boolean
2024-04-02 20:07:01 +01:00
Nick O'Leary
9a3cb0b2b5 Merge pull request #4640 from node-red/fix-subflow-init-err
Guard refresh of unknown subflow
2024-04-02 20:06:47 +01:00
Nick O'Leary
6beae5a806 Merge pull request #4642 from node-red/4641-fix-subflow-module-debug-logging
Fix subflow module sending messages to debug sidebar
2024-04-02 20:06:31 +01:00
Nick O'Leary
66f4008bb8 Fix handling of subflow config-node select type in sf module 2024-04-02 20:01:48 +01:00
Nick O'Leary
a0636632a1 Fix subflow module sending messages to debug sidebar
Fixes #4641
2024-04-02 17:42:19 +01:00
Nick O'Leary
5dfa47ab6c Guard refresh of unknown subflow 2024-04-02 15:54:34 +01:00
Nick O'Leary
e9efe493f9 Remove only 2024-04-02 13:59:15 +01:00
Nick O'Leary
3bd782e62a Fix change node handling of replacing with boolean
Fixes #4638
2024-04-02 13:57:19 +01:00
Steve-Mcl
9b49cb2b50 Merge branch 'dev' into proxy-logiv-dev-v4 2024-03-31 16:49:28 +01:00
Nick O'Leary
963fe87f14 Merge pull request #4637 from node-red/tidy-up-palette-state
Ensure palette filter reapplies and clear up unknown categories
2024-03-28 15:36:05 +00:00
Nick O'Leary
ade4679e8c Merge pull request #4636 from node-red/rel318
Bump for 3.1.8
2024-03-28 15:23:07 +00:00
Nick O'Leary
40060a470b Merge pull request #4635 from node-red/sync-dev
Sync dev
2024-03-28 15:22:57 +00:00
Nick O'Leary
a6e8fbb54a Ensure palette filter reapplies and clear up unknown categories 2024-03-28 15:21:04 +00:00
Nick O'Leary
410b938442 Bump for 3.1.8 2024-03-28 15:02:02 +00:00
Nick O'Leary
ab7e9f94fa Merge branch 'dev' into sync-dev 2024-03-28 14:56:36 +00:00
Nick O'Leary
28e9ccd372 Merge pull request #4634 from node-red/pr_4623
Retain Palette categories collapsed and filter to localStorage
2024-03-28 14:53:15 +00:00
Nick O'Leary
9a66d9addd Merge pull request #4620 from node-red/pr_4387
Add support for plugin (only) modules to the palette manager
2024-03-28 14:35:33 +00:00
Nick O'Leary
8843bda477 Merge pull request #4628 from node-red/comms-updates
Comms API updates
2024-03-28 14:33:10 +00:00
Nick O'Leary
3278303eec Clear localStorage state on logout 2024-03-28 14:31:31 +00:00
Nick O'Leary
f5fd6e3a36 Rework palette state management 2024-03-28 14:23:39 +00:00
Gauthier Dandele
a173e8e70f Apply suggestions from code review
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2024-03-28 12:57:04 +01:00
Nick O'Leary
19dcc3a683 Merge pull request #4632 from node-red/4625-sf-env-err-handling
Add validation and error handling on subflow instance properties
2024-03-28 11:10:28 +00:00
Nick O'Leary
20d067c1ea Merge pull request #4633 from node-red/4617-hide-library-context-options
Hide import/export context menu if disabled in theme
2024-03-28 11:10:14 +00:00
Nick O'Leary
9526566799 Hide import/export context menu if disabled in theme 2024-03-28 11:00:10 +00:00
Nick O'Leary
0b9dd82c91 Merge pull request #4631 from node-red/4626-subflow-change-notification
Show change indicator on subflow tabs
2024-03-27 19:10:39 +00:00
Nick O'Leary
19213434f9 Add validation to subflow instance env properties 2024-03-27 19:08:25 +00:00
Nick O'Leary
014691346a Handle malformed env var values and log errors 2024-03-27 18:23:12 +00:00
Nick O'Leary
6738b95c29 Merge pull request #4630 from node-red/bump-express
Bump dependencies
2024-03-27 18:11:54 +00:00
Nick O'Leary
6a8230ec1e Show change icon on subflow tabs
Fixes #4626
2024-03-27 18:10:04 +00:00
Nick O'Leary
5679d264b6 Bump dependencies 2024-03-27 18:00:06 +00:00
Nick O'Leary
b20c5f3a8d Merge pull request #4621 from GogoVega/addfrench-v4
Add French translations for 4.0.0-beta.1
2024-03-27 17:48:07 +00:00
Nick O'Leary
014f206e9c Initial multiplayer feature 2024-03-27 17:30:44 +00:00
Nick O'Leary
068b93befa CComms API updates 2024-03-27 17:21:12 +00:00
GogoVega
65d8872cea Separate the "add new config-node" option into a button 2024-03-27 14:59:49 +01:00
GogoVega
bffd1d61b2 Improve with error handling, storage cleanup and centralization in one object 2024-03-26 16:58:45 +01:00
GogoVega
4788b81220 Replace setTimeout with a listener 2024-03-26 13:05:38 +01:00
GogoVega
9a07fc03c6 Retain palette collapse and filter to localStorage 2024-03-25 20:47:55 +01:00
GogoVega
954f518030 Add French translations for 4.0.0-beta.1 2024-03-25 12:02:38 +01:00
GogoVega
9f8ff71757 Add French translations for TCP and CSV nodes 2024-03-25 11:58:35 +01:00
GogoVega
06dd59dc81 Add missing global-config translation 2024-03-25 10:51:12 +01:00
Nick O'Leary
37265cf4ef Merge pull request #4619 from node-red/4600-reset-workspace-index
Reset workspace index when clearing nodes
2024-03-21 17:38:39 +00:00
Nick O'Leary
2531a5283a Include module property in plugin metadata 2024-03-21 17:04:03 +00:00
Nick O'Leary
4cc1a5d846 Merge branch 'pr_4387' of github.com:node-red/node-red into pr_4387 2024-03-21 16:56:49 +00:00
Nick O'Leary
8a63275989 Merge pull request #4613 from kazuhitoyokoi/master-fixglobalconfig
Remove typo in global config
2024-03-21 16:54:01 +00:00
Nick O'Leary
2d3e5f4ce0 Fix linting 2024-03-21 16:43:47 +00:00
Nick O'Leary
5135545c6c Merge branch 'dev' into pr_4387 2024-03-21 16:41:24 +00:00
Nick O'Leary
fef93818c9 Ensure plugins api provides a filtered view 2024-03-21 16:37:05 +00:00
Nick O'Leary
7fc64a84e8 Bump test helper 2024-03-21 15:16:49 +00:00
Nick O'Leary
02f7cdd5aa Ensure all httpRequest test servers are ready before tests run 2024-03-21 15:03:37 +00:00
Nick O'Leary
d7dcceef60 Add debug for http tests 2024-03-21 11:32:29 +00:00
Nick O'Leary
ae5e1570ae Reset workspace index when clearing nodes
Fixes #4600
2024-03-21 11:14:34 +00:00
Nick O'Leary
50baad9624 Merge pull request #4612 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for 4.0.0-beta.1
2024-03-21 11:08:28 +00:00
Nick O'Leary
fc0041bd91 Merge pull request #4615 from Steve-Mcl/4614-monaco-and-types
Update monaco to latest and node types to 18 LTS
2024-03-21 09:25:52 +00:00
Steve-Mcl
283f7f5992 support env var NR_PROXY_MODE "legacy"|"strict" 2024-03-17 17:21:33 +00:00
Steve-Mcl
1cf7b95891 add missing ref to getProxyForUrl helper 2024-03-17 17:20:53 +00:00
Steve-Mcl
7bdb8db5ff http node fix tests 2024-03-17 17:11:29 +00:00
Steve-Mcl
de27968e4e (re)add node.insecureHTTPParser) 2024-03-17 17:10:56 +00:00
Steve-Mcl
d31abda28f typo 2024-03-17 17:01:25 +00:00
Steve-Mcl
dcfb4c9a79 update unit tests 2024-03-17 16:36:24 +00:00
Steve-Mcl
bf065ee11d default to strict mode but permit settings 2024-03-17 16:34:42 +00:00
Steve-Mcl
5e3cbadffc merge dev 2024-03-17 12:51:57 +00:00
Steve-Mcl
535ef82e48 Merge remote-tracking branch 'upstream/dev' into proxy-logiv-dev-v4 2024-03-17 12:50:59 +00:00
Steve-Mcl
c368bfea3f var/const updates 2024-03-17 12:37:42 +00:00
Steve-Mcl
4498e4100e Update Monaco editor and type completions
closes #4614
2024-03-17 11:08:47 +00:00
Kazuhito Yokoi
4f1e4faede Add Japanese translations for TCP and CSV nodes 2024-03-16 19:32:42 +09:00
Kazuhito Yokoi
3ca045394a Remove typo in global config 2024-03-16 18:51:13 +09:00
Kazuhito Yokoi
9a19a1113e Fix image paths in previous welcome tour 2024-03-16 18:44:25 +09:00
Kazuhito Yokoi
1cd550022b Fix typos 2024-03-16 18:43:03 +09:00
Kazuhito Yokoi
bad08bafd7 Add Japanese translations for 4.0.0-beta.1 2024-03-16 18:34:24 +09:00
Nick O'Leary
f041a21f22 Update publish-script for 4.x 2024-03-14 16:56:32 +00:00
Nick O'Leary
712d78ca39 Merge pull request #4611 from node-red/rel4-beta-tour
Update for 4.0.0-beta.1 release
2024-03-14 16:50:10 +00:00
Nick O'Leary
93f2910bd2 Bump to 4.0.0-beta.1 2024-03-14 16:05:15 +00:00
Nick O'Leary
d0ef12c486 Update tour for 4.0-beta.0 2024-03-14 16:01:54 +00:00
Nick O'Leary
241fd09053 Merge pull request #4587 from Steve-Mcl/config-in-subflow
Support config selection in a subflow env var
2024-03-14 15:11:54 +00:00
Nick O'Leary
208dd2a457 Merge pull request #4436 from marcus-j-davies/allow-ws-headers
Feat: Add ability to set headers for WebSocket client
2024-03-13 17:20:07 +00:00
Nick O'Leary
e34ee44b21 Update packages/node_modules/@node-red/nodes/core/network/22-websocket.html 2024-03-13 17:19:20 +00:00
Nick O'Leary
d5f59307b7 Update packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js 2024-03-13 17:08:58 +00:00
Nick O'Leary
64136cc565 Update packages/node_modules/@node-red/editor-client/src/js/ui/editors/envVarList.js 2024-03-13 17:08:42 +00:00
Nick O'Leary
3e2508c740 Merge branch 'dev' into config-in-subflow 2024-03-13 17:08:23 +00:00
Nick O'Leary
0853cd65b2 Merge pull request #4598 from joepavitt/header-styling
Improve the appearance of the Node-RED primary header
2024-03-13 17:07:13 +00:00
Nick O'Leary
01802c817b Merge branch 'master' into dev 2024-03-13 16:55:50 +00:00
Marcus Davies
7e10093bb8 Remove global access + reduce header list 2024-03-12 19:26:27 +00:00
Nick O'Leary
179032cd4d Merge pull request #4608 from node-red/rel317
Bump for 3.1.7 release
2024-03-12 17:43:32 +00:00
Nick O'Leary
6a6f0d04d6 Bump for 3.1.7 release 2024-03-12 14:25:41 +00:00
Nick O'Leary
add4d9758c Merge pull request #4603 from kazuhitoyokoi/master-addjpn
Add Japanese translation for v3.1.6
2024-03-11 16:07:28 +00:00
Kazuhito Yokoi
a0d3ea62b2 Add Japanese translation for v3.1.6 2024-03-10 23:36:20 +09:00
Joe Pavitt
54c17c3175 Merge branch 'dev' into header-styling 2024-03-08 13:48:43 +00:00
Nick O'Leary
80e60538e2 Merge pull request #4596 from node-red/revert-4352-rfdc
Revert "changed cloning library to rfdc for runtime module"
2024-03-08 11:40:21 +00:00
Joe Pavitt
84a76909e2 Improve the appearance of the Node-RED primary header 2024-03-08 10:51:25 +00:00
Nick O'Leary
033405fdbc Fully remove rfdc 2024-03-07 16:41:30 +00:00
Nick O'Leary
9444009a9b Revert "changed cloning library to rfdc for runtime module" 2024-03-07 16:40:32 +00:00
Nick O'Leary
29e9def314 Merge pull request #4595 from node-red/fix-rfdc-dependency
Add rfdc to @node-red/runtime package.json
2024-03-07 16:33:06 +00:00
Nick O'Leary
8832a1aa20 Add rfdc to @node-red/runtime package.json 2024-03-07 16:31:31 +00:00
Nick O'Leary
5beb6dbeee Merge pull request #4352 from anshumanr/rfdc
changed cloning library to rfdc for runtime module
2024-03-07 16:29:31 +00:00
Nick O'Leary
1261d26b23 Merge pull request #4439 from ralphwetzel/dev_link
Click on id in debug panel highlights node or flow
2024-03-07 16:27:15 +00:00
Nick O'Leary
0b9dd11fff Merge pull request #4513 from gorenje/html_node_content_and_attributes
HTML node: add option for collecting attributes and content
2024-03-07 16:24:28 +00:00
Dave Conway-Jones
08a607aa6a Merge branch 'dev' of https://github.com/node-red/node-red into dev 2024-03-07 16:21:03 +00:00
Nick O'Leary
e12efc320b Merge pull request #4408 from node-red/Fix-join-to-ignore-parts-in-manual-mode
Fix join node to ignore parts in manual mode
2024-03-07 16:20:05 +00:00
Dave Conway-Jones
3ded9de803 TCP node- when resetting, if no payload, stay disconnected 2024-03-07 16:19:51 +00:00
Nick O'Leary
d5b424910f Merge branch 'dev' into Fix-join-to-ignore-parts-in-manual-mode 2024-03-07 16:05:41 +00:00
Nick O'Leary
d94d13737f Merge pull request #4572 from kevinGodell/dev
let settings.httpNodeAuth accept single middleware or array of middlewares
2024-03-07 15:48:12 +00:00
Nick O'Leary
b1fa4918e3 Merge pull request #4386 from node-red/make-split/join-more-flexible
let split node specify property to split on, and join auto join correctly
2024-03-07 15:47:08 +00:00
Nick O'Leary
742aa2fa0d Merge branch 'dev' into make-split/join-more-flexible 2024-03-07 15:40:56 +00:00
Nick O'Leary
ce133c1c04 Merge pull request #4540 from Steve-Mcl/3934-csv-rfc4180
Add RFC4180 compliant mode to CSV node
2024-03-07 15:39:25 +00:00
Nick O'Leary
e4dc1779c3 Merge pull request #4590 from node-red/jsonata-update
Upgrade to JSONata 2.x
2024-03-07 14:28:35 +00:00
Nick O'Leary
22b4ab6bb2 Merge pull request #4468 from node-red/timestamp-formatting
Timestamp formatting
2024-03-07 14:28:23 +00:00
Nick O'Leary
7447e88a50 Merge pull request #4593 from hardillb/hardillb-patch-1
Update jsonata version
2024-03-07 14:26:02 +00:00
Ben Hardill
a193b79d3d Bump jsonata to match utils 2024-03-05 10:31:03 +00:00
Ben Hardill
da380f7464 Update jsonata version
Pulls in fix for CVE-2024-27307
2024-03-05 10:22:49 +00:00
Nick O'Leary
2dcff51125 Update timestamp formats 2024-03-04 16:51:06 +00:00
Nick O'Leary
b50e0533eb Add js date option to inject 2024-03-04 16:35:59 +00:00
Nick O'Leary
711545539f Allow typedInput timestamp to specify format 2024-03-04 16:35:54 +00:00
Nick O'Leary
a6cbceed28 Upgrade to JSONata 2.x 2024-03-04 16:32:31 +00:00
Nick O'Leary
269cf02c0b Merge pull request #4586 from node-red/rel316
Bump for 3.1.6 release
2024-03-01 11:47:57 +00:00
Steve-Mcl
837d17ab65 Merge branch 'dev' into config-in-subflow 2024-03-01 11:36:35 +00:00
Steve-Mcl
eff31c4bdc linting 2024-03-01 11:35:13 +00:00
Steve-Mcl
6a8f653b73 typo 2024-03-01 11:17:16 +00:00
Steve-Mcl
0cdb36f73d support constrained items in editable list 2024-03-01 11:08:50 +00:00
Steve-Mcl
db249356e6 permit config selection in subflow and subflow template 2024-03-01 11:07:26 +00:00
Steve-Mcl
d509c1a57c add "conf-types" to typedInput 2024-03-01 11:06:27 +00:00
Nick O'Leary
fb50e2772a Bump for 3.1.6 release 2024-03-01 10:50:06 +00:00
Nick O'Leary
058c97138a Merge pull request #4582 from node-red/3795-allow-env-var-in-num-field-validation
Do not flag env var in num typedInput as error
2024-02-26 17:01:45 +00:00
Nick O'Leary
828ae29aed Merge pull request #4581 from node-red/4579-fix-undef-env-vars
Handle undefined env vars
2024-02-26 17:01:27 +00:00
Nick O'Leary
6a0f45140c Merge pull request #4568 from JaysonHurst/fips
fix: Removed offending MD5 crypto hash and replaced with SHA1 and SHA256 …
2024-02-26 17:00:26 +00:00
Nick O'Leary
50a267528d Merge pull request #4580 from giscafer/remove-never-use-code
chore: remove never use import code
2024-02-26 16:58:20 +00:00
Nick O'Leary
220786be60 Do not flag env var in num typedInput as error 2024-02-26 16:55:01 +00:00
Nick O'Leary
fa78bb3d78 Handle undefined env vars
Fixes #4579
2024-02-26 16:17:09 +00:00
Nick O'Leary
9a32ebd0c0 Merge pull request #4578 from kazuhitoyokoi/master-fiximportdialog
Fix example flow name in import dialog
2024-02-26 16:09:10 +00:00
giscafer
4643f5e8cc chore: remove never use import code 2024-02-25 22:44:01 +08:00
Kazuhito Yokoi
7de0984d6d Update test case for example flow name 2024-02-25 17:38:46 +09:00
Kazuhito Yokoi
635334f096 Fix example flow name in import dialog 2024-02-25 17:04:42 +09:00
Nick O'Leary
f0d0990b5a Merge pull request #4575 from giscafer/master
fix: template node zh-CN translation
2024-02-22 13:03:24 +00:00
giscafer
43b3589451 fix: template node zh-CN translation 2024-02-22 13:02:06 +08:00
Nick O'Leary
016a19ba7c Merge pull request #4570 from node-red/fix-icon-scaling
Fix missing node icons in workspace
2024-02-20 10:37:33 +00:00
Nick O'Leary
6802539ccc Merge pull request #4571 from node-red/bump-node-version
Bump minimum version to node 18
2024-02-20 10:37:25 +00:00
Kevin Godell
74efaa3c2d let settings.httpNodeAuth accept single middleware or array of middlewares to replace built-in basic-auth middleware at top level of RED.httpNode 2024-02-19 16:42:14 -06:00
Nick O'Leary
a5223709ba Bump minimum version to node 18 2024-02-19 16:38:06 +00:00
Nick O'Leary
2291dc6132 Merge branch 'master' into dev 2024-02-19 16:14:58 +00:00
Nick O'Leary
aeb79bce2a Fix missing node icons in workspace 2024-02-19 16:07:22 +00:00
Jayson Hurst
0ab9b9a5fd Merge branch 'master' into fips 2024-02-16 17:53:30 -07:00
Jayson Hurst
56e58521bd Removed offending MD5 crypto hash and replaced with SHA1 and SHA256 crypto hashes to work with the FIPS crypto policy. 2024-02-17 00:35:03 +00:00
Steve-Mcl
b2548c158d fix layout and missing tip 2024-02-09 20:50:24 +00:00
Steve-Mcl
5a48d6d4cd Merge branch 'dev' into 3934-csv-rfc4180 2024-02-09 20:33:49 +00:00
Nick O'Leary
b10ef4c98c Merge pull request #4564 from node-red/rel315
Bump for 3.1.5 release
2024-02-08 15:37:48 +00:00
Nick O'Leary
3ff038fb98 Bump for 3.1.5 release 2024-02-08 15:32:53 +00:00
Nick O'Leary
adb498af24 Merge pull request #4562 from node-red/fix-require
Fix require of dns module
2024-02-07 15:24:10 +00:00
Nick O'Leary
fc67a2efc2 Merge pull request #4561 from node-red/4560-fix-global-env-cred
Ensure global creds object is initialised when adding first cred
2024-02-07 14:52:17 +00:00
Nick O'Leary
55771c7241 Fix require of dns module 2024-02-07 14:50:46 +00:00
Nick O'Leary
109fa5f04e Ensure global creds object is initialised when adding first cred 2024-02-07 10:02:22 +00:00
Nick O'Leary
1f412f3d78 Merge pull request #4558 from node-red/rel314
Updates for 3.1.4 release
2024-02-06 16:58:29 +00:00
Nick O'Leary
2b69f52c92 Bump dependencies in packages 2024-02-06 16:54:05 +00:00
Nick O'Leary
6e90798f16 Updates for 3.1.4 release 2024-02-06 16:51:59 +00:00
Nick O'Leary
3994b404a1 Merge pull request #4477 from GogoVega/french-translation-v3.1.3-changes
Add French translation of v3.1.3 changes
2024-02-06 16:37:20 +00:00
Nick O'Leary
0b7e8ec323 Merge pull request #4495 from joebordes/joebordes/i18n_001
i18n(es-ES) Spanish Spain translation
2024-02-06 16:36:45 +00:00
Nick O'Leary
bb0b547d5a Merge pull request #4550 from node-red/4549-improve-import-confict-dialog
Improve feedback in import dialog to show conflicted nodes
2024-02-06 16:36:15 +00:00
Nick O'Leary
1e1acc7ad7 Merge pull request #4556 from node-red/4548-handle-replacement-config-unknown
Handle modified-nodes deploy after replacing unknown config node
2024-02-06 16:36:02 +00:00
Nick O'Leary
1419432b04 Merge branch 'master' into joebordes/i18n_001 2024-02-05 16:47:55 +00:00
Nick O'Leary
1a521d7e09 Merge branch 'master' into 4548-handle-replacement-config-unknown 2024-02-05 16:46:04 +00:00
Nick O'Leary
5858d2789a Add FR translation 2024-02-05 16:44:55 +00:00
Nick O'Leary
6a1e4fac5a Merge pull request #4552 from guidoffm/patch-1
Update editor.json fix typo in German translation
2024-02-05 16:42:24 +00:00
Nick O'Leary
bab6e57a59 Merge pull request #4539 from node-red/4536-handle-undefined-default-export
Handle undefined default export when importing module
2024-02-05 16:36:06 +00:00
Nick O'Leary
4ed53fb622 Merge pull request #4531 from GogoVega/i18n-languages-list
Do not translate the list of available languages
2024-02-05 16:35:45 +00:00
Nick O'Leary
e17775c435 Merge pull request #4554 from node-red/dependabot/github_actions/github-actions-894fc5cb1d
Bump the github-actions group with 1 update
2024-02-05 16:32:44 +00:00
Nick O'Leary
7ee2b93b10 Merge pull request #4553 from lgrkvst/dev
Allow RED.view.select to select links
2024-02-05 16:28:17 +00:00
Nick O'Leary
565c212779 Handle modified-nodes deploy after replacing unknown config node
Fixes #4548
2024-02-05 16:22:58 +00:00
dependabot[bot]
b54e9d8d55 Bump the github-actions group with 1 update
Bumps the github-actions group with 1 update: [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request).


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

---
updated-dependencies:
- 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>
2024-02-01 07:29:17 +00:00
Christian Lagerkvist
cc611a7a02 Fixes #4551 2024-01-30 10:37:52 +01:00
Guido Mülller (Guido Mueller)
861c89a0cc Update editor.json fix typo in German translation 2024-01-30 10:35:54 +01:00
Nick O'Leary
4268a04a04 Improve feedback in import dialog to show conflicted nodes
Fixes #4549
2024-01-29 17:48:01 +00:00
Nick O'Leary
9bd7131914 Merge pull request #4544 from GogoVega/fix-creds-convert-node
(convertNode) Do not create the credentials object if there is nothing to export
2024-01-26 13:49:56 +00:00
Nick O'Leary
8485ca254f Merge pull request #4538 from node-red/4533-fix-subflow-instance-g-property-mapping
Ensure subflow instance node has g property set
2024-01-26 13:48:07 +00:00
Nick O'Leary
507f9b68eb Merge pull request #4546 from node-red/4545-importing-duplicate-subflow
Handle importing flow with existing subflow and instance node
2024-01-26 13:47:20 +00:00
Nick O'Leary
f7b726372f Handle importing flow with existing subflow and instance node
Fixes #4545
2024-01-25 17:26:52 +00:00
GogoVega
14811b5aec Do not create the credentials object if not exported 2024-01-25 15:35:40 +01:00
Steve-Mcl
1a9c34fe40 Merge branch 'dev' into 3934-csv-rfc4180 2024-01-23 10:59:05 +00:00
Steve-Mcl
ff8eb0ec2b Add RFC spec mode to CSV node
closes #3934
2024-01-23 10:35:08 +00:00
Nick O'Leary
c24f05c2cd Handle undefined default export when importing module
Fixes #4536
2024-01-22 16:54:51 +00:00
Nick O'Leary
d2dc1fcc80 Ensure subflow instance node has g property set 2024-01-22 16:28:22 +00:00
Patrick Wozniak
28907082f1 fix usage of vm.Script() 2024-01-21 02:16:00 +01:00
Patrick Wozniak
f83174c40a fix use of vm.Script by adding new 2024-01-21 01:23:07 +01:00
Patrick Wozniak
ec062d008f replace vm.createScript in favor of vm.Script 2024-01-21 01:13:00 +01:00
Patrick Wozniak
a587655a5a adding pollyfill for vm.createScript
adds support for bun.sh
2024-01-21 01:00:02 +01:00
Joe Bordes
97e05c8784 i18n(Editor) sync ES with #4531 to centralize list of languages 2024-01-20 20:36:47 +01:00
Joe Bordes
26cb03da42 i18n(Editor) sync ES with lastest changes 2024-01-20 20:36:01 +01:00
Joe Bordes
d5a8b1592c Merge branch 'master' into joebordes/i18n_001 2024-01-20 20:26:05 +01:00
Nick O'Leary
5b096bfd5e Merge pull request #4483 from gorenje/patch-1
Update index.mst
2024-01-19 17:13:09 +00:00
Nick O'Leary
a1e242ec1e Merge branch 'master' into patch-1 2024-01-19 16:14:20 +00:00
Nick O'Leary
5b9d002f56 Merge pull request #4529 from node-red/4397-hightlight-config-node-errors
Highlight errors in config node sidebar
2024-01-19 16:08:17 +00:00
GogoVega
dd57323889 Do not translate the list of available languages 2024-01-19 10:00:32 +01:00
Nick O'Leary
6620679008 Show standard validation triangle on config nodes 2024-01-16 17:35:50 +00:00
Nick O'Leary
f93654f680 Merge pull request #4527 from node-red/4485-copy-context-path
Include top level property name when copying path from context
2024-01-16 11:59:50 +00:00
Nick O'Leary
1bef0c32a2 Merge pull request #4519 from node-red/4479-ensure-env-not-modified
Clone objects types when getting env values
2024-01-16 11:59:37 +00:00
Nick O'Leary
f66b48e586 Merge pull request #4525 from node-red/fix-change-node-boolean-response
Fix change node to return boolean if asked
2024-01-16 11:58:30 +00:00
Nick O'Leary
a5725c59fd Merge pull request #4526 from node-red/4508
Ensure global-config credential env vars are merged on deploy
2024-01-16 11:58:20 +00:00
Nick O'Leary
4168bbb751 Merge pull request #4528 from node-red/4497-fix-config-footer-css
Modify node users info in config editor footer
2024-01-16 11:58:07 +00:00
Nick O'Leary
b0086edcf9 Update packages/node_modules/@node-red/editor-client/src/sass/tab-config.scss
Co-authored-by: Mauricio Bonani <bonanitech@gmail.com>
2024-01-15 20:16:18 +00:00
Nick O'Leary
89c2efe17d Highlight errors in config node sidebar
Fixes #4397
2024-01-15 17:49:17 +00:00
Nick O'Leary
9030b7d27c Merge pull request #4491 from ralphwetzel/master_fix_icon_resize
Fix icon scaling for non .svg icons
2024-01-15 17:20:14 +00:00
Nick O'Leary
de5111b13f Merge pull request #4522 from gorenje/remove_unused_code
21-httprequest.js remove unused code, because of broken use of toLowercase
2024-01-15 17:17:43 +00:00
Nick O'Leary
8600f4131e Modify node users info in config editor footer
Fixes #4497
2024-01-15 17:11:54 +00:00
Nick O'Leary
8a8245b560 Include top level property name when copying path from context
Fixes #4485
2024-01-15 16:54:16 +00:00
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
931a2344b4 Merge pull request #4480 from node-red/context-auto-complete
Add auto-complete to flow/global typedInput types
2024-01-15 15:53:46 +00:00
Dave Conway-Jones
dd3c75d298 Fix change node to return boolean if asked
to fix #4372
2024-01-14 12:56:25 +00:00
Gerrit Riessen
962fc5990e Merge remote-tracking branch 'nodered/master' into remove_unused_code 2024-01-11 12:04:02 +01:00
Gerrit Riessen
eb2f57fc0d removed unused code 2024-01-11 12:03:28 +01:00
Nick O'Leary
4a4a15de93 Fix context store handling in autocomplete 2024-01-09 01:05:09 +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
a007ab7f2e Merge pull request #4492 from node-red/envvar-auto-complete
Add auto-complete for env vars
2024-01-08 23:41:12 +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
3a6b1e86dc Clone objects types when getting env values
Fixes #4479
2024-01-08 20:56:17 +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
Gerrit Riessen
7b01457038 HTML node: add option to for attribites and content 2024-01-06 00:12:09 +01:00
Nick O'Leary
54e6d60fe5 Add simple caching of env var lookup 2024-01-05 21:07:20 +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
GogoVega
c3536fd7c7 Removes translation of interpolation keys 2024-01-01 15:02:48 +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
Joe Bordes
83279df0fa i18n(es-ES) node help screens 2023-12-27 18:28:54 +01:00
Joe Bordes
2550da9c6e i18n(es-ES) node help screens 2023-12-27 16:06:41 +01:00
Joe Bordes
041f00b811 i18n(es-ES) node help screens 2023-12-27 11:57:02 +01:00
Joe Bordes
21cd4aaeb6 i18n(es-ES) node help screens 2023-12-26 13:16:02 +01:00
Joe Bordes
70ce1e648d i18n(es-ES) messages and runtime. start working on node help screens 2023-12-26 11:42:23 +01:00
Joe Bordes
eab5a9772b i18n(es-ES) Spanish Spain translation 2023-12-24 20:50:40 +01:00
Kazuhito Yokoi
74ff0599d1 Fix location of subflow ports in palette 2023-12-23 19:51:57 +09:00
Nick O'Leary
c2710f4f6f Add auto-complete for env vars 2023-12-20 17:52:52 +00:00
Nick O'Leary
20187b51b1 Fix up cache scope 2023-12-20 16:51:34 +00:00
Nick O'Leary
4be6d57d98 Apply suggestions from code review
Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com>
2023-12-20 16:39:52 +00:00
Ralph Wetzel
c31e622160 Fix icon scaling for non .svg icons 2023-12-20 17:14:12 +01:00
GogoVega
1828d8a279 Add missing validation messages 2023-12-17 19:59:16 +01:00
GogoVega
282d52cf0b Fix non-boolean returned value + try to find validation msg in tooltip 2023-12-17 19:22:44 +01:00
Kazuhito Yokoi
70ea5c839a Add handling to disable items on context menu 2023-12-16 17:02:18 +09:00
Nick O'Leary
a77f8cc3e9 Clear context cache when closing edit dialog 2023-12-15 15:09:15 +00:00
Gerrit Riessen
0b0f1f8701 Update index.mst
Update two additional path specifications
2023-12-15 11:32:26 +01: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
Nick O'Leary
ea4c0cdbee Fix error when switching context types 2023-12-14 17:14:56 +00:00
Gerrit Riessen
b5e955bd5e Update index.mst
Avoid escaping slashes (`/`) in asset paths. 

Content is currently generated as:

```
<title>Node-RED</title>
<link rel="icon" type="image/png" href="favicon.ico">
<link rel="mask-icon" href="red&#x2F;images&#x2F;node-red-icon-black.svg" color="#8f0000">
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css?v=">
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css?v=">
<link rel="stylesheet" href="red/style.min.css?v=">
<link rel="stylesheet" href="vendor/monaco/style.css?v=">
</head>
<body spellcheck="false">
<div id="red-ui-editor"></div>
<script src="vendor/vendor.js?v="></script>
<script src="vendor&#x2F;monaco&#x2F;monaco-bootstrap.js?v="></script>
<script src="red&#x2F;red.min.js?v="></script>
<script src="red&#x2F;main.min.js?v="></script>
```

It still works of course, so feel free to ignore this change.
2023-12-13 17:29:39 +01:00
GogoVega
ba08cf0417 Add tooltip input validation msg 2023-12-12 17:17:33 +01:00
Nick O'Leary
7197153fd5 Support bracket-notation in auto complete when needed 2023-12-11 21:18:44 +00:00
Nick O'Leary
b9c1dedab3 Add auto-complete to flow/global typedInput types 2023-12-11 17:55:02 +00:00
Nick O'Leary
918943816f Merge branch 'master' into dev 2023-12-08 10:27:04 +00:00
GogoVega
b4b5d296d9 French translation of v3.1.3 changes 2023-12-08 09:47:00 +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
33cf34f7c7 Merge branch 'master' into dev 2023-12-04 15:58:45 +00: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
Ralph Wetzel
febc769df5 use of camelCasing: node_selector => nodeSelector 2023-11-21 20:48:52 +01: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
Ralph Wetzel
ea483218ea Click on id in debug panel highlights node or flow 2023-11-18 21:03:13 +01:00
anshumanr
c8f3ad8ac7 Merge branch 'dev' into rfdc 2023-11-17 13:17:26 -05: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
Marcus Davies
7916dc9c05 Slightly cleaner fasly checks 2023-11-14 22:31:25 +00:00
Marcus Davies
3123a5ee51 Feat: Add ability to set headers for WebSocket client 2023-11-14 20:44:04 +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
5b5b06cc06 Merge pull request #4406 from node-red/tcp-request-node-reset-when-in-stay-connected-mode
Let msg.reset  reset Tcp request node connection when in stay connected mode
2023-11-07 17:42:17 +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
Dave Conway-Jones
f49f692ffa Better fix for TCP node reset
now handles reply out node,
and can specify which connection to reset.
2023-11-03 11:57:16 +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
Dave Conway-Jones
10ce681d46 Fix join node to ignore parts in manual mode
to fix #1957
2023-11-01 13:59:09 +00: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
08c6ea94cb Merge pull request #4347 from ZJvandeWeg/zj-remove-production-flag-npm
npm: Remove production flag on npm invocation
2023-11-01 14:18:14 +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
fea1da5542 Merge pull request #4402 from node-red/Let-debug-status-length-be-settable
Let debug node status msg length be settable via settings
2023-11-01 14:13:38 +01:00
Nick O'Leary
33a978a246 Ensure credentials.get stub is restored after tests 2023-11-01 14:12:20 +01:00
Dave Conway-Jones
32e8f4eac6 Add help info 2023-11-01 12:33:57 +00:00
Dave Conway-Jones
bfe5a8a986 Update 31-tcpin.js
don't send if payload not defined.
2023-11-01 12:27:11 +00:00
Dave Conway-Jones
f2cb5ea44e Allow msg.reset to reset connection when tcp request in stay connected mode 2023-11-01 12:07:50 +00: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
Dave Conway-Jones
c7335ed25b Let debug node status msg length be settable via settings 2023-10-31 09:11:17 +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
Dave Conway-Jones
5fda57c730 too enthusiastic clearing up property
reverted
2023-10-18 13:51:40 +01:00
Ralph Wetzel
3df3096bb4 Fix to please linter 2023-10-18 08:07:49 +02:00
Ralph Wetzel
bb10d5bb94 Fix scope of variable 2023-10-18 08:07:06 +02:00
Ralph Wetzel
1704ab7454 Compensate for incomplete config data (e.g. test) 2023-10-18 08:06:15 +02:00
Ralph Wetzel
3275a76fb0 Adapt test after modification to uninstallModule 2023-10-18 08:04:52 +02:00
Ralph Wetzel
81937ddc45 Add plugin support to palette manager 2023-10-17 22:44:33 +02:00
Dave Conway-Jones
9fd929ac1e let split node specify property to split on
and let join auto join the correct property
or manually the specified one.
2023-10-17 21:12:13 +01:00
Mat Smith
44c0bbc61e Ctrl-Enter does not close tray (Monaco) #4377 2023-10-12 18:53:34 +01:00
Mauricio Bonani
3e6f0acf79 Refactor code 2023-10-03 15:42:33 -04:00
Mauricio Bonani
7f93d943d7 Merge remote-tracking branch 'upstream/master' into fix-build-script 2023-10-03 09:04:40 -04:00
ralphwetzel
5ac50fae3a Extend typedInput "num" type validity check to NaN, binary, octal & hex 2023-09-26 20:00:48 +02:00
anshumanr
c48a15c915 Merge branch 'dev' into rfdc 2023-09-25 20:24:23 -04: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
eb940d6d57 Merge pull request #4367 from hlovdal/timer_testing_fix
Timer testing fix
2023-09-25 18:19:30 +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
Håkon Løvdal
9091935d77 Update variable names 2023-09-25 18:53:11 +02: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
Håkon Løvdal
34e8d2b051 Add workaround for timers triggering too early in test 2023-09-24 18:16:59 +02:00
Håkon Løvdal
0c2ab13c48 Print all delta values in case of error, not just the last value
Which might not even be the one triggering the error condition.
2023-09-24 18:16:59 +02:00
Håkon Løvdal
9489953a8f Introduce timeout constant 2023-09-24 18:16:59 +02: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
asr
b0136d03ea reverted earlier change to use rfdc 2023-09-19 11:13:39 -04:00
asr
9fe73645ad remove package lock 2023-09-19 05:55:19 -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
ZJ van de Weg
54d4079457 npm: Remove production flag on npm invocation
When installing packages the `--production` flag used to be added to the
arguments that `npm` received. As npm wants developers to use the
`--omit=dev` flag instead it warned users on STDERR. Standard error was
captured by Node-RED and output to the logs as being an error. This
caught users off-guard and they expected something to have gone
wrong.

With this change the `--omit=dev` is used instead, to remove the
warning.

This change works for NPM of version 8 and beyond[1], included in
Node.JS 16. This change will not work on NPM version 6[2] which is included
in Node.JS 14[3].

[1]: https://docs.npmjs.com/cli/v8/commands/npm-install#omit
[2]: https://docs.npmjs.com/cli/v6/commands/npm-install
[3]: https://nodejs.org/en/download/releases#looking-for-latest-release-of-a-version-branch
2023-09-17 08:43:11 +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
asr
8e1a21e682 typo 2023-09-14 12:27:17 -04:00
asr
d84cdca43e Revert "typo"
This reverts commit 1c6dcd373d.
2023-09-14 12:24:52 -04:00
asr
1c6dcd373d typo 2023-09-14 12:22:34 -04:00
asr
4410ce1486 changed cloning library to rfdc 2023-09-14 11:40:36 -04: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
Nick O'Leary
cef3a01042 Merge pull request #4322 from node-red/prep4
Bump to 4.0.0-dev
2023-09-06 15:04:41 +01:00
Nick O'Leary
0c042abcab Bump to 4.0.0-dev 2023-09-06 14:45:45 +01:00
Nick O'Leary
d9bbac20f3 Merge pull request #4320 from node-red/dev
Sync `dev` to `master` for 3.1.0 release
2023-09-06 14:04:36 +01:00
Nick O'Leary
a48c57dd17 Merge pull request #4321 from node-red/rel-310
Bump version and changelog for 3.1.0 release
2023-09-06 13:57:44 +01:00
Nick O'Leary
77b235655c Bump version and changelog 2023-09-05 21:29:20 +01:00
Nick O'Leary
8dc0261993 Merge pull request #4319 from Rotzbua/fix_duplicate
Fix duplicate declaration
2023-09-05 20:41:38 +01:00
Rotzbua
65d2ad68d3 Fix duplicate declaration 2023-09-05 19:02:39 +02:00
Nick O'Leary
52fde088e9 Merge pull request #4318 from node-red/4315-small-catalogs-fix
Default filter to All Catalogues and show nodes for small lists
2023-09-05 17:59:57 +01:00
Nick O'Leary
ab6d537c3e Default filter to All Catalogues and show nodes for small lists 2023-09-05 17:49:50 +01:00
Nick O'Leary
3a6078a56a Merge pull request #4317 from node-red/update-deps-31
Dependency updates
2023-09-05 17:21:06 +01:00
Nick O'Leary
b0c3fefcab Dependency updates 2023-09-05 17:16:05 +01:00
Nick O'Leary
2bc739194e Merge pull request #4316 from node-red/4221-handle-concurrent-write-file
Ensure storage/util.writeFile handles concurrent write attempts
2023-09-05 15:57:27 +01:00
Nick O'Leary
afb06e8c9a Update packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/util.js 2023-09-05 15:11:47 +01:00
Nick O'Leary
2c1274ff76 Ensure storage/util.writeFile handles concurrent write attempts 2023-09-05 15:09:11 +01:00
Nick O'Leary
e18721a03e Merge pull request #4313 from Rotzbua/fix_https
Migrate http -> https for nodered.org
2023-09-05 14:20:32 +01:00
Rotzbua
25120e44ce Update outdated GH Actions 2023-09-05 14:56:14 +02:00
Rotzbua
aea32cc279 Migrate http -> https for nodered.org 2023-09-05 14:55:56 +02:00
Nick O'Leary
ce0feb2f42 Merge pull request #4310 from node-red/4206-ctrl-click-behaviour
Better distinguish between ctrl and meta keys on mac
2023-09-05 13:48:17 +01:00
Rotzbua
12a1c5c2f4 Add dependabot for GH Action workflows 2023-09-05 14:11:08 +02:00
Nick O'Leary
d307968880 Better distinguish between ctrl and meta keys on mac
Fixes #4206, #4205
2023-09-05 12:36:44 +01:00
Nick O'Leary
1f98d19f77 Merge pull request #4309 from node-red/pr_4265
Adding function timeout to settings file (#4265)
2023-09-05 11:44:41 +01:00
Nick O'Leary
9a934c941f Tidy up functionTimeout setting 2023-09-05 10:34:18 +01:00
Nick O'Leary
dbc7284b97 Merge pull request #4308 from node-red/revert-3991-http-request-form-array
Revert "Support form-data arrays"
2023-09-05 10:03:13 +01:00
Nick O'Leary
de63e17a4d Merge pull request #4305 from Rotzbua/add_node_20_to_test
Add Node 20 to GH Action test matrix
2023-09-05 09:44:23 +01:00
Nick O'Leary
1bad643b30 Merge pull request #4302 from node-red/4291-auto-sub-mqtt-error
Remove unnecessary check for clientid if autoUnsub set
2023-09-05 09:43:52 +01:00
Nick O'Leary
57c44d4b18 Merge pull request #4301 from node-red/4292-fix-catch-in-subflow
Handle group-scoped nodes inside subflow
2023-09-05 09:43:44 +01:00
Nick O'Leary
7d06133787 Merge pull request #4299 from node-red/4296-fix-function-setup-height
Fix function setup tab layout
2023-09-05 09:43:35 +01:00
Nick O'Leary
261998201b Merge pull request #4298 from node-red/4295-urlencode-context-api
Handle non-url-safe chars in context api
2023-09-05 09:43:17 +01:00
Nick O'Leary
9a899b929e Merge pull request #4297 from node-red/4294-fix-junction-quick-add
Ensure junction appears when filtering quick-add list
2023-09-05 09:42:42 +01:00
Nick O'Leary
46b15a51d4 Revert "Support form-data arrays" 2023-09-05 09:41:48 +01:00
Rotzbua
5fcace8776 Add Node 20 to GH Action test matrix 2023-09-04 14:31:30 +02:00
Nick O'Leary
a12826e719 Remove unnecessary check for clientid if autoUnsub set
Fixes #4291
2023-09-02 20:48:13 +01:00
Nick O'Leary
0dc024c722 Handle group-scoped nodes inside subflow
Fixes #4292
2023-09-01 16:38:46 +01:00
Nick O'Leary
f6f36c5599 Fix function setup tab layout 2023-09-01 16:17:29 +01:00
Nick O'Leary
18bd318da2 Handle non-url-safe chars in context api 2023-09-01 16:06:05 +01:00
Nick O'Leary
e1712073c9 Ensure junction appears when filtering quick-add list 2023-09-01 15:41:33 +01:00
Nick O'Leary
2478a7194e Merge pull request #4287 from kazuhitoyokoi/dev-fixjsonata
Update message catalogs for JSONata Expression editor
2023-08-21 15:42:37 +01:00
Nick O'Leary
89b8e88df3 Merge pull request #4290 from kazuhitoyokoi/dev-fixgitpull
Fix git pull operation in project feature
2023-08-21 15:42:11 +01:00
Kazuhito Yokoi
a7e80f351a Fix git pull operation in project feature 2023-08-21 11:18:01 +09:00
Stephen McLaughlin
c39d3db121 Merge pull request #4288 from kazuhitoyokoi/dev-tooltip4nodesearch
Add tooltip to relevance sort button in user settings UI
2023-08-20 08:45:25 +01:00
Kazuhito Yokoi
e7f9e61f2a Add tooltip to relevance sort button in user settings UI 2023-08-20 16:20:18 +09:00
Kazuhito Yokoi
3047ed1253 Improve message catalogs for JSONata Expression editor 2023-08-19 13:33:42 +09:00
Kazuhito Yokoi
cf0c7ce583 Fix typo in Korean message catalog 2023-08-19 13:24:34 +09:00
Kazuhito Yokoi
206211f9ef Fix typo in English message catalog 2023-08-19 13:21:42 +09:00
Kazuhito Yokoi
8220ff8a35 Add docs for function in Japanese message catalog 2023-08-19 13:19:55 +09:00
Nick O'Leary
927fc5424b Merge pull request #4283 from node-red/4282-dirty-junction-undo
Capture workspace dirty state when quick-adding junction
2023-08-18 15:53:25 +01:00
Nick O'Leary
64b94c68d3 Merge pull request #4284 from node-red/4268-add-clone-jsonata-docs
Add docs for $clone function
2023-08-18 15:52:45 +01:00
Nick O'Leary
3391d18942 Merge pull request #4286 from kazuhitoyokoi/dev-fixlinefeed
Change linefeed codes from "\r\n" to "\n" in Korean message catalogs
2023-08-18 15:52:27 +01:00
Kazuhito Yokoi
1414fed776 Change linefeed codes from "\r\n" to "\n" in Korean message catalogs 2023-08-18 20:39:13 +09:00
Nick O'Leary
f884cb015a Merge pull request #4285 from kazuhitoyokoi/dev-fixpermission
Fix file permissions of message catalogs
2023-08-17 10:29:25 +01:00
Kazuhito Yokoi
7e6a96db87 Fix file permissions of message catalogs 2023-08-17 18:17:58 +09:00
Nick O'Leary
966bbe2856 Add docs for $clone function 2023-08-16 16:02:33 +01:00
Nick O'Leary
2137211d42 Capture workspace dirty state when quick-adding junction
Fixes #4282
2023-08-16 15:58:08 +01:00
Nick O'Leary
cc5533c183 Merge pull request #4281 from kazuhitoyokoi/master-roundedcorners
Fix not rounded corners in project settings UI
2023-08-16 15:53:07 +01:00
Nick O'Leary
19787e5d28 Merge pull request #4280 from kazuhitoyokoi/master-svgicon2
Add svg icons for nodes
2023-08-16 15:52:47 +01:00
Kazuhito Yokoi
6e46666895 Fix not rounded corners in project settings UI 2023-08-16 20:14:22 +09:00
Kazuhito Yokoi
a87d7276c5 Add svg icons for nodes 2023-08-16 19:21:31 +09:00
Nick O'Leary
c630b4f770 Merge pull request #4276 from kazuhitoyokoi/master-svgicon
Add svg icons for nodes
2023-08-16 10:23:35 +01:00
Nick O'Leary
f719e5ad49 Merge pull request #4278 from node-red/update-tour
Update tour
2023-08-16 10:23:07 +01:00
Nick O'Leary
20557f20db Update tour for 3.1.0 2023-08-15 20:36:07 +01:00
Kazuhito Yokoi
66144c1eb5 Add svg icons for nodes 2023-08-15 16:16:04 +09:00
Nick O'Leary
054d6870d5 Merge pull request #4267 from kazuhitoyokoi/dev-tostring4filenodes
Fix handling in file nodes when number is specified as file name
2023-08-14 17:28:07 +01:00
Nick O'Leary
04aa4897fe Merge pull request #4275 from kazuhitoyokoi/dev-fixtest4jsonnode
Fix test cases of JSON node
2023-08-14 14:51:02 +01:00
Kazuhito Yokoi
72b22a4845 Fix test cases of JSON node 2023-08-14 22:44:02 +09:00
Kazuhito Yokoi
3fd2b5f4e1 Merge branch 'dev' into dev-tostring4filenodes 2023-08-14 21:16:57 +09:00
Kazuhito Yokoi
8a4128defb Fix handling in file nodes when 0 is specified as file name 2023-08-14 19:53:00 +09:00
Nick O'Leary
9542c22191 Merge pull request #4259 from kazuhitoyokoi/master-fixtooltip4textdiff
Add tooltip to text diff button
2023-08-14 11:11:32 +01:00
Nick O'Leary
15b8d79489 Merge pull request #4261 from DarshanDixit05/fix-Invalid-label
fix : Invalid label for attribute on the tray
2023-08-14 11:11:10 +01:00
Nick O'Leary
97470e94f1 Merge pull request #4262 from sammachin/patch-3
Handle 204 in httprequest JSON
2023-08-14 11:10:24 +01:00
Nick O'Leary
37a518b655 Merge pull request #4273 from kazuhitoyokoi/master-fixswitchnode
Fix broken text input in the switch node (again)
2023-08-14 11:09:05 +01:00
Nick O'Leary
312b2f8ddb Merge pull request #4270 from AuspeXeu/master
fix: undefined node and wrong method name
2023-08-14 11:08:04 +01:00
Kazuhito Yokoi
9caa6a3553 Fix broken text input in the switch node (again) 2023-08-13 15:33:37 +09:00
Christian Vaas
6c597bba5b fix: undefined node and wrong method name 2023-08-08 19:21:31 +02:00
Kazuhito Yokoi
0dd771351d Fix handling in file nodes when number is specified as file name 2023-08-06 20:35:33 +09:00
Kilian Hertel
f7b64b101e adding function timeout to settings file
adding function timeout to settings file
2023-08-04 14:20:49 +02:00
Sam Machin
ec86ec188b Update Test
I've changed the DELETE test to expect an empty object as the node is requesting an object response, this will therefore cover testing the new functionality.
The subsequent HEAD test also expects a 204 response but the requested type is txt so that will still expect an empty string response.
2023-08-02 14:32:39 +01:00
Sam Machin
2b01a3fcd3 Handle 204 in httprequest JSON
If the http statusCode is 204 (Success, No Content) and the node return type is set to JSON this sets msg.payload as an empty json object so as to supress the JSON parse error
2023-08-02 14:15:29 +01:00
DarshanDixit05
9540502d06 removed package lock 2023-07-31 20:22:46 +05:30
DarshanDixit05
792dedb1f1 fix : Invalid label for attribute on the tray 2023-07-31 19:34:12 +05:30
Kazuhito Yokoi
0bf1343442 Add tooltip to text diff button 2023-07-29 16:36:55 +09:00
Nick O'Leary
4d3e3a73fd Merge pull request #4257 from node-red/310b4
Bump versions for 3.1.0-beta.4
2023-07-26 17:10:20 +01:00
Nick O'Leary
1ffef393c2 Bump versions for 3.1.0-beta.4 2023-07-26 17:09:29 +01:00
Nick O'Leary
8b7b3e22d7 Merge branch 'master' into dev 2023-07-26 16:59:12 +01:00
Nick O'Leary
877aa75e4e Merge pull request #4254 from manuel-buchner/fix-html-syntax-httprequest
fix html syntax in 21-httprequest.html
2023-07-26 16:41:09 +01:00
Nick O'Leary
d21c0758b1 Merge pull request #4246 from kazuhitoyokoi/dev-fixfilenode
Fix JSONata in file nodes
2023-07-26 11:55:32 +01:00
Manuel Buchner
21be329008 fix html syntax in 21-httprequest.html 2023-07-18 17:16:17 +02:00
Stephen McLaughlin
e7b27ce7fb Merge pull request #4251 from kazuhitoyokoi/master-fixjpn4inject
Update Japanese translation for inject node
2023-07-16 09:26:33 +01:00
Stephen McLaughlin
fb2c0e5441 Merge pull request #4252 from kazuhitoyokoi/dev-jpn
Add Japanese translation for 3.1.0
2023-07-16 09:24:38 +01:00
Stephen McLaughlin
29898ea68f Merge pull request #4253 from kazuhitoyokoi/dev-fixtimeouticon
Fix timeout icon in function and link call nodes
2023-07-15 17:33:48 +01:00
Kazuhito Yokoi
9dcb8a729c Show timeout icon in link call node 2023-07-15 18:16:42 +09:00
Kazuhito Yokoi
b66237efa8 Show timeout icon in function node 2023-07-15 18:15:34 +09:00
Kazuhito Yokoi
6d2f855227 Add Japanese translation for 3.1.0 2023-07-15 17:34:08 +09:00
Kazuhito Yokoi
638aa0372b Update Japanese translation for inject node 2023-07-15 17:23:14 +09:00
Stephen McLaughlin
d5baa402c8 Merge pull request #4250 from node-red/4239-add-nr-subflow-name-env
Add NR_SUBFLOW_NAME/ID/PATH env vars
2023-07-14 17:24:14 +01:00
Nick O'Leary
ec7e594ec1 Add NR_SUBFLOW_NAME/ID/PATH env vars
Closes #4239
2023-07-14 17:14:39 +01:00
Nick O'Leary
3fad690d1e Merge pull request #4248 from node-red/node-catalog-filter
Improve Catalogue visibility
2023-07-14 16:35:54 +01:00
Steve-Mcl
15973768e2 improve catalog visibility/ux 2023-07-14 12:57:27 +01:00
Stephen McLaughlin
0d73a4b013 remove console debugging 2023-07-13 20:28:24 +01:00
Stephen McLaughlin
50f11faf1f remove console debugging 2023-07-13 20:28:16 +01:00
Stephen McLaughlin
cfb7406fb8 remove console debugging 2023-07-13 20:28:09 +01:00
Steve-Mcl
9008f063c3 hook up filtering to catalog selection 2023-07-13 19:49:17 +01:00
Steve-Mcl
368ac4ed5c i18n update 2023-07-13 19:48:34 +01:00
Steve-Mcl
6ac2e703a6 fix layout bugs 2023-07-13 19:48:13 +01:00
Kazuhito Yokoi
cd9a5f112a Add test cases to cover the JSONata issue in file nodes 2023-07-14 00:27:44 +09:00
Kazuhito Yokoi
f3847a17f3 Fix JSONata in file-in node 2023-07-14 00:24:50 +09:00
Steve-Mcl
1fa4aaf706 add catalog select 2023-07-13 13:27:42 +01:00
Nick O'Leary
db108a37cf Merge pull request #4230 from node-red/revert-4225-4196-fix-jsonata-env-var-async
Evaluate all env vars as part of async flow start
2023-07-11 23:06:09 +01:00
Nick O'Leary
a3e41d4f35 Update packages/node_modules/@node-red/util/lib/util.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2023-07-11 21:11:26 +01:00
Kazuhito Yokoi
80e33489d9 Fix JSONata in file nodes 2023-07-12 02:36:39 +09:00
Nick O'Leary
271b1327c7 Merge branch 'dev' into revert-4225-4196-fix-jsonata-env-var-async 2023-07-10 12:37:41 +01:00
Nick O'Leary
5d990ff4c5 Merge pull request #4242 from kazuhitoyokoi/master-fixswitchnode
Fix broken text input in the switch node
2023-07-10 12:35:50 +01:00
Nick O'Leary
69aacc6256 Merge pull request #4228 from node-red/4223-fix-http-request-keep-alive
Fix connection keep-alive in http request node
2023-07-10 12:31:28 +01:00
Nick O'Leary
a5066d529f Use flowChanged in diff to mark flows to restart 2023-07-10 12:30:36 +01:00
Nick O'Leary
5bf034f3c1 Merge pull request #4238 from ZJvandeWeg/patch-1
help: Template might be a better fit to create multiline strings
2023-07-10 12:10:32 +01:00
Nick O'Leary
0743ff371c Merge pull request #4244 from Steve-Mcl/3877-fix-touch-join-junc
3877-fix-touch-join-junc
2023-07-10 12:09:54 +01:00
Nick O'Leary
7481b78b16 Force reeval of env vars if group/flow/global envs change 2023-07-10 12:04:52 +01:00
Steve-Mcl
bd589aa140 fix touch mode wire linking 2023-07-10 10:24:28 +01:00
Steve-Mcl
8fa1d4def5 Merge remote-tracking branch 'upstream/dev' into dev 2023-07-10 10:17:17 +01:00
Stephen McLaughlin
fe3626035c Merge pull request #4243 from kazuhitoyokoi/master-jpn4subflow
Add Japanese translation for error message when creating subflow
2023-07-05 16:22:38 +01:00
Kazuhito Yokoi
c4019bd91d Add Japanese translation for error message when creating subflow 2023-07-02 15:51:35 +09:00
Kazuhito Yokoi
18e1b670ca Make handlings one line 2023-07-02 01:33:11 +09:00
Kazuhito Yokoi
cd76c934b6 Fix broken text input in the switch node 2023-07-02 00:40:15 +09:00
Zeger-Jan van de Weg
db98641a32 help: Template might be a better fit to create multiline strings
The inject node doesn't create multiline strings as the help text explains. While there's indeed many ways to circumvent this, the Template node might be more "low-code" than the function node is.
2023-06-27 09:53:39 +02:00
Nick O'Leary
59745fec0b Merge pull request #4231 from bvmensvoort/4219-missing-error-logging-for-config-nodes
Show errors and statuses of config nodes in the sidebar when no catch node is available
2023-06-23 16:46:51 +01:00
Nick O'Leary
8bcaea7830 Merge pull request #4232 from node-red/wiring-tweak
Improve wiring for horizontally aligned nodes
2023-06-23 16:46:02 +01:00
Nick O'Leary
56ed32e4a1 Add background to node status 2023-06-23 16:09:34 +01:00
Nick O'Leary
3209777aba Tidy up flow/util 2023-06-23 15:48:06 +01:00
Nick O'Leary
11f9ad8ca3 Remove debug for wiring 2023-06-23 12:29:33 +01:00
Nick O'Leary
26fc942c79 Improve wiring for horizontally aligned nodes 2023-06-23 12:24:48 +01:00
Nick O'Leary
f196493402 Evaluate global-config env on startup 2023-06-23 09:35:00 +01:00
Nick O'Leary
1c5fdb6ab6 Evaluate all env vars as part of async flow start 2023-06-23 02:11:57 +01:00
bvmensvoort
3ed530ed9e Merge branch 'dev' into 4219-missing-error-logging-for-config-nodes 2023-06-22 20:14:56 +02:00
Nick O'Leary
8db2972288 Restore expended env var tests 2023-06-22 10:24:29 +01:00
Nick O'Leary
51a0b68d8e Revert "Add callback to getSetting to support async jsonata access" 2023-06-22 10:17:48 +01:00
Steve-Mcl
2fdbe12a7f Merge remote-tracking branch 'upstream/dev' into dev 2023-06-22 09:35:44 +01:00
Nick O'Leary
2448e137c8 Merge pull request #4229 from node-red/4125-httpstatic-middleware
Add support for httpStatic middleware
2023-06-21 16:57:00 +01:00
Nick O'Leary
33899763ef Add support for httpStatic middleware 2023-06-21 16:47:47 +01:00
Steve-Mcl
d8f4f92e1d Merge remote-tracking branch 'upstream/dev' into dev 2023-06-21 16:39:32 +01:00
Nick O'Leary
ce679f90ee Merge pull request #4177 from k1ln/adding-timeout-to-functio-node
adding timeout attribute to function node
2023-06-21 15:57:44 +01:00
Nick O'Leary
8fb379079b Merge pull request #4225 from node-red/4196-fix-jsonata-env-var-async
Add callback to getSetting to support async jsonata access
2023-06-21 15:56:11 +01:00
Nick O'Leary
b382d048de Merge pull request #4227 from node-red/4196-add-callback-opt-to-env.get
Adds optional callback to env.get in function node
2023-06-21 15:55:40 +01:00
Nick O'Leary
610cb170e7 Fix connection keep-alive in http request node 2023-06-21 15:45:23 +01:00
Nick O'Leary
4d9fcaeebf Update env.get type hint 2023-06-21 15:00:27 +01:00
Nick O'Leary
aa0225f59f Apply suggestions from code review 2023-06-21 14:27:32 +01:00
Nick O'Leary
2571949e90 Merge pull request #4143 from inNETMonitoring/fix/joinManual
fix: closes #4142
2023-06-21 14:24:53 +01:00
Kilian Hertel
20d2c11154 Merge branch 'dev' into adding-timeout-to-functio-node 2023-06-21 15:24:24 +02:00
Nick O'Leary
3f604e9d93 Adds optional callback to env.get in function node 2023-06-21 14:20:23 +01:00
Nick O'Leary
234e92db12 Merge pull request #4215 from node-red/4213-fix-subflow-env
Fix subflow env var `length` not correctly handled in Node-RED
2023-06-21 14:06:05 +01:00
Nick O'Leary
01c56f6515 Merge pull request #4200 from GogoVega/french-translation-of-welcome-tours
French translation of Welcome Tours
2023-06-21 14:03:56 +01:00
Nick O'Leary
026be5a990 Merge pull request #4199 from GogoVega/french-translation-of-v3.1.0-beta.3
French translation of v3.1.0-beta.3 changes
2023-06-21 14:01:10 +01:00
Nick O'Leary
aafb86ef09 Merge branch 'dev' into adding-timeout-to-functio-node 2023-06-21 13:35:48 +01:00
Nick O'Leary
1a24efe85d Merge pull request #4209 from HiroyasuNishiyama/jp-message
add Japanese message for 3.1.0 beta 3
2023-06-21 13:34:23 +01:00
Nick O'Leary
1ad67d5c73 Merge pull request #4218 from node-red/4204-cant-go-fullscreen-on-a-mac
Dont handle shortcuts with both cmd+ctrl modifiers
2023-06-21 13:31:21 +01:00
Nick O'Leary
0b3f7dbb1f Merge pull request #4203 from node-red/fix-delay-node-flush-issue-4202
Fix delay node flush issue
2023-06-21 13:27:32 +01:00
Nick O'Leary
e90007860c Merge pull request #4207 from node-red/4197-status-catch-label
Update status and catch node labels in group mode
2023-06-21 13:27:09 +01:00
Nick O'Leary
3b38669c04 Merge pull request #4163 from XuyuEre/patch-1
Add missing Simplified Chinese translations for editor.json
2023-06-21 13:26:24 +01:00
Steve-Mcl
74ab03288b fix typos in test flows 2023-06-20 12:18:03 +01:00
Steve-Mcl
502dacd865 fix failure to return after calling callback 2023-06-17 22:44:55 +01:00
Steve-Mcl
31bc99cd61 remove .only 2023-06-17 22:29:39 +01:00
Steve-Mcl
5435c9ebd2 fix test (missing getUserSettings stub 🤷‍♂️) 2023-06-17 22:12:09 +01:00
Steve-Mcl
ceb9a320ba expand existing env var test for all scenarios 2023-06-17 22:11:02 +01:00
Steve-Mcl
ee8b2a0b58 Delete stray it.only 2023-06-17 22:03:59 +01:00
Steve-Mcl
8202f1b7c6 Add env var is JSONata expr test 2023-06-17 21:54:32 +01:00
Steve-Mcl
4808cac89d Add async to all paths that JSONata env var calls 2023-06-17 21:14:56 +01:00
bvmensvoort
c1ea3380eb Show errors and statuses of config nodes in the sidebar when no catch nodes are used 2023-06-10 21:27:06 +02:00
Stephen McLaughlin
694fdebc71 dont handle both cmd+ctrl 2023-06-10 16:23:21 +01:00
Steve-Mcl
1cbd910e5d correct declaration of env object/dic/lookup 2023-06-09 11:30:21 +01:00
Steve-Mcl
b102ef512e ensure object before attempting to call function 2023-06-09 11:29:54 +01:00
Kilian Hertel
220a621dc6 Merge branch 'dev' into adding-timeout-to-functio-node 2023-06-02 12:20:51 +02:00
BitCaesar
0db288e6dc Merge branch 'dev' into fix/joinManual 2023-05-31 16:10:41 +02:00
Stephen McLaughlin
f8175fc325 Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2023-05-31 14:11:06 +01:00
Stephen McLaughlin
5eee38e7de Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2023-05-31 14:10:59 +01:00
Nick O'Leary
fb5b470966 Merge pull request #4208 from node-red/4198-group-edit-stack-overflow
Dont clone the group nodes `node` array when saving edits
2023-05-31 09:50:31 +01:00
Kilian Hertel
876053f858 Merge branch 'dev' into adding-timeout-to-functio-node 2023-05-30 14:55:56 +02:00
GogoVega
9714ef19de Missing french translation of beta 3 changes 2023-05-30 14:19:39 +02:00
HiroyasuNishiyama
613a345771 add additional Japanese translation for status and catch node 2023-05-30 20:58:17 +09:00
BitCaesar
4fe29dd33f Merge branch 'dev' into fix/joinManual 2023-05-30 10:42:04 +02:00
Steve-Mcl
4b24223290 Dont clone group node node array
fixes #4198
2023-05-29 23:55:52 +01:00
Steve-Mcl
a78da0db1e Update status and catch node labels in group mode 2023-05-29 22:47:29 +01:00
Dave Conway-Jones
5e4fce1e12 Fix delay node flush issue
to close #4202
2023-05-27 17:51:21 +01:00
GogoVega
4cb2624a5d French translation of Welcome Tours 2023-05-26 20:58:50 +02:00
GogoVega
58e045f25d French translation of changes from v3.1.0-beta.3 2023-05-26 20:52:25 +02:00
Nick O'Leary
26a770e490 Merge pull request #4192 from node-red/prep-310-beta3
Bump everything for beta.3
2023-05-26 13:31:53 +01:00
Nick O'Leary
dfe145a0ed Merge pull request #4190 from Steve-Mcl/fix-mqtt-keep-subscription-4132
Fix new feature "mqtt keep subscription"
2023-05-26 13:31:15 +01:00
Steve-Mcl
6cb4c9224d Merge remote-tracking branch 'upstream/dev' into fix-mqtt-keep-subscription-4132 2023-05-26 10:46:59 +01:00
Nick O'Leary
4047612b96 Merge branch 'master' into patch-1 2023-05-26 10:31:29 +01:00
Nick O'Leary
1978a360af Merge pull request #4191 from node-red/update-xml2js
Update xml2js
2023-05-26 10:30:13 +01:00
Nick O'Leary
1f46b3fda9 Merge pull request #4193 from kazuhitoyokoi/prep-310-beta3-jp
Add Japanese translations for v3.1.0-beta.3
2023-05-26 10:29:59 +01:00
Nick O'Leary
47a945d92e Merge pull request #4195 from node-red/4191-function-error-badge-not-shown
fix function node error badge not shown
2023-05-26 10:29:23 +01:00
Steve-Mcl
59ec87a393 fix function node error badge not shown
fixes #4194
2023-05-26 10:12:48 +01:00
Kazuhito Yokoi
9423104dad Add Japanese translations for v3.1.0-beta.3 2023-05-26 12:35:53 +09:00
Nick O'Leary
614834090e Bump everything for beta.3 2023-05-25 18:10:01 +01:00
Nick O'Leary
2f9523a586 Merge branch 'dev' into adding-timeout-to-functio-node 2023-05-25 17:43:04 +01:00
Nick O'Leary
3a9f38a873 Merge branch 'master' into dev 2023-05-25 17:42:34 +01:00
Nick O'Leary
0697c26dd1 Merge branch 'dev' into adding-timeout-to-functio-node 2023-05-25 17:33:41 +01:00
Nick O'Leary
cfa25dc655 Update xml2js 2023-05-25 17:30:17 +01:00
Nick O'Leary
b3498a888d Merge pull request #4189 from node-red/monaco-0-38-0
Monaco 0.38.0
2023-05-25 17:27:20 +01:00
Steve-Mcl
0528c12782 Merge remote-tracking branch 'upstream/dev' into fix-mqtt-keep-subscription-4132 2023-05-25 12:07:33 +01:00
Steve-Mcl
e19b60b202 patch the editor to fix invisible action list 2023-05-25 11:30:24 +01:00
Steve-Mcl
0ed274f994 remove useless file 2023-05-25 10:59:09 +01:00
Steve-Mcl
e8378b382b update function node env typing 2023-05-25 10:58:25 +01:00
Nick O'Leary
e6c12a0c54 Merge pull request #4186 from node-red/3843-alternative-impl
Remove unused function
2023-05-22 23:20:18 +01:00
Nick O'Leary
70620ad12d Merge pull request #4184 from node-red/4113-group-selection-api
Enable RED.view.select to select group by id
2023-05-22 23:15:56 +01:00
Nick O'Leary
991f13e704 Remove unused function 2023-05-22 23:14:31 +01:00
Nick O'Leary
14bbe79651 Merge pull request #4185 from node-red/3843-alternative-impl
feature: new node selection group for catch and status nodes
2023-05-22 23:13:27 +01:00
Nick O'Leary
2388232179 Fix catch/status group scoping to handle group hierarchies 2023-05-22 22:33:31 +01:00
Nick O'Leary
11ded1e497 Merge branch 'dev' into 3843-alternative-impl 2023-05-22 17:41:23 +01:00
Nick O'Leary
9479b56549 Merge pull request #4109 from kevinGodell/dev
httpStatic feature
2023-05-22 16:54:29 +01:00
Nick O'Leary
90e32f52c9 Enable RED.view.select to select group by id 2023-05-22 16:51:17 +01:00
Nick O'Leary
ce6a4845f2 Merge pull request #4113 from Steve-Mcl/select-deep-linked-item
Select the item that is specified in a deep link URL
2023-05-22 16:48:00 +01:00
Nick O'Leary
0f5cf1d51c Merge pull request #4183 from node-red/4111-subflow-node-position
Place subflow outputs/inputs relative to current view
2023-05-22 16:46:56 +01:00
Kilian Hertel
c2812b05a4 Merge branch 'master' into adding-timeout-to-functio-node 2023-05-22 17:42:59 +02:00
Nick O'Leary
5d698d66d0 Merge pull request #4156 from node-red/4133-mqtt-v5-disconnects-when-subscribing-to-aws-core-broker
Dont use `subscriptionIdentifier` if broker doesnt support it
2023-05-22 16:42:25 +01:00
Nick O'Leary
1a149592d6 Merge pull request #4181 from node-red/4122-exit-codes
Ensure non-zero exit codes for errors
2023-05-22 16:39:22 +01:00
Nick O'Leary
5e2e0b2e39 Merge pull request #4182 from node-red/4101-merge-group-env-vars
Combine existing env vars when merging groups
2023-05-22 16:39:11 +01:00
Nick O'Leary
7a4f48e4fb Place subflow outputs/inputs relative to current view
Fixes #4111
2023-05-22 16:26:38 +01:00
Nick O'Leary
2ae2ec2578 Combine existing env vars when merging groups
Closes #4101
2023-05-22 16:07:14 +01:00
Nick O'Leary
a790136164 Merge branch 'master' into 4133-mqtt-v5-disconnects-when-subscribing-to-aws-core-broker 2023-05-22 15:16:58 +01:00
Nick O'Leary
e6c454bba5 Ensure non-zero exit codes for errors 2023-05-22 15:11:57 +01:00
Nick O'Leary
b904c23e4d Merge pull request #4180 from node-red/4168-make-module-install-synchronous
Ensure external modules are installed synchronously
2023-05-22 14:39:26 +01:00
Nick O'Leary
ece3eb2e7b Merge pull request #4155 from node-red/update-deps
Update dependecies include got
2023-05-22 14:29:30 +01:00
Nick O'Leary
18610bb540 Ensure external modules are installed synchronously
Fixes #4168
2023-05-22 14:24:11 +01:00
Nick O'Leary
69d643942c Merge branch 'dev' into update-deps 2023-05-22 13:57:47 +01:00
Nick O'Leary
6e1b298282 Reconstruct xml2js output as proper object 2023-05-22 13:57:12 +01:00
Nick O'Leary
c2387777c9 Merge branch 'master' into dev 2023-05-22 12:52:07 +01:00
Nick O'Leary
32a49a1ef1 Merge pull request #4172 from wooferguy/Zombie-Junctions-Fix
Check for group
2023-05-22 12:51:46 +01:00
Nick O'Leary
4b88775183 Merge pull request #4166 from node-red/fix-RBE-for-missing-payload
Fix RBE for missing "payload"
2023-05-22 12:49:07 +01:00
Nick O'Leary
29db82625f Merge pull request #4162 from kazuhitoyokoi/master-fixdownload4ipad
Fix content type for downloading flows.json
2023-05-22 12:48:05 +01:00
Nick O'Leary
2b6c9e3439 Merge pull request #4157 from kazuhitoyokoi/master-addjpn
Add Japanese translation for keyboard shortcut scope
2023-05-22 12:47:46 +01:00
Nick O'Leary
a7e0444e92 Merge pull request #4158 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for v3.1 beta.2
2023-05-22 11:36:32 +01:00
Nick O'Leary
e9e32550df Merge pull request #4178 from node-red/4169-remove-express-header
Ensure express server options are applied consistently
2023-05-22 11:35:32 +01:00
Nick O'Leary
59b059f06f Merge pull request #4179 from node-red/4170-remove-version-from-theme
Remove version info from theme endpoint
2023-05-22 11:35:20 +01:00
Nick O'Leary
42166f5fc4 Merge pull request #4153 from node-red/remove-empty-global-config
Avoid creating empty global-config node if not needed
2023-05-22 11:33:07 +01:00
Nick O'Leary
eefe69d136 Merge branch 'dev' into 4170-remove-version-from-theme 2023-05-22 11:29:54 +01:00
Nick O'Leary
aabaf7c5e2 Merge branch 'dev' into 4169-remove-express-header 2023-05-22 11:29:45 +01:00
Nick O'Leary
9ea4853c89 Merge branch 'master' into Zombie-Junctions-Fix 2023-05-22 11:29:31 +01:00
Nick O'Leary
3b5e21761b Merge branch 'master' into fix-RBE-for-missing-payload 2023-05-22 11:29:19 +01:00
Nick O'Leary
2d76bf29cf Merge branch 'master' into master-fixdownload4ipad 2023-05-22 11:29:02 +01:00
Nick O'Leary
a684ec235f Merge branch 'dev' into dev-addjpn 2023-05-22 11:28:50 +01:00
Nick O'Leary
c21f7abe4e Merge branch 'master' into master-addjpn 2023-05-22 11:28:40 +01:00
Nick O'Leary
8c191263c0 Merge branch 'master' into 4133-mqtt-v5-disconnects-when-subscribing-to-aws-core-broker 2023-05-22 11:28:24 +01:00
Nick O'Leary
47005043a5 Merge branch 'dev' into remove-empty-global-config 2023-05-22 11:27:43 +01:00
Nick O'Leary
1e36ba8429 Merge branch 'master' into dev 2023-05-22 11:26:55 +01:00
Nick O'Leary
2679ff277c Merge pull request #4173 from wooferguy/Inject-Node-Test-Fix
Invalid JSONata Inject node test passing condition
2023-05-22 11:26:38 +01:00
Nick O'Leary
0e52271ba9 Remove version info from theme endpoint
Fixes #4170
2023-05-22 11:00:15 +01:00
Nick O'Leary
57359d1659 Ensure express server options are applied consistently
Fixes #4169
2023-05-22 10:54:37 +01:00
Kilian Hertel
2253417459 adding timeout attribute to function node
- [x] New feature (non-breaking change which adds functionality)

Discussion here:
https://discourse.nodered.org/t/function-node-doesnt-have-timeout-feature/78483

## Proposed changes

Adding a timeout attribute to the function node, so an endless funciton doesnt break the node red server.

## Checklist

- [x] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
- [x] For non-bugfix PRs, I have discussed this change on the forum/slack team.
- [x] I have run `grunt` to verify the unit tests pass
- [x] I have added suitable unit tests to cover the new/changed functionality
2023-05-22 10:16:37 +02:00
wooferguy
9e3f148273 Invalid JSONata Inject node test passing condition
This test would sometimes run twice, causing the author to increase its catch count to 2 before considering the test complete. However even one pass proves the node is behaving as expected, and it always runs at least once. I have left the conditional statement in so it can be changed in future.
2023-05-17 18:56:07 +12:00
wooferguy
7e9042e9f7 Check for group
Remove junction from groups node list if it is present.
2023-05-17 05:14:18 +12:00
Steve-Mcl
dee68b903c fix sub/unsub when using "dont unsub" 2023-05-13 11:14:57 +01:00
Dave Conway-Jones
67c5a248ad Fix RBE for missing "payload"
To close #4165
2023-05-08 09:28:35 +01:00
xuyu0v0
940512fb2c Update editor.json
Update Simplified Chinese translation files
2023-05-07 20:16:28 +08:00
Kazuhito Yokoi
e8ddee24a9 Use correct content type for downloading flows.json 2023-05-06 21:10:49 +09:00
Kazuhito Yokoi
be4eab65f6 Fix content type for downloading flows.json 2023-05-06 20:00:20 +09:00
Kazuhito Yokoi
fb5ffa1c31 Add Japanese translation for keyboard shortcut scope 2023-05-01 14:44:14 +09:00
Kazuhito Yokoi
aff0bd3f6a Add Japanese translation for auto unsubscribe in MQTT node 2023-05-01 14:28:02 +09:00
Kazuhito Yokoi
c0650cc0f5 Add Japanese translation for keyboard shortcut scope 2023-05-01 13:53:54 +09:00
Stephen McLaughlin
02c7d014cb dont use subscriptionIdentifier no broker support 2023-04-29 21:00:26 +01:00
Nick O'Leary
e29479fd25 Merge branch 'dev' into remove-empty-global-config 2023-04-28 21:49:16 +01:00
Nick O'Leary
46ae66c8b2 Bump test helper version 2023-04-28 21:42:36 +01:00
Nick O'Leary
20abe4a40c Update dependecies include got 2023-04-28 21:37:03 +01:00
Nick O'Leary
55a9a29f76 Merge branch 'master' into dev 2023-04-28 18:49:03 +01:00
Nick O'Leary
67dd7e30fa Merge pull request #4154 from node-red/add-editor-scope
Add editor scope to keyboard shortcut scope select
2023-04-28 18:47:25 +01:00
Nick O'Leary
e9a08af73b Merge pull request #4148 from GerwinvBeek/Bugs/not-loading-missing-subflow
Handle missing subflow when loading flows into the editor
2023-04-28 18:47:10 +01:00
Nick O'Leary
08b1ef2766 Merge branch 'master' into add-editor-scope 2023-04-28 18:07:47 +01:00
Nick O'Leary
667d8673d4 Merge branch 'master' into Bugs/not-loading-missing-subflow 2023-04-28 18:07:28 +01:00
Nick O'Leary
d44ea9d558 Merge pull request #4152 from node-red/remove-coveralls
Remove coveralls reporting as it is failing builds
2023-04-28 18:07:10 +01:00
Nick O'Leary
86dfe86813 Add editor scope to keyboard shortcut scope select 2023-04-28 17:47:16 +01:00
Nick O'Leary
b129e11c8f Avoid creating empty global-config node if not needed 2023-04-28 17:36:55 +01:00
Nick O'Leary
246409970d Remove coveralls reporting as it is failing builds 2023-04-28 17:17:40 +01:00
Nick O'Leary
bd7b3bb4d7 Merge pull request #4108 from node-red/fix-group-select-delete
Fix group selection when using lasso
2023-04-28 15:28:04 +01:00
Nick O'Leary
841f1849c8 Update packages/node_modules/@node-red/runtime/lib/flows/util.js 2023-04-28 15:25:29 +01:00
Nick O'Leary
00e7e4d43c Merge pull request #4147 from kazuhitoyokoi/master-selection2subflow
Add node width and height to boundingBox
2023-04-28 15:23:02 +01:00
Nick O'Leary
ee43a845aa Merge pull request #4128 from kazuhitoyokoi/master-fixquickadddialog
Fix broken subflow icon and overflowed name in quick add dialog
2023-04-28 15:22:25 +01:00
Nick O'Leary
a7cc66af93 Merge pull request #4130 from kazuhitoyokoi/master-fixoverflow
Wrap long node name in tooltip, info sidebar, and node property
2023-04-28 15:21:37 +01:00
Nick O'Leary
f8701cfed0 Merge pull request #4135 from kazuhitoyokoi/master-fixactionlist
Support uppercase in keyword when searching action list
2023-04-28 15:20:56 +01:00
Nick O'Leary
6b205bf303 Merge pull request #4141 from bonanitech/palette-search-background
Fix palette filter background
2023-04-28 15:20:38 +01:00
Nick O'Leary
07729247ac Merge pull request #4145 from kazuhitoyokoi/dev-addjpn4tour
Add Japanese translations for welcome tour of 3.1.0 beta.2
2023-04-28 15:16:13 +01:00
Nick O'Leary
792b310fad Merge pull request #4151 from mw75/master
Use editor path in generating localStorage keys
2023-04-28 15:15:55 +01:00
Mario Wolff
ed2c9d24e8 use replace instead of replaceAll to support node14 2023-04-28 14:53:18 +02:00
Mario Wolff
f917212d67 a simple approach to fix #2657 2023-04-28 12:35:19 +02:00
Gerwin van Beek
6fbcec8b98 Solved node red not loading without error when subflow is missing 2023-04-24 11:51:06 +02:00
Kazuhito Yokoi
c30e57c31d Add node width and height to boundingBox 2023-04-23 20:47:17 +09:00
Kazuhito Yokoi
674c9f0405 Add Japanese translations for welcome tour of 3.1.0 beta.2 2023-04-22 21:26:02 +09:00
BitCaesar
e16d5cf83d fix: closes #4142
The issue occured because the partId is set to "_" by default and is never overwritten in manual mode.
With concurrent messages and different processing times all parts of all messages have the identifier "_" and are assembled following the FIFO principle.
2023-04-20 12:28:36 +02:00
Mauricio Bonani
df3dc36874 Fix palette filter background 2023-04-17 10:57:40 -04:00
Kazuhito Yokoi
7b71d8d212 Support uppercase in keyword when searching action list 2023-04-10 01:25:34 +09:00
Kazuhito Yokoi
2eaae4b83f Wrap long node name in info sidebar 2023-04-05 00:56:27 +09:00
Kazuhito Yokoi
3c66af9506 Wrap long node name in tooltip and node property 2023-04-05 00:45:46 +09:00
Nick O'Leary
e5d579c1bb Merge pull request #4120 from kazuhitoyokoi/master-fixbuildstatus
Use build status icon of GitHub Actions
2023-04-01 17:29:41 +01:00
Kazuhito Yokoi
ee811ca89b Use build status icon of GitHub Actions 2023-04-02 00:39:33 +09:00
Kazuhito Yokoi
8e4933041d Wrap text of label in quick add dialog 2023-04-01 02:34:11 +09:00
Kazuhito Yokoi
1f3559e14f Set label width in quick add dialog to prevent broken node icon 2023-04-01 02:28:48 +09:00
Steve-Mcl
12ac260dce select the deep link item 2023-03-26 11:00:28 +01:00
Nick O'Leary
53f99ecc23 Merge pull request #4112 from node-red/editor-cred-export
Ensure no node credentials are included when exporting to clipboard
2023-03-24 09:50:14 +00:00
Nick O'Leary
347410f744 Do not include credentials when exporting to clipboard 2023-03-24 09:44:10 +00:00
Kevin Godell
7ae3e32abd update example and document feature for httpStatic options 2023-03-20 17:51:05 -05:00
Kevin Godell
54b2215164 pass options to express.static 2023-03-20 17:51:05 -05:00
Kevin Godell
889489e33e set default when root is not defined 2023-03-20 17:51:05 -05:00
Nick O'Leary
4667e76c6b Merge pull request #4078 from flying7eleven/option-to-disable-mqtt-ubsubscribe-on-disconnect
Option to disable MQTT topic unsubscribe on disconnect
2023-03-20 20:32:08 +00:00
Nick O'Leary
a10d07d1dc Merge pull request #4100 from sroebert/feature/sha-digest-algorithms
Added SHA-256 and SHA-512-256 digest authentication
2023-03-20 20:30:48 +00:00
Nick O'Leary
9f1ac733b7 Merge pull request #4103 from Steve-Mcl/add-missing-timer-types
add "timers" types to known types
2023-03-20 20:29:36 +00:00
Nick O'Leary
33ea25922d Update packages/node_modules/@node-red/editor-client/src/js/ui/view.js 2023-03-20 20:29:10 +00:00
Nick O'Leary
b56bd7bb5e Fix group selection when using lasso 2023-03-20 17:15:45 +00:00
Tim Janke
e7617de1ee Replace a var with a const since its value won't be modified further on 2023-03-13 13:22:51 +01:00
Steve-Mcl
97fbad4dc5 add "timers" types to known types 2023-03-13 10:02:46 +00:00
Steven Roebert
ddf6023983 Added unit tests for digest authentication 2023-03-12 10:08:32 +01:00
Steven Roebert
daa84c9415 Added SHA-256 and SHA-512-256 algorithms to http digest authentication 2023-03-11 12:07:54 +01:00
Stephen McLaughlin
586006de4d Merge pull request #4097 from node-red/fix-json-expr-test
Fix jsonata expression test ui
2023-03-07 17:13:50 +00:00
Stephen McLaughlin
af8ec9f02b Merge pull request #4096 from node-red/fix-search-repeat
Fix search button in palette popover
2023-03-07 17:00:50 +00:00
Nick O'Leary
dc6abb691e Fix jsonata expression test ui 2023-03-07 16:59:13 +00:00
Nick O'Leary
d273c38194 Fix search button in palette popover 2023-03-07 16:47:44 +00:00
Nick O'Leary
940a246160 Merge pull request #4095 from node-red/310-b2
Bump for beta.2
2023-03-06 17:35:28 +00:00
Nick O'Leary
6179d1eef2 Bump for beta.2 2023-03-06 17:30:34 +00:00
Nick O'Leary
9f121f4c72 Merge pull request #4094 from node-red/increase-workspace
Increase workspace size to 8000x8000
2023-03-06 17:13:24 +00:00
Nick O'Leary
4deca552fa Merge pull request #4093 from node-red/subflow-nested-context
Generate stable ids for subflow instance internal nodes
2023-03-06 17:13:10 +00:00
Nick O'Leary
2d066307f4 Merge branch 'master' into dev 2023-03-06 16:17:02 +00:00
Nick O'Leary
0fdf426a93 Increase workspace size to 8000x8000 2023-03-03 20:06:24 +00:00
Nick O'Leary
e47698bfd4 Handle null subflow object when clearing context 2023-03-03 20:02:46 +00:00
Nick O'Leary
e91981207f Generate stable ids for subflow instance internal nodes
Fixes #3996 #3889
2023-03-03 18:46:49 +00:00
Nick O'Leary
683ae4f067 Merge branch 'dev' into pr_3858 2023-03-03 16:09:19 +00:00
Nick O'Leary
94bc887369 Restore drag-to-detach action 2023-03-03 16:09:04 +00:00
Nick O'Leary
d1094da6c7 Merge pull request #3895 from hae-iotplatform/translation-KR
Translation kr
2023-03-03 15:23:54 +00:00
Nick O'Leary
ee1e12eca3 Merge pull request #3952 from cliyr/translation-zhcn
Translation zhcn (!!请懂中文的帮忙review)
2023-03-03 15:22:46 +00:00
Nick O'Leary
307fbef5b2 Merge branch 'dev' into translation-zhcn 2023-03-03 15:22:34 +00:00
Nick O'Leary
f54f500e2b Merge pull request #3964 from GogoVega/french-translation-of-nodes
Add French translation of nodes
2023-03-03 15:18:20 +00:00
Nick O'Leary
fd66335bd4 Merge pull request #3962 from GogoVega/french-translation
Add French translation
2023-03-03 13:37:40 +00:00
Nick O'Leary
301343deb0 Update packages/node_modules/@node-red/editor-client/locales/fr/editor.json
Co-authored-by: Gauthier Dandele <92022724+GogoVega@users.noreply.github.com>
2023-03-03 13:36:45 +00:00
Nick O'Leary
c046c8c4d3 Merge pull request #3804 from FabsMuller/ptbrtranslation
Portuguese Brazilian (pt-BR) translation
2023-03-03 13:33:47 +00:00
Nick O'Leary
0ceca8fc17 Merge pull request #4091 from node-red/enable-unlocked-buttons
Ensure node buttons are redrawn when flow lock state is changed
2023-03-03 13:33:08 +00:00
Nick O'Leary
2ac3b50040 Merge pull request #4090 from node-red/json-async-prep
Deprecate synchronous access to jsonata
2023-03-03 13:23:17 +00:00
Nick O'Leary
70771bf3d6 Ensure node buttons are redrawn when flow lock state is changed 2023-03-03 13:21:23 +00:00
Nick O'Leary
2ed17f0fff Merge pull request #4089 from Steve-Mcl/update-monaco-and-typings
Update-monaco-and-typings
2023-03-03 11:52:13 +00:00
Nick O'Leary
ca53712ee9 Deprecate synchronous access to jsonata 2023-03-03 11:43:06 +00:00
Steve-Mcl
69e139b75b update monaco themes 2023-03-03 10:57:31 +00:00
Steve-Mcl
d268661f29 update monaco locales 2023-03-03 10:56:55 +00:00
Steve-Mcl
ed379387df update monaco (0.36.1) 2023-03-03 10:56:37 +00:00
Steve-Mcl
1eddad82c8 update typings 2023-03-03 10:55:06 +00:00
Nick O'Leary
d7defc011b Merge pull request #4088 from node-red/update-jquery-ui
Update jquery UI
2023-03-03 09:57:22 +00:00
Nick O'Leary
0e4a7829fe Update jquery-ui to latest 2023-03-03 09:33:24 +00:00
Nick O'Leary
e7c6178391 Merge pull request #4085 from node-red/link-call-timeout
Clear link-call timeouts when node is closed
2023-03-02 21:18:34 +00:00
Nick O'Leary
40b506b7b4 Merge pull request #4087 from node-red/junction-loops
Prevent loops being created with junction nodes
2023-03-02 21:18:21 +00:00
Nick O'Leary
b19a679d00 Merge pull request #4086 from node-red/version-check
Bump minimum nodejs version supported to match documented value
2023-03-02 21:16:24 +00:00
Nick O'Leary
81d4c60cbd Prevent loops being created with junction nodes
Fixes #3799
2023-03-02 21:13:39 +00:00
Nick O'Leary
a95be2aa43 Bump minimum nodejs version supported to match documented value 2023-03-02 17:46:00 +00:00
Nick O'Leary
5e9a815b06 Clear link-call timeouts when node is closed
Fixes #3959
2023-03-02 17:23:13 +00:00
Nick O'Leary
010c8eccc8 Bump dependencies 2023-03-02 15:43:38 +00:00
Nick O'Leary
4b89619ef1 Merge pull request #4084 from node-red/add-18
Add Node 18 to test matrix
2023-03-02 15:43:08 +00:00
Nick O'Leary
e5054d306e Merge pull request #4079 from node-red/group-rework
Complete overhaul of Group UX
2023-03-02 15:27:17 +00:00
Nick O'Leary
08eaa9274f Merge branch 'dev' into group-rework 2023-03-02 15:26:01 +00:00
Nick O'Leary
3306cdede5 Merge pull request #4083 from node-red/fix-join-memory
Join: ensure inflight status is cleared when in auto mode
2023-03-02 15:23:43 +00:00
Nick O'Leary
e7f650a9eb Add node 18 and drop node 14 2023-03-02 15:20:48 +00:00
Nick O'Leary
2804794f7a Merge pull request #4072 from node-red-hitachi/fix-align-nodes-on-locked-tab
fix align nodes on locked tab
2023-03-02 15:14:02 +00:00
Nick O'Leary
781eaf058b Merge pull request #4077 from node-red-hitachi/support-editablelist-cnacel
add cancel operation to editableList
2023-03-02 15:13:48 +00:00
Nick O'Leary
b4c155bdb8 Merge branch 'dev' into pr_4059 2023-03-02 15:12:28 +00:00
Nick O'Leary
7c3e045a57 Merge pull request #4068 from node-red/change-tab-notification
Add change icon to tabs
2023-03-02 15:11:28 +00:00
Nick O'Leary
f1fa1bbe4e Merge pull request #4073 from kazuhitoyokoi/master-flowname
Change default file name to flows.json in project feature
2023-03-02 15:11:13 +00:00
Nick O'Leary
d4f4c7b8c6 Merge pull request #4082 from node-red/fix-link-import
Fix importing connected link nodes into a subflow
2023-03-02 15:10:58 +00:00
Nick O'Leary
5eb46c570d Merge pull request #4075 from node-red/fix-link-to-monaco-options-4074
Update monaco docs link in settings.js
2023-03-02 15:08:33 +00:00
Nick O'Leary
9e6d501009 Join: ensure inflight status is cleared when in auto mode
Fixes #4080
2023-03-02 14:59:54 +00:00
Nick O'Leary
27e258b19b Fix importing connected link nodes into a subflow
Fixes #4055
2023-03-02 14:14:33 +00:00
GogoVega
d3615c9661 Add translation of new features from dev branch 2023-03-01 16:50:40 +01:00
GogoVega
a57c449c25 Add translation of new features from dev branch 2023-03-01 16:46:54 +01:00
Nick O'Leary
07f2f0cef3 Complete overhaul of Group UX 2023-03-01 10:02:26 +00:00
Tim Janke
c94f0896e1 Ensure that a client id is set if autoUnsubscribe is disabled 2023-02-27 14:11:22 +01:00
Tim Janke
182361c176 Re-enable the tests for the autoUnsubscribe property 2023-02-27 12:43:04 +01:00
Tim Janke
8dcc530f44 Refactor to use the already existing autoUnsubscribe property
The tests had an unused property called autoUnsubscribe. I refactored
the code to use this wording too.
2023-02-27 12:41:29 +01:00
Tim Janke
b5dfd62c99 Add an option to not unsubscribe topics on disconnect 2023-02-27 12:21:39 +01:00
Hiroyasu Nishiyama
662b1a8100 add cancel operation to editableList 2023-02-27 14:31:44 +09:00
Stephen McLaughlin
24178beafc update monaco docs link in settings.js
closes #4074
2023-02-26 15:17:45 +00:00
Kazuhito Yokoi
e30df544db Change default file name to flows.json in project feature 2023-02-26 00:27:13 +09:00
Hiroyasu Nishiyama
25b6e214dd fix align nodes on locked tab 2023-02-25 00:54:15 +09:00
Nick O'Leary
910f6134f6 Merge pull request #4069 from node-red/prevent-edit-from-sidebar
Prevent opening locked node's edit dialog
2023-02-24 13:10:01 +00:00
Nick O'Leary
52b4b0419f Merge pull request #4066 from kazuhitoyokoi/dev-dedup
Remove duplicated messages in the message catalog
2023-02-23 23:46:30 +00:00
Nick O'Leary
bbc1a82c2a Fix linting on boolean check 2023-02-23 23:45:34 +00:00
Nick O'Leary
6abe66934e Prevent opening locked node's edit dialog 2023-02-23 23:36:51 +00:00
Nick O'Leary
2ddbb44992 Add change annotation for newly added flow 2023-02-23 23:14:18 +00:00
Nick O'Leary
363a8b8588 Add change icon to tabs 2023-02-23 22:48:08 +00:00
Kazuhito Yokoi
5ae56aaec7 Add Japanese translation for action list 2023-02-23 19:45:00 +09:00
Kazuhito Yokoi
37057d1da2 Remove duplicated messages in the message catalog 2023-02-22 01:33:53 +09:00
Nick O'Leary
6044871438 Merge pull request #3911 from node-red/protect-hooks
Ensure errors in preDeliver callback are handled
2023-02-21 14:26:39 +00:00
Nick O'Leary
e612bb6a38 Merge pull request #3915 from node-red/Fix-file-write-last-line-newline-append
Fix extra newline append for multipart file write
2023-02-21 14:26:20 +00:00
Nick O'Leary
196a9ae43a Merge pull request #4065 from node-red/node-help-link
Add link to node help in node edit dialog footer
2023-02-21 14:25:40 +00:00
Nick O'Leary
81ea67d6da Merge pull request #4046 from bggbr/master
Fix "EADDRINUSE" error
2023-02-21 14:25:06 +00:00
Hiroyasu Nishiyama
96cd823fea Update packages/node_modules/@node-red/editor-client/locales/ja/editor.json
thank you!

Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2023-02-21 20:31:12 +09:00
Nick O'Leary
196773d6ba Merge branch 'master' into dev 2023-02-20 18:17:16 +00:00
Nick O'Leary
d8dbe68425 Merge pull request #4064 from node-red/reverse-tab-scroll
Reverse direction of tab scroll to expected direction
2023-02-20 18:15:04 +00:00
Nick O'Leary
286c0b000f Add link to node help in node edit dialog footer 2023-02-20 18:13:02 +00:00
Nick O'Leary
dec3164fdd Reverse direction of tab scroll to expected direction 2023-02-20 17:30:16 +00:00
Nick O'Leary
c2772e5038 Merge pull request #4051 from sonntam/feature-editor-multipleToNode
Added editor feature for connecting multiple nodes to single node
2023-02-20 17:00:14 +00:00
Nick O'Leary
43e4d09f72 Merge pull request #4049 from kazuhitoyokoi/dev-addjpn
Support i18n of lock/unlock buttons in flow property UI
2023-02-20 16:51:55 +00:00
Nick O'Leary
06e35baeaa Merge pull request #4060 from node-red-hitachi/fix-empty-group-on-outliner
Fix to add empty marker to empty group
2023-02-20 16:51:17 +00:00
Nick O'Leary
c9d72d7a1d Merge pull request #4056 from kazuhitoyokoi/master-validator
Add validators for complete and link call nodes
2023-02-20 16:50:01 +00:00
Nick O'Leary
7e0a783220 Merge pull request #4054 from kazuhitoyokoi/dev-fixmermaid
Apply Mermaid diagram for project settings UI
2023-02-20 16:46:08 +00:00
Nick O'Leary
f853c9f1c3 Merge pull request #4053 from kazuhitoyokoi/dev-fixtour
Fix image URLs for v3.0 tour
2023-02-20 16:45:44 +00:00
Nick O'Leary
62e9572070 Merge pull request #4048 from kazuhitoyokoi/master-notification
Show scrollbar in notification dialog only when needed
2023-02-20 16:45:10 +00:00
Nick O'Leary
fea68c8acc Merge pull request #4050 from kazuhitoyokoi/master-addtooltip
Add tooltip for show/hide button on info sidebar
2023-02-20 16:44:38 +00:00
cliyr
ce9462e6aa Apply suggestions from code review
Co-authored-by: zoollcar <zoollcar@qq.com>
2023-02-20 21:42:53 +08:00
Hiroyasu Nishiyama
929f0e90ac add empty marker to group 2023-02-14 20:49:43 +09:00
Hiroyasu Nishiyama
2e57d80959 add show-global-env action 2023-02-13 14:50:07 +09:00
Kazuhito Yokoi
81331e68d2 Add validator for link call node 2023-02-10 18:55:10 +09:00
Kazuhito Yokoi
4fda59a585 Add validator for complete node 2023-02-10 16:49:29 +09:00
Kazuhito Yokoi
4477b9ac18 Apply mermaid diagram for project settings UI 2023-02-06 00:53:22 +09:00
Kazuhito Yokoi
78f93dc11a Fix image URLs for v3.0 tour 2023-02-05 18:58:58 +09:00
sonntam
4c7c855f2c fix code style 2023-02-04 21:31:49 +01:00
Marcus Sonntag
8740ec5570 Added editor feature for connecting multiple nodes to single node 2023-02-04 21:03:30 +01:00
Kazuhito Yokoi
ede3ac4282 Add tooltip for show/hide button on info sidebar 2023-02-04 20:55:34 +09:00
Kazuhito Yokoi
3e4cad3a79 Add Japanese translations for the action list 2023-02-04 20:38:08 +09:00
Kazuhito Yokoi
bd0b0077a3 Support i18n of lock/unlock button in flow property UI 2023-02-04 20:28:08 +09:00
Kazuhito Yokoi
16f8b78b39 Show scrollbar in notification dialog only when needed 2023-02-04 11:16:28 +09:00
bggbr
892d21fb77 Fix "EADDRINUSE" error 2023-02-03 22:23:09 +09:00
Nick O'Leary
90d1bb0ae4 Merge branch 'master' into dev 2023-02-03 09:11:46 +00:00
Nick O'Leary
adfc5b3e98 Merge pull request #4043 from node-red-hitachi/fix-group-undo-position
fix group position after undo
2023-02-03 09:11:20 +00:00
Hiroyasu Nishiyama
f3d7016ab2 fix group position after undo 2023-02-03 17:25:58 +09:00
Nick O'Leary
ae776547ce Merge pull request #4042 from kazuhitoyokoi/dev-jpn
Add Japanese translations for welcome tour of 3.1.0 beta.1
2023-02-02 15:19:09 +00:00
Kazuhito Yokoi
da6885be62 Add Japanese translations for welcome tour of 3.1.0 beta.1 2023-02-03 00:08:50 +09:00
Nick O'Leary
c0fa4a077f Merge pull request #4041 from node-red/310-tour
Add welcome tour for 3.1.0 beta.1
2023-02-02 14:53:06 +00:00
Nick O'Leary
df19e54555 Fix lint issue 2023-02-02 14:36:04 +00:00
Nick O'Leary
19139a4dce Merge pull request #4040 from kazuhitoyokoi/dev-jpn
Add Japanese translations for locking flow
2023-02-02 13:42:22 +00:00
Kazuhito Yokoi
7d1c3133b3 Fix i18n of tooltips for locking flow 2023-02-02 22:37:00 +09:00
Kazuhito Yokoi
da9c0af854 Add Japanese translations for locking flow 2023-02-02 22:35:07 +09:00
Nick O'Leary
6d717a21cf Add 310 tour 2023-02-02 13:34:01 +00:00
Nick O'Leary
fc251d005e Merge pull request #4039 from node-red/tab-context-menu
Locking Flows
2023-02-02 11:35:54 +00:00
Nick O'Leary
4624e28675 Merge branch 'dev' into tab-context-menu 2023-02-02 11:33:06 +00:00
Nick O'Leary
3a1cc38aaf Update for 3.1.0-beta.1 2023-02-02 11:09:55 +00:00
Nick O'Leary
68bb38b8d7 Merge branch 'master' into dev 2023-02-02 10:40:33 +00:00
Nick O'Leary
2ca3b3e99d Merge pull request #4007 from node-red-hitachi/add-markdown-mermaid-diagram
Add support for mermaid diagram to markdown editor
2023-02-02 10:35:32 +00:00
Nick O'Leary
67c8354f76 Merge branch 'pr_4006' into dev 2023-02-02 10:29:39 +00:00
Nick O'Leary
384377782a Remove package-lock from git history 2023-02-02 10:25:57 +00:00
Nick O'Leary
8382665bbb Merge pull request #3999 from node-red/fix-plugin-async
Fix async loading of modules containing both nodes and plugins
2023-02-02 10:23:42 +00:00
Nick O'Leary
e799cfc16a Merge pull request #4004 from dirkjanfaber/patch-1
Comment node: Clarify where the text will appear
2023-02-02 10:23:22 +00:00
Nick O'Leary
055d0081e1 Merge pull request #4021 from node-red/inject-async-context
Allow Inject node to work with async context stores
2023-02-02 10:22:59 +00:00
Nick O'Leary
9035de32c8 Merge pull request #4023 from node-red/trigger-hide-nul-option
Hide trigger node repeat send  option if sending nothing
2023-02-02 10:22:40 +00:00
Nick O'Leary
af62a520d3 Merge pull request #4028 from node-red/Add-count-to-join-and-batch-node-labels
Add count to join and batch node labels
2023-02-02 10:21:57 +00:00
Nick O'Leary
1824108d09 Merge pull request #4035 from xiaobinqt/dev
fix .red-ui-notification class
2023-02-02 10:20:54 +00:00
Nick O'Leary
149e8ce9b0 Merge pull request #4036 from kazuhitoyokoi/master-usemain
Use main branch as default in project feature
2023-02-02 10:20:24 +00:00
Nick O'Leary
f4661eec75 Merge pull request #4038 from bonanitech/function-node-setup-tab
Fix border radius on Modules list header
2023-02-02 10:19:54 +00:00
Mauricio Bonani
0f7a1a42e4 Fix border radius on Modules list header 2023-02-01 17:50:05 -05:00
Kazuhito Yokoi
5bda221f9d Use main branch as default in project feature 2023-01-31 00:36:18 +09:00
weibin
fd42becbdc fix .red-ui-notification
if flows stopped due to missing too much node types manage-project-dep button display none.
2023-01-30 23:02:42 +08:00
Nick O'Leary
2759c1616c Merge branch 'dev' into pr_4031 2023-01-30 09:52:31 +00:00
Nick O'Leary
55ac98c989 Merge pull request #4033 from node-red-hitachi/fix-hide-subflow-tooltip
fix hide subflow tooltip
2023-01-30 09:48:20 +00:00
Nick O'Leary
c42c6a7b08 Merge pull request #4030 from node-red-hitachi/disable-delete-tab-menu-when-single-tab-exists
Disable delete tab menu when single tab exists
2023-01-30 09:38:16 +00:00
Nick O'Leary
b99bd38649 Merge pull request #4029 from node-red-hitachi/fix-tab-menu-error
fix workspace reference error in case of empty tabs
2023-01-30 09:34:05 +00:00
Hiroyasu Nishiyama
013ee2f1f4 fix hide subflow tooltip 2023-01-30 16:24:52 +09:00
Hiroyasu Nishiyama
7b79d79f84 remove useless console output 2023-01-30 14:09:01 +09:00
Hiroyasu Nishiyama
66f9686e48 disable hide all menu if all tabs hidden 2023-01-30 11:42:53 +09:00
Hiroyasu Nishiyama
9b1b7437b3 disable delete tab menu when single tab exists 2023-01-30 10:59:34 +09:00
Hiroyasu Nishiyama
720d44d53e fix workspace reference error in case of empty tabs 2023-01-30 10:30:06 +09:00
Nick O'Leary
7dca148349 Merge pull request #4027 from kazuhitoyokoi/master-fixmenu4project
Fix disabled menu items in project feature
2023-01-27 10:29:10 +00:00
Dave Conway-Jones
47bacaf58a Add count to join and batch node labels 2023-01-26 22:13:17 +00:00
Kazuhito Yokoi
ffff8aeb91 Fix disabled menu in project feature 2023-01-26 02:11:34 +09:00
Dave Conway-Jones
9a856f50d7 Hide repeat send option if sending nothing
to address https://discourse.nodered.org/t/trigger-node-how-to-delay-and-repeat-message/74117/5
2023-01-25 10:59:04 +00:00
Hiroyasu Nishiyama
e7540de85d remove useless variable 2023-01-25 16:40:12 +09:00
Hiroyasu Nishiyama
ba9ddefbee removed endpoint 2023-01-25 16:06:37 +09:00
Hiroyasu Nishiyama
f1801f9662 fix to prevent uploging unexpected file type 2023-01-25 13:38:12 +09:00
Nick O'Leary
ae92ea9476 Merge pull request #4020 from node-red/rename-package-var
Rename package var to avoid strict mode error
2023-01-23 20:44:29 +00:00
Nick O'Leary
c7017ee84b Allow Inject node to work with async context stores
Fixes #4014
2023-01-23 20:42:25 +00:00
Nick O'Leary
0346294c59 Rename package var 2023-01-23 17:50:50 +00:00
Nick O'Leary
8d240ca797 Rename package var to avoid strict more error
Fixes #4017
2023-01-23 17:44:03 +00:00
Nick O'Leary
a607ee90e0 Merge pull request #4009 from node-red/TCP-node-replaceall-fix
TCP Node: ensure newline substitution applies to whole message
2023-01-23 17:23:28 +00:00
Nick O'Leary
7dbbafec1b Merge pull request #4000 from node-red/fix-split-stream
Split node: avoid duplicate done call for buffer split
2023-01-23 17:04:31 +00:00
Nick O'Leary
428132ea3b Merge pull request #3997 from kazuhitoyokoi/dev-jpn
Add Japanese translation for v3.1.0-beta.0
2023-01-23 17:04:16 +00:00
Nick O'Leary
c49330f9d1 Merge pull request #4012 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v3.0.3
2023-01-23 16:58:34 +00:00
Nick O'Leary
937c5fe893 Merge pull request #4019 from node-red/force-ipv4-lookup-over-ipv6
if possible - force ipv4 name resolution to have priority
2023-01-23 16:58:21 +00:00
Dave Conway-Jones
d2c9f12c3a if possible - force ipv4 name resolution to have priority
to fix Issue #4010
and others (eg) email node server connect fails, and some reported on SO
2023-01-23 13:02:58 +00:00
Robin Schneider
10324d8260 Fix typos in settings.js (#4013)
* chore: Remove trailing whitespace in settings.js

* chore: Fix typos in settings.js

* chore: Use consistent terminology in settings.js
2023-01-19 09:28:46 +00:00
Kazuhito Yokoi
8f27dae7ea Add Japanese translations for v3.0.3 2023-01-16 00:56:39 +09:00
Dave Conway-Jones
94ae511a6d fix tcp to replace all in newline substitution
to close #3989
2023-01-09 09:26:24 +00:00
Hiroyasu Nishiyama
038f75e48f add support for mermaid diagram to markdown editor 2023-01-05 17:25:16 +09:00
Hiroyasu Nishiyama
b9fe4c5cd3 merge upstream/dev 2023-01-05 10:29:45 +09:00
Hiroyasu Nishiyama
7e8b7602b4 add support for inline image drag and drop to markdown editor 2023-01-05 10:28:48 +09:00
Dirk-Jan Faber
7c306a8430 Comment node: Clarify where the text will appear
The text will appear in the information tab of the side panel and not in the help tab of the side panel.
2023-01-03 13:41:31 +01:00
Nick O'Leary
dd2bc44c2d Merge branch 'dev' into dev-jpn 2023-01-01 22:38:20 +00:00
Nick O'Leary
74794fea09 Split node: avoid duplicate done call for buffer split
Fixes #3982
2023-01-01 22:21:49 +00:00
Nick O'Leary
1efd1a52a7 Ensure modules containing plugins and nodes are loaded properly
Fixes #3523
2023-01-01 19:36:49 +00:00
Nick O'Leary
928131cf08 Merge pull request #3941 from node-red-hitachi/global-env-var
add global environment variable feature
2023-01-01 14:10:15 +00:00
Nick O'Leary
4d202a7a37 Merge pull request #3971 from node-red/fix-cred-on-global
Ensure credentials object is removed before returning node in getFlow request
2023-01-01 14:09:54 +00:00
Nick O'Leary
e0d71abdc6 Merge pull request #3992 from node-red/fix-mqtt-reconnect
Fix mqtt nodes not reconnecting on modified-flows deploy
2023-01-01 14:09:33 +00:00
Nick O'Leary
550eb6ee2f Merge pull request #3995 from bonanitech/radialMenu
Let themes change radialMenu text colors
2023-01-01 14:06:00 +00:00
Kazuhito Yokoi
a661bc1d23 Add Japanese translation for v3.1.0-beta.0 2022-12-31 00:41:35 +09:00
Mauricio Bonani
f737162697 Let themes change radialMenu text colors 2022-12-29 09:47:05 -05:00
Nick O'Leary
ce57ba80eb Fix mqtt nodes not reconnected on modified-flows deploy 2022-12-27 14:56:32 +00:00
Hiroyasu Nishiyama
99bd957ea0 Resolve merge conflict 2022-12-27 23:45:25 +09:00
Nick O'Leary
270eb56718 Merge pull request #3916 from kazuhitoyokoi/dev
Add Japanese translation for v3.1.0-beta.0
2022-12-27 14:16:02 +00:00
Nick O'Leary
b3ce0c0079 Merge pull request #3987 from kazuhitoyokoi/master-fixproject
Ignore commit error in project feature
2022-12-27 14:15:47 +00:00
Nick O'Leary
e6cee58e0d Merge pull request #3974 from Steve-Mcl/remember-export-format
Remember compact/pretty flow export user choice
2022-12-27 14:09:47 +00:00
Nick O'Leary
4adc6b269c Merge pull request #3980 from kazuhitoyokoi/master-fixpalette
Hide subflow category after deleting subflow
2022-12-27 13:47:58 +00:00
Nick O'Leary
920b0178ec Merge pull request #3981 from kazuhitoyokoi/master-fixtypo
Fix typo in 25-status.html
2022-12-27 13:43:35 +00:00
Nick O'Leary
3583b40e02 Merge pull request #3990 from node-red/csv-change-to-replaceAll
Csv change replace to replaceAll
2022-12-27 13:43:12 +00:00
Nick O'Leary
7870830367 Merge pull request #3991 from hardillb/http-request-form-array
Support form-data arrays
2022-12-27 13:42:33 +00:00
Ben Hardill
4c1d7ad2d2 Merge branch 'master' into http-request-form-array 2022-12-24 20:37:33 +00:00
Ben Hardill
661b07c856 Add tests 2022-12-24 20:35:51 +00:00
Ben Hardill
5670bd8265 Support form-data arrays 2022-12-24 19:32:33 +00:00
Dave Conway-Jones
93a1911232 CSV - Add note about msg.reset to info page
to close #3976
2022-12-19 21:26:20 +00:00
Dave Conway-Jones
2429191838 CSV - swap to regex replace for node14 support 2022-12-19 13:48:21 +00:00
Dave Conway-Jones
f6901cd19f CSV node replace replace with replaceAll just in case
mentioned in Issue #3989
2022-12-19 09:50:29 +00:00
Kazuhito Yokoi
156c3984a7 Ignore commit error in project feature 2022-12-12 01:23:00 +09:00
Kazuhito Yokoi
f91af2153a Fix typo in 25-status.html 2022-12-10 00:33:40 +09:00
GogoVega
c3f13eb428 A few more minor fixes 2022-12-08 22:31:27 +01:00
GogoVega
91ae0206ac fix: Typos 2022-12-08 21:53:16 +01:00
GogoVega
19b5eda98c fix: Double line in popup 2022-12-08 16:47:25 +01:00
Kazuhito Yokoi
805f8a5ee7 Hide subflow category after deleting subflow 2022-12-08 23:43:47 +09:00
GogoVega
6cbfe3d736 Last adjustment and translation of commit 2022-12-07 12:28:02 +01:00
GogoVega
2274319274 fix: Typos 2022-12-07 02:08:29 +01:00
Hiroyasu Nishiyama
2ab8121a4a add description to global-config settings 2022-12-06 21:12:52 +09:00
Hiroyasu Nishiyama
601a4ec70d Add hasUsers to global-config 2022-12-06 10:33:10 +09:00
Hiroyasu Nishiyama
707b831c30 Update packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 10:25:38 +09:00
Hiroyasu Nishiyama
72ae375e44 Update packages/node_modules/@node-red/nodes/core/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 10:25:25 +09:00
Hiroyasu Nishiyama
3c1ddb5c9d Update packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:32:04 +09:00
Hiroyasu Nishiyama
817db23146 Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:29:51 +09:00
Hiroyasu Nishiyama
a8c820f558 Update packages/node_modules/@node-red/nodes/locales/ja/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:29:45 +09:00
Hiroyasu Nishiyama
6d09c81f11 Update packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:27:17 +09:00
Hiroyasu Nishiyama
192e537e5d Update packages/node_modules/@node-red/editor-client/locales/ja/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:27:02 +09:00
Steve-Mcl
7b52ef34be Remember compact/pretty flow export user choice
closes #3849
2022-12-05 22:17:05 +00:00
Kazuhito Yokoi
7117472e73 Add Japanese translation for range node 2022-12-05 23:13:50 +09:00
Kazuhito Yokoi
c24b123917 Add Japanese translation for editor actions 2022-12-05 23:11:56 +09:00
Hiroyasu Nishiyama
9eb8cf121c Update Japanese message reflecting English message update 2022-12-05 11:29:37 +09:00
Hiroyasu Nishiyama
41ef9ae010 Update test/nodes/core/common/91-global-config_spec.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:29 +09:00
Hiroyasu Nishiyama
1674bbbde9 Update packages/node_modules/@node-red/nodes/locales/ja/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:20 +09:00
Hiroyasu Nishiyama
0fb739f7cd Update packages/node_modules/@node-red/nodes/locales/ja/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:04 +09:00
Hiroyasu Nishiyama
169fa940e4 Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:16:53 +09:00
Hiroyasu Nishiyama
c9664cc425 Update packages/node_modules/@node-red/nodes/locales/en-US/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:16:35 +09:00
Hiroyasu Nishiyama
e61cdff655 Update packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:15:43 +09:00
Hiroyasu Nishiyama
a479b8a5d7 Update packages/node_modules/@node-red/nodes/core/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:15:32 +09:00
Kazuhito Yokoi
26462e684b Merge branch 'node-red:dev' into dev 2022-12-05 01:08:45 +09:00
Nick O'Leary
113d42ef35 Merge pull request #3970 from node-red/prevent-text-selection
Prevent dbl-click opening node edit dialog with text selected
2022-12-03 23:34:57 +00:00
Nick O'Leary
c18018f017 Ensure credentials object is removed before returning node in getFlow request
Fixes #3953
2022-12-03 23:33:33 +00:00
Nick O'Leary
e804addf0a Prevent dbl-click opening node edit dialog with text selected
Fixes #3958
2022-12-03 23:13:44 +00:00
Nick O'Leary
04cea003b9 Merge pull request #3938 from node-red/locking-flows
Locking flows
2022-12-03 23:01:58 +00:00
Nick O'Leary
65fcc56a56 Merge pull request #3968 from node-red/context-menu-updates
Context menu updates
2022-12-03 23:01:37 +00:00
Nick O'Leary
c065d253e9 Merge pull request #3969 from node-red/bump-deps
Update dependencies
2022-12-03 23:01:10 +00:00
Nick O'Leary
4bb2b91ee6 Merge pull request #3966 from Steve-Mcl/fix-mqtt-single-subscription
fix single subscription mqtt node status
2022-12-03 22:47:42 +00:00
Nick O'Leary
b3f761776d Update dependencies 2022-12-03 22:43:03 +00:00
Nick O'Leary
dce1cccbde Allow subflow to be edited if instance exists on locked flow 2022-12-03 22:30:35 +00:00
Nick O'Leary
3630056ed8 Fix RED.nodes.clear() when handling locked flows 2022-12-03 21:44:33 +00:00
Nick O'Leary
2d6e1d7089 NLS updates for context menu 2022-12-03 21:29:27 +00:00
Nick O'Leary
71db79ba53 More context menu options 2022-12-03 21:16:57 +00:00
GogoVega
a6934b3cba fix: Typos (grammar) 2022-12-03 09:37:08 +01:00
GogoVega
5be378e266 fix: Typos (infinitif) 2022-12-02 18:27:36 +01:00
GogoVega
a9b6eaa31f Add French option in other languages 2022-12-02 18:15:06 +01:00
GogoVega
01f9ce0015 Add French translation of runtime.json file 2022-12-02 18:09:06 +01:00
GogoVega
f4309f5af6 fix: Typos (infinitif) 2022-12-02 18:07:39 +01:00
GogoVega
1004ce564f Add French translation of storage nodes 2022-12-02 14:21:34 +01:00
GogoVega
5a1823d13e Add French translation of sequence nodes 2022-12-02 12:10:43 +01:00
GogoVega
66fb66edbc Add French translation of messages.json file 2022-12-01 21:29:12 +01:00
Steve-Mcl
5bb66ed7d4 fix single subscription mqtt node status 2022-12-01 13:08:48 +00:00
Steve-Mcl
f61971bc23 first pass at common proxy determining logic 2022-12-01 12:53:03 +00:00
Nick O'Leary
14e74afb07 Merge pull request #3909 from node-red/warn-invalid-msg
Add check that node sends object rather than primitive type
2022-11-30 22:28:09 +00:00
Nick O'Leary
742f05f59d Update packages/node_modules/@node-red/runtime/lib/flows/Flow.js 2022-11-30 22:23:04 +00:00
Nick O'Leary
79db4f8aa1 Update packages/node_modules/@node-red/runtime/lib/flows/Flow.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-11-30 22:22:44 +00:00
Nick O'Leary
c4e216f839 Merge pull request #3912 from node-red/handle_username_spaces
Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname
2022-11-30 22:18:24 +00:00
Nick O'Leary
752fdfedf2 Merge pull request #3935 from node-red/Add-drop-mode-to-range-node
Add drop mode to range node
2022-11-30 22:17:02 +00:00
Nick O'Leary
2a49e7c8ef Merge pull request #3908 from node-red/add-httpheaders
Ensure msg.req.headers is enumerable
2022-11-30 22:15:23 +00:00
Nick O'Leary
02af01d2ca Merge pull request #3867 from Steve-Mcl/improve-nodesdir-scan
Fix nodesDir scan when node package has js/html in sub dir to package.json
2022-11-30 22:15:00 +00:00
Nick O'Leary
07c05c1f2a Merge pull request #3930 from node-red/tab-context-menu
Improve UX around hiding flows via context menu
2022-11-30 22:13:54 +00:00
Nick O'Leary
ee4af4c7bf Merge pull request #3920 from node-red/CSV-header-props-with-quotes
CSV node check header properties for ' and "
2022-11-30 22:10:40 +00:00
Nick O'Leary
3d565e74a5 Merge pull request #3917 from kazuhitoyokoi/master-fixpermission
Fix file permissions
2022-11-30 22:10:03 +00:00
Nick O'Leary
3cb84222f8 Merge pull request #3921 from node-red/fix-group-unknown
Handle replacing unknown node inside group or subflow
2022-11-30 22:09:35 +00:00
Nick O'Leary
1b013bb73b Merge pull request #3949 from Steveorevo/master
Fix #3939, red border red-ui-typedInput-container
2022-11-30 22:07:04 +00:00
Nick O'Leary
fd54e625d5 Merge pull request #3965 from we11adam/master
fix: fix typo in catch.html
2022-11-30 21:40:34 +00:00
GogoVega
e440694987 Use English names for nodes (better) 2022-11-30 17:35:22 +01:00
GogoVega
b4a12edc61 Add French translation of parsers nodes 2022-11-30 15:38:49 +01:00
GogoVega
2b04d6834f Add French translation of network nodes 2022-11-30 13:34:19 +01:00
GogoVega
1191574e07 fix: Typos 2022-11-30 13:22:10 +01:00
Adam Lau
77f6412d3b fix: fix typo in catch.html 2022-11-30 17:51:13 +08:00
Gauthier Dandele
327eab6c0d fix: capitals and better translation 2022-11-29 23:09:28 +01:00
GogoVega
0d8a0db883 Add French translation of function nodes 2022-11-29 20:50:18 +01:00
GogoVega
9727062d60 Add French translation of common nodes 2022-11-29 12:50:19 +01:00
GogoVega
09979d3270 Translation of the jsonata.json file 2022-11-29 00:10:04 +01:00
GogoVega
116839d6f6 Add French translation 2022-11-28 23:00:31 +01:00
lkaiy
bed6c48e99 Translation of nodes context, setting menu, etc.
Translation of node context, setting context, etc.
Corrected previously mistranslated ones (grammar, word-spacing)
2022-11-15 16:41:36 +08:00
lkaiy
4d09e90b90 Translation of side-menu,context-menu, etc. 2022-11-15 16:39:29 +08:00
lkaiy
1d91cc6fc1 translation side-menu,keyboard, contextmenu, etc. 2022-11-15 16:36:50 +08:00
Stephen J. Carnam
c81cd5450f Support for PHP syntax highlight 2022-11-09 16:45:32 -08:00
Steveorevo
0b663abe50 Fix #3939, red border red-ui-typedInput-container 2022-11-09 10:33:05 -08:00
Nick O'Leary
3b27fb2aa7 Merge branch 'dev' into dev 2022-11-07 23:16:14 +00:00
Nick O'Leary
6bd67ae68c Merge pull request #3944 from node-red-hitachi/fix-deploy-locked-flow
fix deployment of locked flow
2022-11-07 23:13:03 +00:00
Nick O'Leary
14c362d4ba Merge pull request #3942 from node-red-hitachi/fix-watch-test
fix watch node test on MacOS/ARM
2022-11-07 21:12:55 +00:00
Nick O'Leary
f28bc1bff7 Remove jshint warning 2022-11-07 21:11:58 +00:00
Nick O'Leary
de8a5ea262 Merge pull request #3945 from node-red-hitachi/fix-jshint-es-version
fix to allow es11 for jshint check
2022-11-07 21:10:26 +00:00
Nick O'Leary
339013434b Merge pull request #3946 from node-red-hitachi/i18n-item-url-copy-notification
i18n item URL copy notification & add Japanese message
2022-11-07 21:07:57 +00:00
Nick O'Leary
8a3ad331d2 Merge pull request #3947 from node-red-hitachi/add-item-url-Japanese-action-message
add Japanese message for item url copy actions
2022-11-07 21:07:38 +00:00
Hiroyasu Nishiyama
e3892dc26d add Japanese message for item url copy actions 2022-11-07 16:07:46 +09:00
Hiroyasu Nishiyama
b95df6d883 i18n item URL copy notification & add Japanese message 2022-11-07 15:47:19 +09:00
Hiroyasu Nishiyama
11ad03b21e fix to allow es11 for jshint check 2022-11-07 10:42:07 +09:00
Hiroyasu Nishiyama
9cb474ea9c fix deployment of locked flow 2022-11-07 09:40:36 +09:00
Hiroyasu Nishiyama
fce43b4e1d fix condition for platform check 2022-11-05 14:50:14 +09:00
Hiroyasu Nishiyama
1d547500e8 fix watch node test on MacOS/ARM 2022-11-05 14:30:32 +09:00
Hiroyasu Nishiyama
f23d0480e4 add global environment variable feature 2022-11-04 18:42:51 +09:00
Nick O'Leary
fe9c630572 Prevent deleting subflow if instance on locked tab 2022-11-01 11:42:40 +00:00
Nick O'Leary
ce94226c3c Disable subflow/flow menu options if active is locked 2022-11-01 11:29:23 +00:00
Nick O'Leary
f12d36b5ed Locking flows fixes and context menu options 2022-11-01 10:48:48 +00:00
Nick O'Leary
3cb5259494 Initial locking flows UX 2022-11-01 10:37:18 +00:00
Nick O'Leary
a351cd9d9f Add move-to-start/end and better subflow menu options 2022-11-01 10:35:57 +00:00
Nick O'Leary
d8e01584f3 Remove add-flow-to-right option if clicked in tab bar 2022-10-31 20:20:05 +00:00
Kazuhito Yokoi
dd76840568 Fix uncleared translations in change node 2022-11-01 01:09:06 +09:00
Dave Conway-Jones
94690fad7a Merge branch 'CSV-header-props-with-quotes' of https://github.com/node-red/node-red into CSV-header-props-with-quotes 2022-10-29 17:36:17 +01:00
Dave Conway-Jones
d693af9615 CSV node check header properties for ' and "
and add test
to close #3919
2022-10-29 17:35:45 +01:00
Dave Conway-Jones
4cc18c25fe Add drop mode to range node
and include tests
2022-10-29 17:34:29 +01:00
Kazuhito Yokoi
8b398f49c0 Merge branch 'node-red:master' into master-fixpermission 2022-10-29 23:51:05 +09:00
Nick O'Leary
902ce68164 Merge branch 'master' into fix-group-unknown 2022-10-26 00:52:34 +01:00
Nick O'Leary
cd0474ce7b Update packages/node_modules/@node-red/editor-client/src/js/nodes.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-10-26 00:51:41 +01:00
Nick O'Leary
fb499be979 Add context menu to tab bar 2022-10-25 23:44:59 +01:00
Nick O'Leary
69a575097d Remove debug 2022-10-16 22:47:34 +01:00
Nick O'Leary
b40551a8fa Handle replacing unknown node inside group or subflow 2022-10-16 22:38:11 +01:00
Dave Conway-Jones
5b27bcd781 CSV node check header properties for ' and "
and add test
to close #3919
2022-10-16 18:05:21 +01:00
Kazuhito Yokoi
75725a38df Fix file permission 2022-10-12 23:50:33 +09:00
Kazuhito Yokoi
c4e277853c Add Japanese translation for button of node URL 2022-10-12 23:24:21 +09:00
Nick O'Leary
24b055b1b8 Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname 2022-10-04 15:44:29 +01:00
Nick O'Leary
7da3773f7f Merge pull request #3898 from node-red/delay-flush-reset
let delay node handle both flush then reset
2022-10-04 15:39:05 +01:00
Nick O'Leary
f4d7b71984 Ensure errors in preDeliver callback are handled
Fixes #3848
2022-10-04 15:20:06 +01:00
Stephen McLaughlin
6d1a12af4b remove debug/test code 2022-10-04 13:36:23 +01:00
Nick O'Leary
a40e5dbcd4 Add check that node sends object rather than primitive type
Fixes #3876
2022-10-04 11:49:49 +01:00
Nick O'Leary
7c79ca7878 Ensure msg.req.headers is enumerable
Fixes #3878
2022-10-04 11:28:26 +01:00
Steve-Mcl
e6b379358a better logging of set with no types 2022-09-29 21:28:13 +01:00
Dave Conway-Jones
fc657ecc71 let delay node handle both flush then reset
and add tests
2022-09-22 10:51:48 +01:00
hae-iotplatform
87cb61750f Korean Translation (message.json) 2022-09-20 11:02:11 +09:00
hae-iotplatform
3231247fb6 Korean Translation (editor.json) 2022-09-20 11:01:08 +09:00
Nick O'Leary
313bab37e2 Merge pull request #3870 from node-red/uri-fragments
Support uri fragments for nodes and groups including edit support
2022-09-15 21:25:50 +01:00
Nick O'Leary
0caa308757 Add core:copy-item-link action and expose in info sidebar 2022-09-12 20:53:46 +01:00
Nick O'Leary
1fa8f30550 Support uri fragments for nodes and groups including edit support 2022-09-05 21:08:36 +01:00
Steve-Mcl
6a19d8246c add test for specific arrangement of node package 2022-09-03 21:37:27 +01:00
Steve-Mcl
d6bb3a558f fix loading node package in nodesDir on linux
fixes #3861
2022-09-03 21:23:38 +01:00
Nick O'Leary
44300dbb34 Merge pull request #3807 from node-red-hitachi/env-var-jsonata
Env var jsonata
2022-09-02 20:47:30 +01:00
Bruno Feurer
eb53054f49 Editor: quick-add when connect to empty. 2022-08-30 15:07:30 +02:00
NetHans
e147602a3a JSdoc documentation fixed 2022-08-18 21:27:59 +02:00
NetHans
b7a016edcf Update Flow.js
replace tabs with whitespace
2022-08-16 06:37:03 +02:00
NetHans
371253a4f6 catch node extended 2022-08-14 20:45:01 +02:00
NetHans
08ce6cce97 status node extended 2022-08-14 20:24:05 +02:00
NetHans
d7a10328c0 function for group analysis added 2022-08-14 20:20:59 +02:00
Fabio Barcello S. Dos R. Muller
e7c657f82d Some minor changes to sections that were missing in some files
Fixing missing sessions is some pt-BR files and also
one small typo of an hmtl end paragraph </p> in the 21-httprequest.html
of en-US original language file.
2022-08-05 16:29:19 -03:00
Fabio Barcello S. Dos R. Muller
3bae92b356 Changing as pr-3804 discussion
Added some lines to /pt-br/editor.json and
added pt-br into other language editor.json locales.
2022-08-04 11:15:57 -03:00
Hiroyasu Nishiyama
1ddbeaa50f add test cases 2022-07-27 20:25:43 +09:00
Fabulous Muller
b937c37be3 Fixed JSON errors in 2 files
fixed 2 errors in editor.json and messages.json as reported
by run tests #1174 build(16).
2022-07-26 17:18:43 -03:00
Fabulous Muller
0908369dba Portuguese Brazilian (pt-BR) translation
Portuguese Brazilian, pt-BR, translation based on v.3.0.1 branch.
The initial efforts started with PR #3100(V.2.0.3) but there were some
problems and the quality needed to be improved. Now I fixed some
failures and reviewed files line by line, double-checked with text editor
and dictionary updated to last ortographic agreement of the portuguese
language; anyway errors still can happen, this is not a robotic translation
and portuguese has  a latin branch, so one word in english(sometimes) has
many possible translations in portuguese; just to complicate a litlle
bit.

So we are finally making pt-BR available in Node-Red and hope to just
improve from now on.

Resolves: #3100
2022-07-25 16:35:06 -03:00
Hiroyasu Nishiyama
93a88a83a8 add JSONata support for env var definition 2022-07-20 10:13:13 +09:00
Nick O'Leary
30ea300f65 Merge pull request #3769 from node-red/310
Bump dev to 3.1.0-beta.0
2022-07-14 21:16:38 +01:00
Nick O'Leary
04f4d5274d Bump dev to 3.1.0-beta.0 2022-07-14 20:58:25 +01:00
Nick O'Leary
1f9695abd7 Merge pull request #3768 from node-red/master
Get `dev` branch up to date with `master`
2022-07-14 20:56:42 +01:00
Mauricio Bonani
12543d2c2a Ensure all CSS variables are in the output file 2022-07-06 19:06:11 -04:00
638 changed files with 114651 additions and 20533 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@v6
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@v6
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>

View File

@@ -12,16 +12,15 @@ permissions:
jobs:
build:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16]
node-version: [18, 20, 22]
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
@@ -29,8 +28,3 @@ jobs:
- name: Run tests
run: |
npm run test
- name: Publish to coveralls.io
if: ${{ matrix.node-version == 14 }}
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ github.token }}

1
.gitignore vendored
View File

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

View File

@@ -15,5 +15,5 @@
"shadow": true, // allow variable shadowing (re-use of names...)
"sub": true, // don't warn that foo['bar'] should be written as foo.bar
"proto": true, // allow setting of __proto__ in node < v0.12,
"esversion": 6 // allow es6
"esversion": 11 // allow es11(ES2020)
}

View File

@@ -1,261 +1,227 @@
#### 3.0.2: Maintenance Release
#### 4.0.4: Maintenance Release
Editor
- Fix workspace chart bottom property (#3812) @bonanitech
- Update german translation (#3802) @Dennis14e
- Support color reset to the default in subflow and group (#3801) @kazuhitoyokoi
- Allow generateNodeNames to handle names containing regex control chars (#3817) @knolleary
- Hide scrollbars until they're needed (#3808) @bonanitech
- Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary
- remove console.log (#3820) @Steve-Mcl
- Fix `link call` node can call out of a subflow (#4892) @GogoVega
- Fix wrong unlock state when event is triggered after deployment (#4889) @GogoVega
- i18n(App) update with latest language file changes (#4903) @joebordes
- fix typo: depreciated (#4895) @dxdc
Runtime
- Register subflow module instance node with parent flow (#3818) @knolleary
- Update dev dependencies (#4893) @knolleary
Nodes
- MQTT: Allow msg.userProperties to have number values (#4900) @hardillb
- HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb
#### 3.0.1: Maintenance Release
#### 4.0.3: Maintenance Release
Editor
- Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl
- Sys info (diagnostics report) amendments (#3793) @Steve-Mcl
- Allow `mode` and `title` to be omitted in `options` argument for `createEditor` (#3791) @Steve-Mcl
- Fix focus issues (#3789) @Steve-Mcl
- Ensure all typedInput buttons have button type set (#3788) @knolleary
- Do not flag hasUsers=false nodes as unused in search (#3787) @knolleary
- Properly position quick-add dialog in all cases (#3786) @knolleary
- Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary
- Remove use of Object.hasOwn (#3784) @knolleary
#### 3.0.0: Milestone Release
Editor
- Use theme page and header values if settings.js values are not present (#3767) @Steve-Mcl
- Focus editor for undo after some actions in menu (#3759) @kazuhitoyokoi
- Ensure node icon shade has properly rounded corners (#3763) @knolleary
- Fix storing subflow credential type when input has multiple types (#3762) @knolleary
- Ensure global-config and flow-config have info in the hierarchy popover (#3752) @Steve-Mcl
- Include dirty state in history event (#3748) @Steve-Mcl
- Fix display direction of context sub-menu (#3746) @knolleary
- Fix clear pinned paths of debug sidebar menu (#3745) @HiroyasuNishiyama
- prevent exception generating tooltip for deleted nodes (#3742) @Steve-Mcl
- Fix context menu issues ready for v3 beta.5 (#3741) @Steve-Mcl
- Do not generate new node-ids when pasting a cut flow (#3729) @knolleary
- Fix to prevent node from moving out of workspace (#3731) @HiroyasuNishiyama
- Don't let themes change disabled config node background color (#3736) @bonanitech
- Move colors left behind in #3692 to CSS variables (#3737) @bonanitech
- Fix handling of global debug message (#3733) @HiroyasuNishiyama
- Fix label overflow @ config-node palette (#3730) @ralphwetzel
- Fix defaulting to monaco if settings does not contain codeEditor (#3732) @knolleary
- Disable keyboard shortcut mapping when showing Edit[..]Dialog (#3700) @ralphwetzel
- Update add-junction menu to work in more cases (#3727) @knolleary
- Ensure importMap is not null when using import UI (#3723) @Steve-Mcl
- Add Japanese translations for v3.0-beta.4 (#3724) @kazuhitoyokoi
- Fix "split with" on virtual links (#3766) @Steve-Mcl
- Refresh page title after changing tab name (#4850) @kazuhitoyokoi
- Add Japanese translations for v4.0.2 (again) (#4853) @kazuhitoyokoi
- Stay in quick-add mode following context menu insert (#4883) @knolleary
- Do not include Junction type in quick-add for virtual links (#4879) @knolleary
- Multiplayer cursor tracking (#4845) @knolleary
- Hide add-flow options when disabled via editorTheme (#4869) @knolleary
- Fix env-var config select when multiple defined (#4872) @knolleary
- Fix subflow outbound-link filter (#4857) @GogoVega
- Add French translations for v4.0.2 (#4856) @GogoVega
- Fix moving link wires (#4851) @knolleary
- Adjust type search dialog position to prevent x-overflow (#4844) @Steve-Mcl
- fix: modulesInUse might be undefined (#4838) @lorenz-maurer
- Add Japanese translations for v4.0.2 (#4849) @kazuhitoyokoi
- Fix menu to enable/disable selection when it's a group (#4828) @GogoVega
Runtime
- Do not remove unknown credentials of Subflow Modules (#3728) @knolleary
- Add missing entries from beta.4 changelog (#3721) @knolleary
- Update dependencies (#4874) @knolleary
- GitHub: Add citation file to enable "Cite this repository" feature (#4861) @lobis
- Remove use of util.log (#4875) @knolleary
Nodes
- Change: Fix change node, not handling from field properly when using context (#3754) @Fadoli
- Link Call: Fix linkcall registry bugs (#3751) @Steve-Mcl
- WebSocket: Fix close timeout of websocket node (#3734) @HiroyasuNishiyama
- Fix invalid property error in range node example (#4855)
- Fix typo in flow example name (#4854) @kazuhitoyokoi
- Move SNI, ALPN and Verify Server cert out of check (#4882) @hardillb
- Set status of mqtt nodes to "disconnected" when deregistered from broker (#4878) @Steve-Mcl
- MQTT: Ensure will payload is a string (#4873) @knolleary
- Let batch node terminate "early" if msg.parts set to end of sequence (#4829) @dceejay
- Fix unintentional Capitalisation in Split node name (#4835) @dceejay
#### 3.0.0-beta.4: Beta Release
#### 4.0.2: Maintenance Release
Editor
- Move all colours to CSS variables (#3692) @bonanitech
- Fix clicking on node in workspace to hide context menu (#3696) @knolleary
- Fix credential type input item of subflow template (#3703) @HiroyasuNishiyama
- Add option flag `reimport` to `importNodes` (#3718) @Steve-Mcl
- Update german translation (#3691) @Dennis14e
- List welcome tours in help sidebar (#3717) @knolleary
- Ensure 'hidden flow' count doesn't include subflows (#3715) @knolleary
- Fix Chinese translate (#3706) @hotlong
- Fix use default button for node icon (#3714) @kazuhitoyokoi
- Fix select boxes vertical alignment (#3698) @bonanitech
- Ensure workspace clean after undoing dropped node (#3708) @Steve-Mcl
- Use solid colour as config node icon background to hide text overflow (#3710) @Steve-Mcl
- Increase quick-add height to reveal 2 most recent entries (#3711) @Steve-Mcl
- Set default editor to monaco in absence of user preference (#3702) @knolleary
- Add Japanese translations for v3.0-beta.3 (#3688) @kazuhitoyokoi
- Fix handling of spacebar inside JSON visual editor (#3687) @knolleary
- Fix menu padding to handle both icons and submenus (#3686) @knolleary
- Include scroll offset when positioning quick-add dialog (#3685) @knolleary
- Use a more subtle border on the header (#4818) @bonanitech
- Improve the editor's French translations (#4824) @GogoVega
- Clean up orphaned editors (#4821) @Steve-Mcl
- Fix node validation if the property is not required (#4812) @GogoVega
- Ensure mermaid.min.js is cached properly between loads of the editor (#4817) @knolleary
Runtime
- Allow flows to be stopped and started manually (#3719) @knolleary
- Import default export if node is a transpiled es module (#3669) @dschmidt
- Leave Monaco theme commented out by default (#3704) @bonanitech
- Allow auth cookie name to be customised (#4815) @knolleary
- Guard against undefined sessions in multiplayer (#4816) @knolleary
Nodes
- CSV: Fix CSV node to handle when outputting text fields (#3716) @dceejay
- Delay: Fix delay rate limit last timing when empty (#3709) @dceejay
- Link: Ensure link-call cache is updated when link-in is modified (#3695) @Steve-Mcl
- Join: Join node in reduce mode doesn't keep existing msg properties (#3670) @dceejay
- Template: Add support for evalulating {{env.<var>}} within a template node (#3690) @cow0w
#### 3.0.0-beta.3: Beta Release
#### 4.0.1: Maintenance Release
Editor
- Add Right-Click content menu (#3678) @knolleary
- Fix disable junction (#3671) @HiroyasuNishiyama
- Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi
- Reset mouse state when switching tabs (#3643) @knolleary
- Fix uncorrect fix of junction to subflow conversion (#3666) @HiroyasuNishiyama
- Fix undoing junction to subflow (#3653) @HiroyasuNishiyama
- Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama
- Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama
- Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi
- Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama
- Fix to sanitize tab name (#3646) @HiroyasuNishiyama
- Fix selector placement (#3644) @bonanitech
- Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi
- Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama
- Fix layer of palette node (#3638) @HiroyasuNishiyama
- Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama
- Fix typo in CSS (#3628) @bonanitech
- Use the correct variable for the gutter text color (#3615) @bonanitech
- Ensure subflow instance credential property values are extracted (#4802) @knolleary
- Use `_ADD_` value for both `add new...` and `none` options (#4800) @GogoVega
- Fix the config node select value assignment (#4788) @GogoVega
- Add tooltip for number of subflow instance on info tab (#4786) @kazuhitoyokoi
- Add Japanese translations for v4.0.0 (#4785) @kazuhitoyokoi
Runtime
- Support loading node modules from `nodesdir` (#3676) @Steve-Mcl
- fix buffer parse error message of evaluateNodeProperty (#3624) @HiroyasuNishiyama
- Ensure group nodes are properly exported in /flow api (#4803) @knolleary
Nodes
Nodes
- File: Further simplify file node filename entry UX (v3) (#3677) @Steve-Mcl
- Function: Fix initial cursor position of init/finalize tab of function node (#3674) @HiroyasuNishiyama
- Function: Fix ESM module loading in Function node (#3645) @knolleary
- Inject: Fix JSONata evaluation of inject button (#3632) @HiroyasuNishiyama
- TCP: Dont delete TCP socket twice (#3630) @Steve-Mcl
- MQTT Node: define noproxy variable (#3626) @Steve-Mcl
- Debug: i18n debug sidebar node label (#3623) @HiroyasuNishiyama
- Joins: make using msg.parts optional in join node (#4796) @dceejay
- HTTP Request: UI proxy should setup agents for both http_proxy and https_proxy (#4794) @Steve-Mcl
- HTTP Request: Remove default user agent (#4791) @Steve-Mcl
#### 3.0.0-beta.2: Beta Release
#### 4.0.0: Milestone Release
**Migration from 2.x**
This marks the next major release of Node-RED. The following changes represent
those added since the last beta. Check the beta release details below for the complete
list.
- The 'slice wires' action has changed from Ctrl-RightMouseButton to Alt-LeftMouseButton
Breaking Changes
- Node-RED now requires Node 18.x or later. At the time of release, we recommend
using Node 20.
Editor
- Rework Junctions to be more node like in their event handling (#3607) @knolleary
- Change slicing / slice-junction operations over to mouse button 0 (Left Mouse Button) (#3609) @Steve-Mcl
- Do not slice-junction link node wires (#3608) @knolleary
- Handle many-to-one slicing of wires (#3604) @knolleary
- Ensure ACE worker options are set (#3611) @Steve-Mcl
- Remove duplicate history add of ungroup event (#3605) @knolleary
- use text width instead of number of characters for deciding select fi… (#3603) @HiroyasuNishiyama
- Update Japanese info of link call node reflecting update of English info (#3600) @HiroyasuNishiyama
- Fix typedInput label not visible on themes (#3580) @bonanitech
- Fix project switching when junctions are present (#3595) @Steve-Mcl
- Fix junction: when wiring from a regular nodes INPUT, backwards to a junction (#3591) @Steve-Mcl
- Fix error initialising flow tab editor (#3585) @Steve-Mcl
- Add Japanese translations for v3.0-beta.1 (#3576) @kazuhitoyokoi
- Fix image paths where `red/image/typedInput/XXXX.png` should be `red/image/typedInput/XXXX.svg` (#3592) @kazuhitoyokoi
- Fix browser console error Uncaught TypeError when searching certain terms (#3584) @Steve-Mcl
Runtime
- fix error on system-info action (#3589) @HiroyasuNishiyama
Nodes
- I18n switch rule selector (#3602) @HiroyasuNishiyama
- Handle removal of event handlers to allow mqtt client.end() to work (#3594) @PhilDay-CT
- update link-call node info according to current behavior (#3597) @HiroyasuNishiyama
#### 3.0.0-beta.1: Beta Release
**Migration from 2.x**
- Node-RED now requires Node.js 14.x or later.
- New installs of Node-RED will default to the monaco editor.
- Add `httpStaticCors` (#4761) @knolleary
- Update dependencies (#4763) @knolleary
- Sync master to dev (#4756) @knolleary
- Add tooltip and message validation to `typedInput` (#4747) @GogoVega
- Replace bcrypt with @node-rs/bcrypt (#4744) @knolleary
- Export Nodes dialog refinement (#4746) @Steve-Mcl
#### 4.0.0-beta.4: Beta Release
Editor
- Add Junctions (#3462) @knolleary
- Allow node name to be auto-generated when added (#3478, #3538) @knolleary
- Set monaco as default code editor as of v3.x (#3543) @Steve-Mcl
- Update Monaco to V0.33.0 (#3522) @Steve-Mcl
- Auto-complete Improvements (#3521) @Steve-Mcl
- Add a tooltip to debug sidebar messages to reveal full path to node (#3503) @knolleary
- Fix down arrow triggering menu in search box (#3507) @Steve-Mcl
- Add Japanese translations for v3.0 (#3512) @kazuhitoyokoi
- Add feature: Continuous search tools (search previous, search next) (#3405) @Steve-Mcl
- Add feature: split-wire-to-links (#3399, #3476) @Steve-Mcl
- Add copy button to node properties tables (#3390) @knolleary
- Add info-tab search options dropdown to the regular search (#3395) @Steve-Mcl
- New Feature: Add ability to find modified nodes/flows. (#3392) @Steve-Mcl
- Code editor ux improvements around remembering state of each code editor in a flow (#3553) @Steve-Mcl
- Make it easier to apply themes on SVG icons (#3515) @bonanitech
- Add support of property validation message (#3438) @HiroyasuNishiyama
- Ensure node validation tooltip is closed when field becomes valid (#3570) @knolleary
- Add "search for" buttons to notifications (#3567) @Steve-Mcl
- Don't let themes change node config colors (#3564) @bonanitech
- Fix gap between typedInput containers borders (#3560) @bonanitech
- Fix recording removed links in edit history (#3547) @knolleary
- Remove unused SASS vars (#3536) @bonanitech
- Add custom style for jQuery widgets borders (#3537) @bonanitech
- fix out of scope reference of hasUnusedConfig variable (#3535) @HiroyasuNishiyama
- correct "non string" check parenthesis (#3524) @Steve-Mcl
- Ensure i18n of scoped package name (#3516) @Steve-Mcl
- Prevent shortcut deploy when deploy button shaded (#3517) @Steve-Mcl
- Fix: Sidebar "Configuration" filter button tooltip (#3500) @ralphwetzel
- Add the ability to customize diff colors even more (#3499) @bonanitech
- Do JSON comparison of old value/new value in editor (#3481) @Steve-Mcl
- Fix nodes losing their wires when in an iframe (#3484) @zettca
- Improve scroll into view (#3468) @Steve-Mcl
- Do not show 1st tab if hidden when loading (#3464) @Steve-Mcl
- Fix the Sidebar Config is not refreshed after a deploy (#4734) @GogoVega
- Fix checkboxes are not updated when calling `typedInput("value", "")` (#4729) @GogoVega
- Fix panning with middle mouse button on windows 10/11 (#4716) @corentin-sodebo-voile
- Add Japanese translation for sidebar tooltip (#4727) @kazuhitoyokoi
- Translate the number of items selected in the options list (#4730) @GogoVega
- Fix a checkbox should return a Boolean value and not the string `on` (#4715) @GogoVega
- Deleting a grouped node should update the group (#4714) @GogoVega
- Change the Config Node cursor to `pointer` (#4711) @GogoVega
- Add missing tooltips to Sidebar (#4713) @GogoVega
- Allow nodes to return additional history entries in onEditSave (#4710) @knolleary
- Update to Monaco 0.49.0 (#4725) @Steve-Mcl
- Add Japanese translations for 4.0.0-beta.3 (#4726) @kazuhitoyokoi
- Show lock on deploy if user is read-only (#4706) @knolleary
Runtime
- Fix importing external module from node-red module (#3541) @knolleary
- Add support for multiple static paths with optional static root (#3542) @Steve-Mcl
- Store external token when authenticating if provided (#3460) @ArFe
- Support OAuth/OpenID logout (#3388) @mw75
- Allow adminAuth to auto-login users when using passport strategy (#3519) @knolleary
- Add runtime diagnostics admin endpoint (#3511) @Steve-Mcl
- Don't start if user has no home directory (#3540) @hardillb
- Error on invalid encrypted credentials (#3498) @sammachin
- Ensure all CSS variables are in the output file (#3743) @bonanitech
- Add httpAdminCookieOptions (#4718) @knolleary
- chore: migrate deprecated `util.isArray` (#4724) @Rotzbua
- Add --version cli args (#4707) @knolleary
- feat(grunt): fail if files are missing (#4739) @Rotzbua
- fix(node-red-pi): node-red not started by path (#4736) @Rotzbua
- fix(editor): remove trailing slash (#4735) @Rotzbua
- fix: remove deprecated mqtt.js (#4733) @Rotzbua
Nodes
- Debug: Add message count option to Debug status (#3544 #3551) @rafaelmuynarsk @knolleary
- File: Change basic Filename field to a typedInput (#3533) @Steve-Mcl
- HTTP Request: Add UI for Http Request node headers (#3488) @Steve-Mcl
- Inject: let inject optionally fire at start in only at time mode. (#3385) @dceejay
- Link Call: Dynamic link call (#3463) @Steve-Mcl
- Link Call: Display link targets of nodes in a regular flow, for Link Call nodes inside a subflow (#3528) @Steve-Mcl
- MQTT: MQTT payload auto parsing improvements (#3530) @Steve-Mcl
- MQTT: Add client and Runtime MQTT topic validation (#3563) @Steve-Mcl [dev]
- MQTT: save and restore v5 config user props (#3562) @Steve-Mcl
- MQTT: Fix incorrect MQTT status (#3552) @Steve-Mcl
- MQTT: fix reference error of msg.status in debug node (#3526) @HiroyasuNishiyama
- MQTT: Add unit tests for MQTT nodes (#3497) @Steve-Mcl
- MQTT: fix typo of will properties (#3502) @Steve-Mcl
- MQTT: ensure mqtt v5 props can be set false (#3472) @Steve-Mcl
- Switch: add check for NaN in is of type number to be false (#3409) @dceejay
- TCP: TCP node better split (#3465) @dceejay
- Watch: Update Watch node to use node-watch module (#3559 #3569) @knolleary
- WebSocket: call done after ws disconnects (#3531) @Steve-Mcl
- Perform Proxy logic more like cURL (#4616) @Steve-Mcl
#### 4.0.0-beta.3: Beta Release
Editor
- Improve background-deploy notification handling (#4692) @knolleary
- Hide workspace tab on middle mouse click (#4657) @Steve-Mcl
- multiplayer: Add user presence indicators (#4666) @knolleary
- Enable updating dependency node of package.json in project feature (#4676) @kazuhitoyokoi
- Add French translations for 4.0.0-beta.2 (#4681) @GogoVega
- Add Japanese translations for 4.0.0-beta.2 (#4674) @kazuhitoyokoi
- Fix saving of conf-type properties in module packaged subflows (#4658) @knolleary
- Add npm install timeout notification (#4662) @hardillb
- Fix undo of subflow env property edits (#4667) @knolleary
- Fix three error typos in monaco.js (#4660) @JoshuaCWebDeveloper
- docs: Add closing paragraph tag (#4664) @ZJvandeWeg
- Avoid login loops when autoLogin enabled but login fails (#4684) @knolleary
Runtime
- Allow blank strings to be used for env var property substitutions (#4672) @knolleary
- Use rfdc for cloning pure JSON values (#4679) @knolleary
- fix: remove outdated Node 11+ check (#4314) @Rotzbua
- feat(ci): add new nodejs v22 (#4694) @Rotzbua
- fix(node): increase required node >=18.5 (#4690) @Rotzbua
- fix(dns): remove outdated node check (#4689) @Rotzbua
- fix(polyfill): remove import module polyfill (#4688) @Rotzbua
- Fix typo (#4686) @Rotzbua
Nodes
- Pass full error object in Function node and copy over cause property (#4685) @knolleary
- Replacing vm.createScript in favour of vm.Script (#4534) @patlux
#### 4.0.0-beta.2: Beta Release
Editor
- Introduce multiplayer feature (#4629) @knolleary
- Separate the "add new config-node" option into a new (+) button (#4627) @GogoVega
- Retain Palette categories collapsed and filter to localStorage (#4634) @knolleary
- Ensure palette filter reapplies and clear up unknown categories (#4637) @knolleary
- Add support for plugin (only) modules to the palette manager (#4620) @knolleary
- Update monaco to latest and node types to 18 LTS (#4615) @Steve-Mcl
Runtime
- Fix handling of subflow config-node select type in sf module (#4643) @knolleary
- Comms API updates (#4628) @knolleary
- Add French translations for 4.0.0-beta.1 (#4621) @GogoVega
- Add Japanese translations for 4.0.0-beta.1 (#4612) @kazuhitoyokoi
Nodes
- Fix change node handling of replacing with boolean (#4639) @knolleary
#### 4.0.0-beta.1: Beta Release
Editor
- Click on id in debug panel highlights node or flow (#4439) @ralphwetzel
- Support config selection in a subflow env var (#4587) @Steve-Mcl
- Add timestamp formatting options to TypedInput (#4468) @knolleary
- Allow RED.view.select to select links (#4553) @lgrkvst
- Add auto-complete to flow/global/env typedInput types (#4480) @knolleary
- Improve the appearance of the Node-RED primary header (#4598) @joepavitt
Runtime
- let settings.httpNodeAuth accept single middleware or array of middlewares (#4572) @kevinGodell
- Upgrade to JSONata 2.x (#4590) @knolleary
- Bump minimum version to node 18 (#4571) @knolleary
- npm: Remove production flag on npm invocation (#4347) @ZJvandeWeg
- Timer testing fix (#4367) @hlovdal
- Bump to 4.0.0-dev (#4322) @knolleary
Nodes
- TCP node - when resetting, if no payload, stay disconnected @dceejay
- HTML node: add option for collecting attributes and content (#4513) @gorenje
- let split node specify property to split on, and join auto join correctly (#4386) @dceejay
- Add RFC4180 compliant mode to CSV node (#4540) @Steve-Mcl
- Fix change node to return boolean if asked (#4525) @dceejay
- Let msg.reset reset Tcp request node connection when in stay connected mode (#4406) @dceejay
- Let debug node status msg length be settable via settings (#4402) @dceejay
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
#### Older Releases

7
CITATION.cff Normal file
View File

@@ -0,0 +1,7 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
title: "Node-RED"
authors:
- family-names: "OpenJS Foundation"
- family-names: "Contributors"
url: "https://nodered.org"

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

@@ -143,6 +143,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/user.js",
"packages/node_modules/@node-red/editor-client/src/js/comms.js",
"packages/node_modules/@node-red/editor-client/src/js/runtime.js",
"packages/node_modules/@node-red/editor-client/src/js/multiplayer.js",
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js",
"packages/node_modules/@node-red/editor-client/src/js/text/format.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js",
@@ -169,6 +170,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
@@ -206,35 +208,52 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
],
nonull: true,
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
},
vendor: {
files: {
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"node_modules/marked/marked.min.js",
"node_modules/dompurify/dist/purify.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"node_modules/i18next/i18next.min.js",
"node_modules/i18next-http-backend/i18nextHttpBackend.min.js",
"node_modules/jquery-i18next/jquery-i18next.min.js",
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js",
],
// "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
// // TODO: resolve relative resource paths in
// // bootstrap/FA/jquery
// ],
"packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
]
}
files: [
{
src: [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"node_modules/marked/marked.min.js",
"node_modules/dompurify/dist/purify.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"node_modules/i18next/i18next.min.js",
"node_modules/i18next-http-backend/i18nextHttpBackend.min.js",
"node_modules/jquery-i18next/jquery-i18next.min.js",
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js"
],
nonull: true,
dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js"
},
// {
// src: [
// // TODO: resolve relative resource paths in
// // bootstrap/FA/jquery
// ],
// dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css"
// },
{
src: [
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
],
nonull: true,
dest: "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js",
},
{
src: "node_modules/mermaid/dist/mermaid.min.js",
nonull: true,
dest: "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js",
},
]
}
},
uglify: {
@@ -403,7 +422,7 @@ module.exports = function(grunt) {
{
cwd: 'packages/node_modules/@node-red/editor-client/src',
src: [
'types/node/*.ts',
'types/node/**/*.ts',
'types/node-red/*.ts',
],
expand: true,

View File

@@ -1,17 +1,16 @@
# Node-RED
http://nodered.org
https://nodered.org
[![Build Status](https://travis-ci.org/node-red/node-red.svg?branch=master)](https://travis-ci.org/node-red/node-red)
[![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master)
[![Build Status](https://github.com/node-red/node-red/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/node-red/node-red/actions?query=branch%3Amaster)
Low-code programming for event-driven applications.
![Node-RED: Low-code programming for event-driven applications](http://nodered.org/images/node-red-screenshot.png)
![Node-RED: Low-code programming for event-driven applications](https://nodered.org/images/node-red-screenshot.png)
## Quick Start
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
Check out https://nodered.org/docs/getting-started/ for full instructions on getting
started.
1. `sudo npm install -g --unsafe-perm node-red`
@@ -20,7 +19,7 @@ started.
## Getting Help
More documentation can be found [here](http://nodered.org/docs).
More documentation can be found [here](https://nodered.org/docs).
For further help, or general discussion, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).

View File

@@ -1,8 +1,8 @@
{
"name": "node-red",
"version": "3.0.2",
"version": "4.0.4",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"homepage": "https://nodered.org",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -26,69 +26,70 @@
}
],
"dependencies": {
"acorn": "8.7.1",
"acorn-walk": "8.2.0",
"ajv": "8.11.0",
"async-mutex": "0.3.2",
"acorn": "8.12.1",
"acorn-walk": "8.3.4",
"ajv": "8.17.1",
"async-mutex": "0.5.0",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.20.0",
"body-parser": "1.20.3",
"cheerio": "1.0.0-rc.10",
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.5.0",
"cookie-parser": "1.4.6",
"content-type": "1.0.5",
"cookie": "0.7.2",
"cookie-parser": "1.4.7",
"cors": "2.8.5",
"cronosjs": "1.7.1",
"denque": "2.1.0",
"express": "4.18.1",
"express-session": "1.17.3",
"express": "4.21.1",
"express-session": "1.18.1",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"got": "11.8.5",
"fs-extra": "11.2.0",
"got": "12.6.1",
"hash-sum": "2.0.0",
"hpagent": "1.0.0",
"hpagent": "1.2.0",
"https-proxy-agent": "5.0.1",
"i18next": "21.8.16",
"i18next": "21.10.0",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "4.1.0",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.6",
"jsonata": "2.0.5",
"lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0",
"memorystore": "1.6.7",
"mime": "3.0.0",
"moment": "2.29.4",
"moment-timezone": "0.5.34",
"mqtt": "4.3.7",
"moment": "2.30.1",
"moment-timezone": "0.5.46",
"mqtt": "5.7.0",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"node-red-admin": "^3.0.0",
"node-watch": "0.7.3",
"node-red-admin": "^4.0.1",
"node-watch": "0.7.4",
"nopt": "5.0.0",
"oauth2orize": "1.11.1",
"oauth2orize": "1.12.0",
"on-headers": "1.0.2",
"passport": "0.6.0",
"passport": "0.7.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.5.1",
"semver": "7.3.7",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.16.3",
"uuid": "8.3.2",
"ws": "7.5.6",
"xml2js": "0.4.23"
"raw-body": "3.0.0",
"rfdc": "^1.3.1",
"semver": "7.6.3",
"tar": "7.4.3",
"tough-cookie": "^5.0.0",
"uglify-js": "3.17.4",
"uuid": "9.0.1",
"ws": "7.5.10",
"xml2js": "0.6.2"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
"@node-rs/bcrypt": "1.10.4"
},
"devDependencies": {
"dompurify": "2.3.10",
"grunt": "1.5.3",
"dompurify": "2.5.7",
"grunt": "1.6.1",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
"grunt-cli": "~1.5.0",
"grunt-concurrent": "3.0.0",
"grunt-contrib-clean": "2.0.1",
"grunt-contrib-compress": "2.0.0",
@@ -99,7 +100,7 @@
"grunt-contrib-watch": "1.1.0",
"grunt-jsdoc": "2.4.1",
"grunt-jsdoc-to-markdown": "6.0.0",
"grunt-jsonlint": "2.1.3",
"grunt-jsonlint": "3.0.0",
"grunt-mkdir": "~1.1.0",
"grunt-npm-command": "~0.1.2",
"grunt-sass": "~3.1.0",
@@ -108,19 +109,20 @@
"i18next-http-backend": "1.4.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "4.0.18",
"marked": "4.3.0",
"mermaid": "11.3.0",
"minami": "1.2.3",
"mocha": "9.2.2",
"node-red-node-test-helper": "^0.3.0",
"nodemon": "2.0.19",
"node-red-node-test-helper": "^0.3.3",
"nodemon": "3.1.7",
"proxy": "^1.0.2",
"sass": "1.54.2",
"sass": "1.62.1",
"should": "13.2.3",
"sinon": "11.1.2",
"stoppable": "^1.1.0",
"supertest": "6.2.4"
"supertest": "6.3.3"
},
"engines": {
"node": ">=14"
"node": ">=18.5"
}
}

View File

@@ -33,6 +33,9 @@ module.exports = {
store: req.query['store'],
req: apiUtils.getRequestLogObject(req)
}
if (req.query['keysOnly'] !== undefined) {
opts.keysOnly = true
}
runtimeAPI.context.getValue(opts).then(function(result) {
res.json(result);
}).catch(function(err) {

View File

@@ -14,8 +14,6 @@
* limitations under the License.
**/
var express = require("express");
var nodes = require("./nodes");
var flows = require("./flows");
var flow = require("./flow");
@@ -37,18 +35,9 @@ module.exports = {
plugins.init(runtimeAPI);
diagnostics.init(settings, runtimeAPI);
var needsPermission = auth.needsPermission;
var adminApp = express();
var defaultServerSettings = {
"x-powered-by": false
}
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (var eOption in serverSettings) {
adminApp.set(eOption, serverSettings[eOption]);
}
const needsPermission = auth.needsPermission;
const adminApp = apiUtil.createExpressApp(settings)
// Flows
adminApp.get("/flows",needsPermission("flows.read"),flows.get,apiUtil.errorHandler);
@@ -102,6 +91,7 @@ module.exports = {
// Plugins
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
adminApp.get(/^\/plugins\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("plugins.read"),plugins.getConfig,apiUtil.errorHandler);
adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);

View File

@@ -40,5 +40,31 @@ module.exports = {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err);
})
},
getConfig: function(req, res) {
let opts = {
user: req.user,
module: req.params[0],
req: apiUtils.getRequestLogObject(req)
}
if (req.get("accept") === "application/json") {
runtimeAPI.nodes.getNodeInfo(opts.module).then(function(result) {
res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
opts.lang = "en-US";
}
runtimeAPI.plugins.getPluginConfig(opts).then(function(result) {
return res.send(result);
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}
};

View File

@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var apiUtils = require("../util");
var runtimeAPI;
var settings;
var theme = require("../editor/theme");

View File

@@ -160,20 +160,34 @@ function completeVerify(profile,done) {
function genericStrategy(adminApp,strategy) {
var crypto = require("crypto")
var session = require('express-session')
var MemoryStore = require('memorystore')(session)
const crypto = require("crypto")
const session = require('express-session')
const MemoryStore = require('memorystore')(session)
adminApp.use(session({
// As the session is only used across the life-span of an auth
// hand-shake, we can use a instance specific random string
secret: crypto.randomBytes(20).toString('hex'),
resave: false,
saveUninitialized: false,
store: new MemoryStore({
checkPeriod: 86400000 // prune expired entries every 24h
})
}));
const sessionOptions = {
// As the session is only used across the life-span of an auth
// hand-shake, we can use a instance specific random string
secret: crypto.randomBytes(20).toString('hex'),
resave: false,
saveUninitialized: false,
store: new MemoryStore({
checkPeriod: 86400000 // prune expired entries every 24h
})
}
if (settings.httpAdminCookieOptions) {
sessionOptions.cookie = {
path: '/',
httpOnly: true,
secure: false,
maxAge: null,
...settings.httpAdminCookieOptions
}
if (sessionOptions.cookie.name){
sessionOptions.name = sessionOptions.cookie.name
delete sessionOptions.cookie.name
}
}
adminApp.use(session(sessionOptions));
//TODO: all passport references ought to be in ./auth
adminApp.use(passport.initialize());
adminApp.use(passport.session());
@@ -205,11 +219,12 @@ function genericStrategy(adminApp,strategy) {
passport.use(new strategy.strategy(options, verify));
adminApp.get('/auth/strategy',
passport.authenticate(strategy.name, {session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot
passport.authenticate(strategy.name, {
session:false,
failWithError: true,
failureMessage: true
}),
completeGenerateStrategyAuth,
completeGenericStrategyAuth,
handleStrategyError
);
@@ -221,14 +236,14 @@ function genericStrategy(adminApp,strategy) {
passport.authenticate(strategy.name, {
session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot
failWithError: true
}),
completeGenerateStrategyAuth,
completeGenericStrategyAuth,
handleStrategyError
);
}
function completeGenerateStrategyAuth(req,res) {
function completeGenericStrategyAuth(req,res) {
var tokens = req.user.tokens;
delete req.user.tokens;
// Successful authentication, redirect home.
@@ -238,6 +253,8 @@ function handleStrategyError(err, req, res, next) {
if (res.headersSent) {
return next(err)
}
// Remove the header that passport auto-adds as we don't need it
res.removeHeader('WWW-Authenticate')
log.audit({event: "auth.login.fail.oauth",error:err.toString()});
res.redirect(settings.httpAdminRoot + '?session_message='+err.toString());
}

View File

@@ -25,7 +25,7 @@ function hasPermission(userScope,permission) {
}
var i;
if (util.isArray(permission)) {
if (Array.isArray(permission)) {
// Multiple permissions requested - check each one
for (i=0;i<permission.length;i++) {
if (!hasPermission(userScope,permission[i])) {
@@ -36,7 +36,7 @@ function hasPermission(userScope,permission) {
return true;
}
if (util.isArray(userScope)) {
if (Array.isArray(userScope)) {
if (userScope.length === 0) {
return false;
}

View File

@@ -18,7 +18,6 @@ var BearerStrategy = require('passport-http-bearer').Strategy;
var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
var passport = require("passport");
var crypto = require("crypto");
var util = require("util");
var Tokens = require("./tokens");

View File

@@ -17,7 +17,7 @@
var util = require("util");
var clone = require("clone");
var bcrypt;
try { bcrypt = require('bcrypt'); }
try { bcrypt = require('@node-rs/bcrypt'); }
catch(e) { bcrypt = require('bcryptjs'); }
var users = {};
var defaultUser = null;
@@ -33,11 +33,11 @@ function authenticate() {
if (args.length === 2) {
// Username/password authentication
var password = args[1];
return new Promise(function(resolve,reject) {
bcrypt.compare(password, user.password, function(err, res) {
resolve(res?cleanUser(user):null);
});
});
return bcrypt.compare(password, user.password).then(res => {
return res ? cleanUser(user) : null
}).catch(err => {
return null
})
} else {
// Try to extract common profile information
if (args[0].hasOwnProperty('photos') && args[0].photos.length > 0) {
@@ -74,7 +74,7 @@ function init(config) {
} else {
var us = config.users;
/* istanbul ignore else */
if (!util.isArray(us)) {
if (!Array.isArray(us)) {
us = [us];
}
for (var i=0;i<us.length;i++) {

View File

@@ -77,6 +77,53 @@ function CommsConnection(ws, user) {
log.trace("comms.close "+self.session);
removeActiveConnection(self);
});
const handleAuthPacket = function(msg) {
Tokens.get(msg.auth).then(function(client) {
if (client) {
Users.get(client.user).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(msg, client.scope,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(msg, null,null,false);
}
});
} else {
Users.tokens(msg.auth).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(msg, user.permissions,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(msg, null,null,false);
}
});
}
});
}
const completeConnection = function(msg, userScope, session, sendAck) {
try {
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
ws.send(JSON.stringify({auth:"fail"}));
ws.close();
} else {
pendingAuth = false;
addActiveConnection(self);
self.token = msg.auth;
if (sendAck) {
ws.send(JSON.stringify({auth:"ok"}));
}
}
} catch(err) {
console.log(err.stack);
// Just in case the socket closes before we attempt
// to send anything.
}
}
ws.on('message', function(data,flags) {
var msg = null;
try {
@@ -86,68 +133,34 @@ function CommsConnection(ws, user) {
return;
}
if (!pendingAuth) {
if (msg.subscribe) {
if (msg.auth) {
handleAuthPacket(msg)
} else if (msg.subscribe) {
self.subscribe(msg.subscribe);
// handleRemoteSubscription(ws,msg.subscribe);
} else if (msg.topic) {
runtimeAPI.comms.receive({
user: self.user,
client: self,
topic: msg.topic,
data: msg.data
})
}
} else {
var completeConnection = function(userScope,session,sendAck) {
try {
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
ws.send(JSON.stringify({auth:"fail"}));
ws.close();
} else {
pendingAuth = false;
addActiveConnection(self);
self.token = msg.auth;
if (sendAck) {
ws.send(JSON.stringify({auth:"ok"}));
}
}
} catch(err) {
console.log(err.stack);
// Just in case the socket closes before we attempt
// to send anything.
}
}
if (msg.auth) {
Tokens.get(msg.auth).then(function(client) {
if (client) {
Users.get(client.user).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(client.scope,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,null,false);
}
});
} else {
Users.tokens(msg.auth).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(user.permissions,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,null,false);
}
});
}
});
handleAuthPacket(msg)
} else {
if (anonymousUser) {
log.audit({event: "comms.auth",user:anonymousUser});
self.user = anonymousUser;
completeConnection(anonymousUser.permissions,null,false);
completeConnection(msg, anonymousUser.permissions, null, false);
//TODO: duplicated code - pull non-auth message handling out
if (msg.subscribe) {
self.subscribe(msg.subscribe);
}
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,null,false);
completeConnection(msg, null,null,false);
}
}
}

View File

@@ -14,11 +14,9 @@
* limitations under the License.
**/
var express = require("express");
var path = require('path');
var comms = require("./comms");
var library = require("./library");
var info = require("./settings");
var auth = require("../auth");
@@ -46,14 +44,15 @@ module.exports = {
runtimeAPI = _runtimeAPI;
needsPermission = auth.needsPermission;
if (!settings.disableEditor) {
info.init(runtimeAPI);
info.init(settings, runtimeAPI);
comms.init(server,settings,runtimeAPI);
var ui = require("./ui");
ui.init(runtimeAPI);
ui.init(settings, runtimeAPI);
const editorApp = apiUtil.createExpressApp(settings)
var editorApp = express();
if (settings.requireHttps === true) {
editorApp.enable('trust proxy');
editorApp.use(function (req, res, next) {
@@ -86,7 +85,7 @@ module.exports = {
//Projects
var projects = require("./projects");
projects.init(runtimeAPI);
projects.init(settings, runtimeAPI);
editorApp.use("/projects",projects.app());
// Locales

View File

@@ -15,8 +15,6 @@
**/
var apiUtils = require("../util");
var fs = require('fs');
var fspath = require('path');
var runtimeAPI;

View File

@@ -13,9 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var fs = require('fs');
var path = require('path');
// var apiUtil = require('../util');
var i18n = require("@node-red/util").i18n; // TODO: separate module

View File

@@ -14,9 +14,9 @@
* limitations under the License.
**/
var express = require("express");
var apiUtils = require("../util");
var settings;
var runtimeAPI;
var needsPermission = require("../auth").needsPermission;
@@ -77,11 +77,12 @@ function getProjectRemotes(req,res) {
})
}
module.exports = {
init: function(_runtimeAPI) {
init: function(_settings, _runtimeAPI) {
settings = _settings;
runtimeAPI = _runtimeAPI;
},
app: function() {
var app = express();
var app = apiUtils.createExpressApp(settings)
app.use(function(req,res,next) {
runtimeAPI.projects.available().then(function(available) {

View File

@@ -18,9 +18,9 @@ var runtimeAPI;
var sshkeys = require("./sshkeys");
module.exports = {
init: function(_runtimeAPI) {
init: function(settings, _runtimeAPI) {
runtimeAPI = _runtimeAPI;
sshkeys.init(runtimeAPI);
sshkeys.init(settings, runtimeAPI);
},
userSettings: function(req, res) {
var opts = {

View File

@@ -15,15 +15,16 @@
**/
var apiUtils = require("../util");
var express = require("express");
var runtimeAPI;
var settings;
module.exports = {
init: function(_runtimeAPI) {
init: function(_settings, _runtimeAPI) {
runtimeAPI = _runtimeAPI;
settings = _settings;
},
app: function() {
var app = express();
const app = apiUtils.createExpressApp(settings);
// List all SSH keys
app.get("/", function(req,res) {

View File

@@ -14,11 +14,11 @@
* limitations under the License.
**/
var express = require("express");
var util = require("util");
var path = require("path");
var fs = require("fs");
var clone = require("clone");
const apiUtil = require("../util")
var defaultContext = {
page: {
@@ -27,8 +27,7 @@ var defaultContext = {
tabicon: {
icon: "red/images/node-red-icon-black.svg",
colour: "#8f0000"
},
version: require(path.join(__dirname,"../../package.json")).version
}
},
header: {
title: "Node-RED",
@@ -40,6 +39,7 @@ var defaultContext = {
vendorMonaco: ""
}
};
var settings;
var theme = null;
var themeContext = clone(defaultContext);
@@ -70,7 +70,7 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
var result = [];
if (themeValue) {
var array = themeValue;
if (!util.isArray(array)) {
if (!Array.isArray(array)) {
array = [array];
}
@@ -92,7 +92,8 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
}
module.exports = {
init: function(settings, _runtimeAPI) {
init: function(_settings, _runtimeAPI) {
settings = _settings;
runtimeAPI = _runtimeAPI;
themeContext = clone(defaultContext);
if (process.env.NODE_ENV == "development") {
@@ -113,7 +114,15 @@ module.exports = {
var url;
themeSettings = {};
themeApp = express();
themeApp = apiUtil.createExpressApp(settings);
const defaultServerSettings = {
"x-powered-by": false
}
const serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (const eOption in serverSettings) {
themeApp.set(eOption, serverSettings[eOption]);
}
if (theme.page) {
@@ -224,6 +233,10 @@ module.exports = {
themeSettings.projects = theme.projects;
}
if (theme.hasOwnProperty("multiplayer")) {
themeSettings.multiplayer = theme.multiplayer;
}
if (theme.hasOwnProperty("keymap")) {
themeSettings.keymap = theme.keymap;
}
@@ -329,6 +342,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('sha1').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

@@ -24,11 +24,8 @@
* @namespace @node-red/editor-api
*/
var express = require("express");
var bodyParser = require("body-parser");
var util = require('util');
var passport = require('passport');
var cors = require('cors');
var auth = require("./auth");
var apiUtil = require("./util");
@@ -37,7 +34,6 @@ var adminApp;
var server;
var editor;
/**
* Initialise the module.
* @param {Object} settings The runtime settings
@@ -49,7 +45,7 @@ var editor;
function init(settings,_server,storage,runtimeAPI) {
server = _server;
if (settings.httpAdminRoot !== false) {
adminApp = express();
adminApp = apiUtil.createExpressApp(settings);
var cors = require('cors');
var corsHandler = cors({
@@ -64,14 +60,6 @@ function init(settings,_server,storage,runtimeAPI) {
}
}
var defaultServerSettings = {
"x-powered-by": false
}
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (var eOption in serverSettings) {
adminApp.set(eOption, serverSettings[eOption]);
}
auth.init(settings,storage);
var maxApiRequestSize = settings.apiMaxLength || '5mb';
@@ -136,10 +124,11 @@ async function stop() {
editor.stop();
}
}
module.exports = {
init: init,
start: start,
stop: stop,
init,
start,
stop,
/**
* @memberof @node-red/editor-api

View File

@@ -14,10 +14,9 @@
* limitations under the License.
**/
const express = require("express");
var log = require("@node-red/util").log; // TODO: separate module
var i18n = require("@node-red/util").i18n; // TODO: separate module
const { log, i18n } = require("@node-red/util");
module.exports = {
errorHandler: function(err,req,res,next) {
@@ -64,5 +63,17 @@ module.exports = {
path: req.path,
ip: (req.headers && req.headers['x-forwarded-for']) || (req.connection && req.connection.remoteAddress) || undefined
}
},
createExpressApp: function(settings) {
const app = express();
const defaultServerSettings = {
"x-powered-by": false
}
const serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (let eOption in serverSettings) {
app.set(eOption, serverSettings[eOption]);
}
return app
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.0.2",
"version": "4.0.4",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,25 +16,25 @@
}
],
"dependencies": {
"@node-red/util": "3.0.2",
"@node-red/editor-client": "3.0.2",
"@node-red/util": "4.0.4",
"@node-red/editor-client": "4.0.4",
"bcryptjs": "2.4.3",
"body-parser": "1.20.0",
"body-parser": "1.20.3",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.3",
"express": "4.18.1",
"express-session": "1.18.1",
"express": "4.21.1",
"memorystore": "1.6.7",
"mime": "3.0.0",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"oauth2orize": "1.11.1",
"oauth2orize": "1.12.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.6.0",
"ws": "7.5.6"
"passport": "0.7.0",
"ws": "7.5.10"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
"@node-rs/bcrypt": "1.10.4"
}
}

14
packages/node_modules/@node-red/editor-client/locales/de/editor.json vendored Executable file → Normal file
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",
@@ -591,6 +590,8 @@
},
"nodeCount": "__label__ Node",
"nodeCount_plural": "__label__ Nodes",
"pluginCount": "__count__ Plugin",
"pluginCount_plural": "__count__ Plugins",
"moduleCount": "__count__ Modul verfügbar",
"moduleCount_plural": "__count__ Module verfügbar",
"inuse": "In Gebrauch",
@@ -1076,7 +1077,7 @@
"git-auth-error": "Git-Authentifizierungsfehler"
},
"create-success": {
"success": "Sie haben Ihr erstes Projekt erfolgreich erstduellt!",
"success": "Sie haben Ihr erstes Projekt erfolgreich erstellt!",
"desc0": "Sie können jetzt Node-RED wie bisher verwenden.",
"desc1": "Im Tab 'Info' in der Seitenleiste wird angezeigt, welches das aktuelle Projekt ist. Über die Schaltfläche rechts neben dem Projektnamen gelangt man zu 'Projekteinstellungen'.",
"desc2": "Im Tab 'Commit-Historie' in der Seitenleiste werden alle Dateien angezeigt, die sich in Ihrem Projekt geändert haben, und um sie ins lokale Repository zu übertragen (commit). Es zeigt Ihnen eine vollständige Historie Ihrer Commits an und ermöglicht es Ihnen, Ihre Commits in ein (remote) Server-Repository zu schieben (push)."
@@ -1172,15 +1173,6 @@
"diagnostics": {
"title": "System-Informationen"
},
"languages": {
"de": "Deutsch",
"en-US": "Englisch",
"ja": "Japanisch",
"ko": "Koreanisch",
"ru": "Russisch",
"zh-CN": "Chinesisch (Vereinfacht)",
"zh-TW": "Chinesisch (Traditionell)"
},
"validator": {
"errors": {
"invalid-json": "Ungültige JSON-Daten: __error__",

View File

View File

@@ -205,7 +205,7 @@
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Wandelt `number` in eine Zeichenfolge um und formatiert sie in eine dezimale Darstellung, wie im `picture`-String-Parameter vorgegeben.\n\nDas Verhalten dieser Funktion ist mit der XPath/XQuery-Funktion fn:formatnummer konsistent, wie sie in der XPath F&O 3.1-Spezifikation definiert ist. Der `picture`-String-Parameter definiert, wie die Zahl formatiert ist und hat die gleiche Syntax wie fn:format-number.\n\nDer optionale dritte Parameter `options` wird verwendet, um die standardmäßigen länderspezifischen Formatierungszeichen, wie z.B. das Dezimaltrennzeichen, zu überschreiben. Wenn dieser Parameter vorgegeben wird, muss es sich um ein Objekt handeln, das Name/Wert-Paare enthält, die im Abschnitt mit dem Dezimalformat der XPath F&O 3.1-Spezifikation vorgegeben sind."
"desc": "Wandelt `number` in eine Zeichenfolge um und formatiert sie in eine dezimale Darstellung, wie im `picture`-String-Parameter vorgegeben.\n\nDas Verhalten dieser Funktion ist mit der XPath/XQuery-Funktion `fn:formatnummer` konsistent, wie sie in der XPath F&O 3.1-Spezifikation definiert ist. Der `picture`-String-Parameter definiert, wie die Zahl formatiert ist und hat die gleiche Syntax wie `fn:format-number`.\n\nDer optionale dritte Parameter `options` wird verwendet, um die standardmäßigen länderspezifischen Formatierungszeichen, wie z.B. das Dezimaltrennzeichen, zu überschreiben. Wenn dieser Parameter vorgegeben wird, muss es sich um ein Objekt handeln, das Name/Wert-Paare enthält, die im Abschnitt mit dem Dezimalformat der XPath F&O 3.1-Spezifikation vorgegeben sind."
},
"$formatBase": {
"args": "number [, radix]",

View File

@@ -23,7 +23,12 @@
"position": "Position",
"enable": "Enable",
"disable": "Disable",
"upload": "Upload"
"upload": "Upload",
"lock": "Lock",
"unlock": "Unlock",
"locked": "Locked",
"unlocked": "Unlocked",
"format": "Format"
},
"type": {
"string": "string",
@@ -53,22 +58,30 @@
"confirmDelete": "Confirm delete",
"delete": "Are you sure you want to delete '__label__'?",
"dropFlowHere": "Drop the flow here",
"dropImageHere": "Drop the image here",
"addFlow": "Add flow",
"addFlowToRight": "Add flow to the right",
"closeFlow": "Close flow",
"hideFlow": "Hide flow",
"hideOtherFlows": "Hide other flows",
"showAllFlows": "Show all flows",
"showAllFlows": "Show all flows (__count__ hidden)",
"hideAllFlows": "Hide all flows",
"hiddenFlows": "List __count__ hidden flow",
"hiddenFlows_plural": "List __count__ hidden flows",
"showLastHiddenFlow": "Show last hidden flow",
"showLastHiddenFlow": "Reopen hidden flow",
"listFlows": "List flows",
"listSubflows": "List subflows",
"status": "Status",
"enabled": "Enabled",
"disabled": "Disabled",
"info": "Description",
"selectNodes": "Click nodes to select"
"selectNodes": "Click nodes to select",
"enableFlow": "Enable flow",
"disableFlow": "Disable flow",
"lockFlow": "Lock flow",
"unlockFlow": "Unlock flow",
"moveToStart": "Move flow to start",
"moveToEnd": "Move flow to end"
},
"menu": {
"label": {
@@ -101,6 +114,7 @@
"displayStatus": "Show node status",
"displayConfig": "Configuration nodes",
"import": "Import",
"importExample": "Import example flow",
"export": "Export",
"search": "Search flows",
"searchInput": "search your flows",
@@ -109,7 +123,6 @@
"selectionToSubflow": "Selection to Subflow",
"flows": "Flows",
"add": "Add",
"rename": "Rename",
"delete": "Delete",
"keyboardShortcuts": "Keyboard shortcuts",
"login": "Login",
@@ -117,6 +130,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",
@@ -286,7 +304,8 @@
"missingType": "Input not a valid flow - item __index__ missing 'type' property"
},
"conflictNotification1": "Some of the nodes you are importing already exist in your workspace.",
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them."
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them.",
"alreadyExists": "This node already exists"
},
"copyMessagePath": "Path copied",
"copyMessageValue": "Value copied",
@@ -354,8 +373,12 @@
"deleted": "deleted",
"flowDeleted": "flow deleted",
"flowAdded": "flow added",
"moved": "moved",
"movedTo": "moved to __id__",
"movedFrom": "moved from __id__"
"movedFrom": "moved from __id__",
"none": "none",
"position": "position",
"wires": "wires"
},
"nodeCount": "__count__ node",
"nodeCount_plural": "__count__ nodes",
@@ -364,9 +387,14 @@
"reviewChanges": "Review Changes",
"noBinaryFileShowed": "Cannot show binary file contents",
"viewCommitDiff": "View Commit Changes",
"commit": "Commit",
"compareChanges": "Compare Changes",
"saveConflict": "Save conflict resolution",
"conflictHeader": "<span>__resolved__</span> of <span>__unresolved__</span> conflicts resolved",
"localChanges": "Local Changes",
"remoteChanges": "Remote Changes",
"useLocalChanges": "use local changes",
"useRemoteChanges": "use remote changes",
"commonVersionError": "Common Version doesn't contain valid JSON:",
"oldVersionError": "Old Version doesn't contain valid JSON:",
"newVersionError": "New Version doesn't contain valid JSON:"
@@ -403,6 +431,7 @@
},
"errors": {
"noNodesSelected": "<strong>Cannot create subflow</strong>: no nodes selected",
"acrossMultipleGroups": "Cannot create subflow across multiple groups",
"multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection"
}
},
@@ -491,12 +520,14 @@
"unassigned": "Unassigned",
"global": "global",
"workspace": "workspace",
"editor": "edit dialog",
"selectAll": "Select all",
"selectNone": "Select none",
"selectAllConnected": "Select connected",
"addRemoveNode": "Add/remove node from selection",
"editSelected": "Edit selected node",
"deleteSelected": "Delete selected nodes or link",
"deleteSelected": "Delete selection",
"deleteReconnect": "Delete and reconnect",
"importNode": "Import nodes",
"exportNode": "Export nodes",
"nudgeNode": "Move selected nodes (1px)",
@@ -531,7 +562,9 @@
"types": {
"local": "Local",
"examples": "Examples"
}
},
"type": "Type",
"name": "Name"
},
"palette": {
"noInfo": "no information available",
@@ -571,6 +604,7 @@
"editor": {
"title": "Manage palette",
"palette": "Palette",
"allCatalogs": "All Catalogs",
"times": {
"seconds": "seconds ago",
"minutes": "minutes ago",
@@ -592,6 +626,8 @@
},
"nodeCount": "__label__ node",
"nodeCount_plural": "__label__ nodes",
"pluginCount": "__count__ plugin",
"pluginCount_plural": "__count__ plugins",
"moduleCount": "__count__ module available",
"moduleCount_plural": "__count__ modules available",
"inuse": "in use",
@@ -610,6 +646,7 @@
"tab-nodes": "Nodes",
"tab-install": "Install",
"sort": "sort:",
"sortRelevance": "relevance",
"sortAZ": "a-z",
"sortRecent": "recent",
"more": "+ __count__ more",
@@ -618,6 +655,7 @@
"errors": {
"catalogLoadFailed": "<p>Failed to load node catalogue.</p><p>Check the browser console for more information</p>",
"installFailed": "<p>Failed to install: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"installTimeout": "<p>Install continuing the background.</p><p>Nodes will appear in palette when complete. Check the log for more information.</p>",
"removeFailed": "<p>Failed to remove: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"updateFailed": "<p>Failed to update: __module__</p><p>__message__</p><p>Check the log for more information</p>",
"enableFailed": "<p>Failed to enable: __module__</p><p>__message__</p><p>Check the log for more information</p>",
@@ -632,6 +670,9 @@
"body": "<p>Removing '__module__'</p><p>Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.</p>",
"title": "Remove nodes"
},
"removePlugin": {
"body": "<p>Removed plugin __module__. Please reload the editor to clear left-overs.</p>"
},
"update": {
"body": "<p>Updating '__module__'</p><p>Updating the node will require a restart of Node-RED to complete the update. This must be done manually.</p>",
"title": "Update nodes"
@@ -643,7 +684,8 @@
"review": "Open node information",
"install": "Install",
"remove": "Remove",
"update": "Update"
"update": "Update",
"understood": "Understood"
}
}
}
@@ -683,7 +725,11 @@
"empty": "empty",
"globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action",
"find": "Find in workspace"
"find": "Find in workspace",
"copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard",
"showFlow": "Show",
"hideFlow": "Hide"
},
"help": {
"name": "Help",
@@ -692,6 +738,7 @@
"nodeHelp": "Node Help",
"showHelp": "Show help",
"showInOutline": "Show in outline",
"hideTopics": "Hide topics",
"showTopics": "Show topics",
"noHelp": "No help topic selected",
"changeLog": "Change Log"
@@ -766,6 +813,7 @@
"branches": "Branches",
"noBranches": "No branches",
"deleteConfirm": "Are you sure you want to delete the local branch '__name__'? This cannot be undone.",
"deleteBranch": "Delete branch",
"unmergedConfirm": "The local branch '__name__' has unmerged changes that will be lost. Are you sure you want to delete it?",
"deleteUnmergedBranch": "Delete unmerged branch",
"gitRemotes": "Git remotes",
@@ -887,6 +935,8 @@
}
},
"typedInput": {
"selected": "__count__ selected",
"selected_plural": "__count__ selected",
"type": {
"str": "string",
"num": "number",
@@ -897,7 +947,14 @@
"date": "timestamp",
"jsonata": "expression",
"env": "env variable",
"cred": "credential"
"cred": "credential",
"conf-types": "config node"
},
"date": {
"format": {
"timestamp": "milliseconds since epoch",
"object": "JavaScript Date Object"
}
}
},
"editableList": {
@@ -984,7 +1041,10 @@
"quote": "Quote",
"link": "Link",
"horizontal-rule": "Horizontal rule",
"toggle-preview": "Toggle preview"
"toggle-preview": "Toggle preview",
"mermaid": {
"summary": "Mermaid Diagram"
}
},
"bufferEditor": {
"title": "Buffer editor",
@@ -1177,20 +1237,22 @@
"title": "System Info"
},
"languages": {
"de": "German",
"de": "Deutsch",
"en-US": "English",
"ja": "Japanese",
"es-ES": "Español (España)",
"fr": "Français",
"ja": "日本語",
"ko": "Korean",
"ru": "Russian",
"zh-CN": "Chinese(Simplified)",
"zh-TW": "Chinese(Traditional)"
"pt-BR": "Português (Brasil)",
"ru": "Русский",
"zh-CN": "简体中文",
"zh-TW": "繁體中文"
},
"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",
@@ -1202,9 +1264,15 @@
}
},
"contextMenu": {
"showActionList": "Show action list",
"insert": "Insert",
"node": "Node",
"junction": "Junction",
"linkNodes": "Link Nodes"
},
"env-var": {
"environment": "Environment",
"header": "Global Environment Variables",
"revert": "Revert"
}
}

View File

View File

@@ -1,7 +1,7 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Casts the `arg` parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function. If `prettify` is true, then \"prettified\" JSON is produced. i.e One line per field and lines will be indented based on the field depth."
"desc": "Casts the `arg` parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function. If `prettify` is true, then \"prettified\" JSON is produced. i.e One line per field and lines will be indented based on the field depth."
},
"$length": {
"args": "str",
@@ -53,7 +53,7 @@
},
"$now": {
"args": "$[picture [, timezone]]",
"desc": "Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional picture and timezone parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function"
"desc": "Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional `picture` and `timezone` parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function"
},
"$base64encode": {
"args": "string",
@@ -137,7 +137,7 @@
},
"$sort": {
"args": "array [, function]",
"desc": "Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values left and right. If the value of left should be placed after the value of right in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`."
"desc": "Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values `left` and `right`. If the value of `left` should be placed after the value of `right` in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`."
},
"$reverse": {
"args": "array",
@@ -201,11 +201,11 @@
},
"$fromMillis": {
"args": "number, [, picture [, timezone]]",
"desc": "Convert the `number` representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a formatted string representation of the timestamp as specified by the picture string.\n\nIf the optional `picture` parameter is omitted, then the timestamp is formatted in the ISO 8601 format.\n\nIf the optional `picture` string is supplied, then the timestamp is formatted occording to the representation specified in that string. The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function `format-dateTime` as defined in the XPath F&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the same syntax as `format-dateTime`.\n\nIf the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone. The `timezone` string should be in the format '±HHMM', where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC."
"desc": "Convert the `number` representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a formatted string representation of the timestamp as specified by the picture string.\n\nIf the optional `picture` parameter is omitted, then the timestamp is formatted in the ISO 8601 format.\n\nIf the optional `picture` string is supplied, then the timestamp is formatted according to the representation specified in that string. The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function `format-dateTime` as defined in the XPath F&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the same syntax as `format-dateTime`.\n\nIf the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone. The `timezone` string should be in the format '±HHMM', where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Casts the `number` to a string and formats it to a decimal representation as specified by the `picture` string.\n\n The behaviour of this function is consistent with the XPath/XQuery function fn:format-number as defined in the XPath F&O 3.1 specification. The picture string parameter defines how the number is formatted and has the same syntax as fn:format-number.\n\nThe optional third argument `options` is used to override the default locale specific formatting characters such as the decimal separator. If supplied, this argument must be an object containing name/value pairs specified in the decimal format section of the XPath F&O 3.1 specification."
"desc": "Casts the `number` to a string and formats it to a decimal representation as specified by the `picture` string.\n\n The behaviour of this function is consistent with the XPath/XQuery function `fn:format-number` as defined in the XPath F&O 3.1 specification. The `picture` string parameter defines how the number is formatted and has the same syntax as `fn:format-number`.\n\nThe optional third argument `options` is used to override the default locale specific formatting characters such as the decimal separator. If supplied, this argument must be an object containing name/value pairs specified in the decimal format section of the XPath F&O 3.1 specification."
},
"$formatBase": {
"args": "number [, radix]",
@@ -233,15 +233,15 @@
},
"$error": {
"args": "[str]",
"desc": "Throws an error with a message. The optional `str` will replace the default message of `$error() function evaluated`"
"desc": "Throws an error with a message. The optional `str` will replace the default message of `$error() function evaluated`"
},
"$assert": {
"args": "arg, str",
"desc": "If `arg` is true the function returns undefined. If `arg` is false an exception is thrown with `str` as the message of the exception."
"desc": "If `arg` is `true` the function returns `undefined`. If `arg` is `false` an exception is thrown with `str` as the message of the exception."
},
"$single": {
"args": "array, function",
"desc": "Returns the one and only value in the `array` parameter that satisfies the `function` predicate (i.e. the `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.\n\nThe function should be supplied in the following signature: `function(value [, index [, array]])` where value is each input of the array, index is the position of that value and the whole array is passed as the third argument"
"desc": "Returns the one and only value in the `array` parameter that satisfies the `function` predicate (i.e. the `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.\n\nThe function should be supplied in the following signature: `function(value [, index [, array]])` where value is each input of the array, index is the position of that value and the whole array is passed as the third argument"
},
"$encodeUrlComponent": {
"args": "str",
@@ -249,15 +249,15 @@
},
"$encodeUrl": {
"args": "str",
"desc": "Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character. \n\nExample: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
"desc": "Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.\n\nExample: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Decodes a Uniform Resource Locator (URL) component previously created by encodeUrlComponent. \n\nExample: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
"desc": "Decodes a Uniform Resource Locator (URL) component previously created by encodeUrlComponent.\n\nExample: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Decodes a Uniform Resource Locator (URL) previously created by encodeUrl. \n\nExample: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
"desc": "Decodes a Uniform Resource Locator (URL) previously created by encodeUrl.\n\nExample: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "Gets a date object using the Moment library."
},
"$clone": {
"args": "value",
"desc": "Safely clone an object."
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
{
"info": {
"tip0": "Puedes eliminar los nodos o enlaces seleccionados con {{core:delete-selection}}",
"tip1": "Busca nodos con {{core:search}}",
"tip2": "{{core:toggle-sidebar}} alternará la vista de esta barra lateral",
"tip3": "Puedes gestionar tu paleta de nodos con {{core:manage-palette}}",
"tip4": "Tus nodos de configuración de flujo aparecen en el panel de la barra lateral. Se puede acceder desde el menú o con {{core:show-config-tab}}",
"tip5": "Activa o desactiva estos consejos desde la opción en la configuración",
"tip6": "Mueve los nodos seleccionados usando las teclas [izquierda] [arriba] [abajo] y [derecha]. Mantén pulsada [Mayús] para desplazarlos más",
"tip7": "Arrastrar un nodo a un cable lo insertará en el enlace",
"tip8": "Exporta los nodos seleccionados, o la pestaña actual con {{core:show-export-dialog}}",
"tip9": "Importa un flujo arrastrando su JSON al editor, o con {{core:show-import-dialog}}",
"tip10": "[shift][clic] y arrastrar en un puerto de nodo para mover todos los cables conectados o sólo el seleccionado",
"tip11": "Mostrar la pestaña Información con {{core:show-info-tab}} o la pestaña Depuración con {{core:show-debug-tab}}",
"tip12": "[ctrl] [clic] en el área de trabajo para abrir el diálogo de adición rápida",
"tip13": "Mantén pulsada [ctrl] cuando [haces clic] en un puerto de nodo para habilitar el enlazado rápido",
"tip14": "Mantén pulsada [shift] cuando [haces clic] en un nodo para seleccionar también todos sus nodos conectados",
"tip15": "Mantén pulsada [ctrl] cuando [haces clic] en un nodo para añadirlo o eliminarlo de la selección actual",
"tip16": "Cambia de pestaña de flujo con {{core:show-previous-tab}} y {{core:show-next-tab}}",
"tip17": "Puedes confirmar tus cambios en la bandeja de edición de nodos con {{core:confirm-edit-tray}} o cancelarlos con {{core:cancel-edit-tray}}",
"tip18": "Al pulsar {{core:edit-selected-node}} se editará el primer nodo de la selección actual"
}
}

View File

@@ -0,0 +1,278 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Convierte el parámetro `arg` a una cadena usando las siguientes reglas de conversión:\n\n - Las cadenas no cambian\n - Las funciones se convierten en una cadena vacía\n - El infinito numérico y NaN arrojan un error porque no se pueden representar como un número JSON\n: todos los demás valores se convierten a una cadena JSON usando la función `JSON.stringify`. Si `prettify` es verdadero, entonces se produce JSON \"prettified\". es decir, una línea por campo y las líneas se indentarán según la profundidad del campo."
},
"$length": {
"args": "str",
"desc": "Devuelve el número de caracteres de la cadena `str`. Se genera un error si `str` no es una cadena."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Devuelve una cadena que contiene los caracteres del primer parámetro `str` comenzando en la posición `start` (desplazamiento cero). Si se especifica 'longitud', la subcadena contendrá el máximo de caracteres de 'longitud'. Si 'inicio' es negativo, indica el número de caracteres desde el final de 'cadena'."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Devuelve la subcadena antes de la primera aparición de la secuencia de caracteres `chars` en `str`. Si `str` no contiene `caracteres`, entonces devuelve `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Devuelve la subcadena después de la primera aparición de la secuencia de caracteres `chars` en `str`. Si `str` no contiene `caracteres`, entonces devuelve `str`."
},
"$uppercase": {
"args": "str",
"desc": "Devuelve una cadena con todos los caracteres de `str` convertidos a mayúsculas."
},
"$lowercase": {
"args": "str",
"desc": "Devuelve una cadena con todos los caracteres de `str` convertidos a minúsculas."
},
"$trim": {
"args": "str",
"desc": "Normaliza y recorta todos los caracteres de espacio en blanco en `str` aplicando los siguientes pasos:\n\n - Todas las tabulaciones, retornos de carro y avances de línea se reemplazan con espacios.\n- Las secuencias contiguas de espacios se reducen a un solo espacio.\n- Se eliminan los espacios iniciales y finales.\n\n Si no se especifica `str` (es decir, esta función se invoca sin argumentos), entonces el valor de contexto se utiliza como el valor de `str`. Se genera un error si `str` no es una cadena."
},
"$contains": {
"args": "str, pattern",
"desc": "Devuelve 'verdadero' si 'cadena' coincide con 'patrón', de lo contrario, devuelve 'falso'. Si no se especifica `str` (es decir, esta función se invoca con un argumento), entonces el valor del contexto se utiliza como valor de `str`. El parámetro `patrón` puede ser una cadena o una expresión regular."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Divide el parámetro `str` en una matriz de subcadenas. Es un error si `str` no es una cadena. El parámetro opcional `separador` especifica los caracteres dentro de la `cadena` sobre los cuales se debe dividir como una cadena o una expresión regular. Si no se especifica 'separador', se supone que la cadena está vacía y 'cadena' se dividirá en una matriz de caracteres individuales. Es un error si el 'separador' no es una cadena. El parámetro opcional 'límite' es un número que especifica el número máximo de subcadenas que se incluirán en la matriz resultante. Cualquier subcadena adicional se descarta. Si no se especifica `límite`, entonces `str` se divide completamente sin límite para el tamaño de la matriz resultante. Es un error si 'límite' no es un número positivo."
},
"$join": {
"args": "array[, separator]",
"desc": "Une una matriz de cadenas de componentes en una única cadena concatenada con cada cadena de componentes separada por el parámetro 'separador' opcional. Es un error si la 'matriz' de entrada contiene un elemento que no es una cadena. Si no se especifica 'separador', se supone que es una cadena vacía, es decir, que no hay 'separador' entre las cadenas componentes. Es un error si el 'separador' no es una cadena."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "Aplica la cadena `str` a la expresión regular `pattern` y devuelve una matriz de objetos, cada objeto contiene información sobre cada aparición de una coincidencia dentro de `str`."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "Encuentra apariciones de `patrón` dentro de `str` y las reemplaza con `reemplazo`.\n\nEl parámetro opcional `límite` es el número máximo de reemplazos."
},
"$now": {
"args": "$[picture [, timezone]]",
"desc": "Genera una marca de tiempo en formato compatible con ISO 8601 y la devuelve como una cadena. Si se proporcionan los parámetros opcionales `picture` y `zona horaria`, entonces la marca de tiempo actual se formatea como se describe en la función `$fromMillis()`"
},
"$base64encode": {
"args": "string",
"desc": "Convierte una cadena ASCII a una representación base 64. Cada carácter de la cadena se trata como un byte de datos binarios. Esto requiere que todos los caracteres de la cadena estén en el rango de 0x00 a 0xFF, que incluye todos los caracteres de las cadenas codificadas con URI. No se admiten caracteres Unicode fuera de ese rango."
},
"$base64decode": {
"args": "string",
"desc": "Convierte bytes codificados en base 64 en una cadena, utilizando una página de códigos Unicode UTF-8."
},
"$number": {
"args": "arg",
"desc": "Convierte el parámetro `arg` a un número usando las siguientes reglas de conversión:\n\n - Los números no cambian\n - Las cadenas que contienen una secuencia de caracteres que representan un número JSON legal se convierten a ese número\n - Todos los demás valores provocar que se arroje un error."
},
"$abs": {
"args": "number",
"desc": "Devuelve el valor absoluto del parámetro 'número'."
},
"$floor": {
"args": "number",
"desc": "Devuelve el valor de 'número' redondeado hacia abajo al entero más cercano que sea menor o igual a 'número'."
},
"$ceil": {
"args": "number",
"desc": "Devuelve el valor de 'número' redondeado al número entero más cercano que sea mayor o igual a 'número'."
},
"$round": {
"args": "number [, precision]",
"desc": "Devuelve el valor del parámetro 'número' redondeado al número de decimales especificado por el parámetro opcional 'precisión'."
},
"$power": {
"args": "base, exponent",
"desc": "Devuelve el valor de 'base' elevado a la potencia de 'exponente'."
},
"$sqrt": {
"args": "number",
"desc": "Devuelve la raíz cuadrada del valor del parámetro 'número'."
},
"$random": {
"args": "",
"desc": "Devuelve un número pseudoaleatorio mayor o igual a cero y menor que uno."
},
"$millis": {
"args": "",
"desc": "Devuelve el número de milisegundos desde la época Unix (1 de enero de 1970 UTC) como un número. Todas las invocaciones de `$millis()` dentro de una evaluación de una expresión devolverán el mismo valor."
},
"$sum": {
"args": "array",
"desc": "Devuelve la suma aritmética de una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
},
"$max": {
"args": "array",
"desc": "Devuelve el número máximo en una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
},
"$min": {
"args": "array",
"desc": "Devuelve el número mínimo en una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
},
"$average": {
"args": "array",
"desc": "Devuelve el valor medio de una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
},
"$boolean": {
"args": "arg",
"desc": "Convierte el argumento a un booleano usando las siguientes reglas:\n\n - `Booleano`: sin cambios\n - `cadena`: vacía: `falso`\n - `cadena`: no vacía: `verdadero`\n - `número`: `0`: `falso`\n - `número`: distinto de cero: `verdadero`\n - `nulo`: `falso`\n - `matriz`: vacía: `falso`\n - `array`: contiene un miembro que se convierte en `true`: `true`\n - `array`: todos los miembros se convierten en `false`: `false`\n - `object`: vacío: `false`\n - `objeto`: no vacío: `verdadero`\n - `función`: `falso`"
},
"$not": {
"args": "arg",
"desc": "Devuelve booleano NEGADO del argumento. `arg` se convierte antes en un booleano"
},
"$exists": {
"args": "arg",
"desc": "Devuelve booleano 'verdadero' si la expresión 'arg' se evalúa como un valor, o 'falso' si la expresión no coincide con nada (por ejemplo, una ruta a una referencia de campo inexistente)."
},
"$count": {
"args": "array",
"desc": "Devuelve el número de elementos de la matriz."
},
"$append": {
"args": "array, array",
"desc": "Agrega dos matrices"
},
"$sort": {
"args": "array [, function]",
"desc": "Devuelve una matriz que contiene todos los valores en el parámetro `array`, pero ordenados.\n\nSi se proporciona una `función` de comparador, entonces debe ser una función que toma dos parámetros:\n\n`function(left , derecha)`\n\nEsta función es invocada por el algoritmo de clasificación para comparar dos valores `izquierda` y `derecha`. Si el valor de `izquierda` debe colocarse después del valor de `derecha` en el orden de clasificación deseado, entonces la función debe devolver un valor booleano 'verdadero' para indicar un intercambio. De lo contrario debe devolver 'falso'."
},
"$reverse": {
"args": "array",
"desc": "Devuelve una matriz que contiene todos los valores del parámetro `matriz`, pero en orden inverso."
},
"$shuffle": {
"args": "array",
"desc": "Devuelve una matriz que contiene todos los valores del parámetro `array`, pero mezclados en orden aleatorio."
},
"$zip": {
"args": "array, ...",
"desc": "Devuelve una matriz convolucionada (comprimida) que contiene matrices agrupadas de valores de los argumentos `matriz1`... `matrizN` del índice 0, 1, 2...."
},
"$keys": {
"args": "object",
"desc": "Devuelve una matriz que contiene las claves del objeto. Si el argumento es una matriz de objetos, entonces la matriz devuelta contiene una lista deduplicada de todas las claves de todos los objetos."
},
"$lookup": {
"args": "object, key",
"desc": "Devuelve el valor asociado con la clave en el objeto. Si el primer argumento es una matriz de objetos, entonces se buscan todos los objetos de la matriz y se devuelven los valores asociados con todas las apariciones de la clave."
},
"$spread": {
"args": "object",
"desc": "Divide un objeto que contiene pares clave/valor en una matriz de objetos, cada uno de los cuales tiene un único par clave/valor del objeto de entrada. Si el parámetro es una matriz de objetos, entonces la matriz resultante contiene un objeto para cada par clave/valor en cada objeto de la matriz proporcionada."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Fusiona una matriz de objetos en un único objeto que contiene todos los pares clave/valor de cada uno de los objetos en la matriz de entrada. Si alguno de los objetos de entrada contiene la misma clave, entonces el objeto devuelto contendrá el valor del último en la matriz. Es un error si la matriz de entrada contiene un elemento que no es un objeto."
},
"$sift": {
"args": "object, function",
"desc": "Devuelve un objeto que contiene solo los pares clave/valor del parámetro `objeto` que satisfacen el predicado `función` pasado como segundo parámetro.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función(valor [, clave [, objeto]])`"
},
"$each": {
"args": "object, function",
"desc": "Devuelve una matriz que contiene los valores devueltos por la función cuando se aplica a cada par clave/valor en el objeto."
},
"$map": {
"args": "array, function",
"desc": "Devuelve una matriz que contiene los resultados de aplicar el parámetro `función` a cada valor en el parámetro `matriz`.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función( valor [, índice [, matriz]])`"
},
"$filter": {
"args": "array, function",
"desc": "Devuelve una matriz que contiene solo los valores en el parámetro `matriz` que satisfacen el predicado `función`.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función(valor [ , índice [, matriz]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "Devuelve un valor agregado derivado de aplicar el parámetro `función` sucesivamente a cada valor en `matriz` en combinación con el resultado de la aplicación anterior de la función.\n\nLa función debe aceptar dos argumentos y se comporta como un operador infijo entre cada valor dentro de la matriz. La firma de la `función` debe tener la forma: `myfunc($accumulator, $value[, $index[, $array]])`\n\nEl parámetro opcional `init` se utiliza como valor inicial en la agregación."
},
"$flowContext": {
"args": "string[, string]",
"desc": "Recupera una propiedad de contexto de flujo.\n\nEsta es una función definida por Node-RED."
},
"$globalContext": {
"args": "string[, string]",
"desc": "Recupera una propiedad de contexto global.\n\nEsta es una función definida por Node-RED."
},
"$pad": {
"args": "string, width [, char]",
"desc": "Devuelve una copia de la `cadena` con relleno adicional, si es necesario, de modo que su número total de caracteres sea al menos el valor absoluto del parámetro `ancho`.\n\nSi `ancho` es un número positivo, entonces la cadena está acolchado hacia la derecha; si es negativo, se rellena hacia la izquierda.\n\nEl argumento opcional `char` especifica los caracteres de relleno que se utilizarán. Si no se especifica, el valor predeterminado es el carácter de espacio."
},
"$fromMillis": {
"args": "number, [, picture [, timezone]]",
"desc": "Convierte el `número` que representa milisegundos desde la época Unix (1 de enero de 1970 UTC) en una representación de cadena formateada según la plantilla en picture.\n\nSi se omite el parámetro opcional `picture`, entonces la marca de tiempo es formateado en el formato ISO 8601.\n\nSi se proporciona la cadena opcional `picture`, entonces la marca de tiempo se formatea de acuerdo con la representación especificada en esa cadena. El comportamiento de esta función es consistente con la versión de dos argumentos de la función XPath/XQuery `format-dateTime` tal como se define en la especificación XPath F&O 3.1. El parámetro de plantilla define cómo se formatea la marca de tiempo y tiene la misma sintaxis que `format-dateTime`.\n\nSi se proporciona la cadena opcional `timezone`, entonces la marca de tiempo formateada estará en esa zona horaria. La cadena `timezone` debe tener el formato '±HHMM', donde ± es el signo más o menos y HHMM es el desplazamiento en horas y minutos desde UTC. Desplazamiento positivo para zonas horarias al este de UTC, desplazamiento negativo para zonas horarias al oeste de UTC."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Convierte el `número` en una cadena y lo formatea en una representación decimal según lo especificado en la cadena `picture`.\n\n El comportamiento de esta función es coherente con la función XPath/XQuery `fn:format-number` tal como se define en la especificación XPath F&O 3.1. El parámetro de cadena `picture` define cómo se formatea el número y tiene la misma sintaxis que `fn:formato-número`.\n\nEl tercer argumento opcional `opciones` se utiliza para anular los caracteres de formato específicos de la configuración regional predeterminada, como el decimal. separador. Si se proporciona, este argumento debe ser un objeto que contenga pares de nombre/valor especificados en la sección de formato decimal de la especificación XPath F&O 3.1."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "Convierte el número en una cadena y lo formatea como un número entero representado en la base numérica especificada por el argumento `radix`. Si no se especifica `radix`, el valor predeterminado es la base 10. `radix` puede estar entre 2 y 36; de lo contrario, se genera un error."
},
"$toMillis": {
"args": "timestamp",
"desc": "Convierte una cadena de `marca de tiempo` en el formato ISO 8601 al número de milisegundos desde la época Unix (1 de enero de 1970 UTC) como un número. Se genera un error si la cadena no tiene el formato correcto."
},
"$env": {
"args": "arg",
"desc": "Devuelve el valor de una variable de entorno.\n\nEsta es una función definida por Node-RED."
},
"$eval": {
"args": "expr [, context]",
"desc": "Analiza y evalúa la cadena `expr` que contiene JSON literal o una expresión JSONata utilizando el contexto actual como contexto para la evaluación."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Convierte el número en una cadena y lo formatea en una representación entera como lo especifica la cadena `picture`. El parámetro de define cómo se formatea el número y tiene la misma sintaxis que `fn:format-integer` de la especificación XPath F&O 3.1."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Analiza el contenido del parámetro cadena en un número entero (como un número JSON) utilizando el formato especificado por la cadena `picture`. El parámetro tiene el mismo formato que `$formatInteger`."
},
"$error": {
"args": "[str]",
"desc": "Lanza un error con un mensaje. El parámetro `str` opcional reemplazará el mensaje predeterminado de `$error() función evaluada`"
},
"$assert": {
"args": "arg, str",
"desc": "Si `arg` es `verdadero`, la función devuelve indefinido. Si `arg` es `falso`, se lanza una excepción con `str` como mensaje de excepción."
},
"$single": {
"args": "array, function",
"desc": "Devuelve el único valor en el parámetro `array` que satisface el predicado de `función` (es decir, la `función` devuelve booleano `verdadero` cuando se pasa el valor). Lanza una excepción si el número de valores coincidentes no es exactamente uno.\n\nLa función debe proporcionarse con la siguiente firma: `función(valor [, índice [, matriz]])` donde el valor es cada entrada de la matriz. El índice es la posición de ese valor y toda la matriz se pasa como tercer argumento."
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Codifica un componente de URL reemplazando cada instancia de ciertos caracteres por una, dos, tres o cuatro secuencias de escape que representan la codificación UTF-8 del carácter.\n\nEjemplo: `$encodeUrlComponent(\"?x=prueba\")` => `\"%3Fx%3Dprueba\"`"
},
"$encodeUrl": {
"args": "str",
"desc": "Codifica una URL reemplazando cada instancia de ciertos caracteres por una, dos, tres o cuatro secuencias de escape que representan la codificación UTF-8 del carácter.\n\nEjemplo: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Decodifica un componente de URL creado previamente por encodeUrlComponent.\n\nEjemplo: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Decodifica una URL creado previamente por encodeUrl.\n\nEjemplo: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "Devuelve una matriz con valores duplicados eliminados de `matriz`"
},
"$type": {
"args": "value",
"desc": "Devuelve el tipo de `valor` como una cadena. Si `valor` no está definido, esto devolverá indefinido."
},
"$moment": {
"args": "[str]",
"desc": "Obtiene un objeto de fecha usando la biblioteca Moment."
},
"$clone": {
"args": "value",
"desc": "Clona un objeto de forma segura."
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"info": {
"tip0": "Vous pouvez supprimer les noeuds ou les liens sélectionnés avec {{core:delete-selection}}",
"tip1": "Rechercher des noeuds à l'aide de {{core:search}}",
"tip2": "{{core:toggle-sidebar}} basculera l'affichage de cette barre latérale",
"tip3": "Vous pouvez gérer votre palette de noeuds avec {{core:manage-palette}}",
"tip4": "Vos noeuds de configuration de flux sont répertoriés dans le panneau de la barre latérale. Ils sont accessibles depuis le menu ou avec {{core:show-config-tab}}",
"tip5": "Activer ou désactiver ces conseils à partir de l'option dans les paramètres",
"tip6": "Déplacer les noeuds sélectionnés à l'aide des touches [gauche] [haut] [bas] et [droite]. Maintenir la touche [shift] enfoncée pour les pousser plus loin",
"tip7": "Faire glisser un noeud sur un fil le raccordera au lien",
"tip8": "Exporter les noeuds sélectionnés, ou l'onglet actuel avec {{core:show-export-dialog}}",
"tip9": "Importer un flux en faisant glisser son JSON dans l'éditeur, ou avec {{core:show-import-dialog}}",
"tip10": "[majuscule] [clic] et faites glisser sur un port de noeud pour déplacer tous les fils attachés ou seulement celui sélectionné",
"tip11": "Afficher l'onglet Infos avec {{core:show-info-tab}} ou l'onglet Débogage avec {{core:show-debug-tab}}",
"tip12": "[ctrl] [clic] dans l'espace de travail pour ouvrir la boîte de dialogue d'ajout rapide",
"tip13": "Maintenir la touche [ctrl] enfoncée lorsque vous [cliquez] sur un port de noeud pour activer le câblage rapide",
"tip14": "Maintenir la touche [shift] enfoncée lorsque vous [cliquez] sur un noeud pour sélectionner également tous ses noeuds connectés",
"tip15": "Maintenir la touche [ctrl] enfoncée lorsque vous [cliquez] sur un noeud pour l'ajouter ou le supprimer de la sélection actuelle",
"tip16": "Changer d'onglet de flux avec {{core:show-previous-tab}} et {{core:show-next-tab}}",
"tip17": "Vous pouvez confirmer vos modifications dans le panneau d'édition du noeud avec {{core:confirm-edit-tray}} ou les annuler avec {{core:cancel-edit-tray}}",
"tip18": "Appuyer sur {{core:edit-selected-node}} modifiera le premier noeud de la sélection actuelle"
}
}

View File

@@ -0,0 +1,278 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Convertit le paramètre `arg` en une chaîne de caractères en utilisant les règles de typage suivantes :\n\n - Les chaînes de caractères sont inchangées\n - Les fonctions sont converties en une chaîne vide\n - L'infini numérique et NaN renvoient une erreur car ils ne peuvent pas être représentés comme un Numéro JSON\n - Toutes les autres valeurs sont converties en une chaîne JSON à l'aide de la fonction `JSON.stringify`. Si `prettify` est vrai, alors le JSON \"prettified\" est produit. c'est-à-dire une ligne par champ et les lignes seront en retrait en fonction de la profondeur du champ."
},
"$length": {
"args": "str",
"desc": "Renvoie le nombre de caractères dans la chaîne `str`. Une erreur est renvoyée si `str` n'est pas une chaîne de caractères."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Renvoie une chaîne contenant les caractères du premier paramètre `str` commençant à la position `start` (pas de décalage). Si `length` est spécifié, alors la sous-chaîne contiendra un maximum de caractères `length`. Si `start` est négatif alors il indique le nombre de caractères à partir de la fin de `str`."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Renvoie la sous-chaîne avant la première occurrence de la séquence de caractères `chars` dans `str`. Si `str` ne contient pas `chars`, alors il renvoie `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Renvoie la sous-chaîne après la première occurrence de la séquence de caractères `chars` dans `str`. Si `str` ne contient pas `chars`, alors il renvoie `str`."
},
"$uppercase": {
"args": "str",
"desc": "Renvoie une chaîne avec tous les caractères de `str` convertis en majuscules."
},
"$lowercase": {
"args": "str",
"desc": "Renvoie une chaîne avec tous les caractères de `str` convertis en minuscules."
},
"$trim": {
"args": "str",
"desc": "Normalise et supprime tous les caractères d'espacement dans `str` en appliquant les étapes suivantes :\n\n - Toutes les tabulations, retours à la ligne et sauts de ligne sont remplacés par des espaces.\n- Les séquences contiguës d'espaces sont réduites à un seul espace.\n- Les espaces de fin et de début sont supprimés.\n\n Si `str` n'est pas spécifié (c'est-à-dire que cette fonction est invoquée sans argument), alors la valeur de contexte est utilisée comme valeur de `str`. Une erreur est renvoyée si `str` n'est pas une chaîne."
},
"$contains": {
"args": "str, pattern",
"desc": "Renvoie `true` si `str` correspond à `pattern`, sinon il renvoie `false`. Si `str` n'est pas spécifié (c'est-à-dire que cette fonction est invoquée avec un argument), alors la valeur de contexte est utilisée comme valeur de `str`. Le paramètre `pattern` peut être une chaîne ou une expression régulière."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Divise le paramètre `str` en un tableau de sous-chaînes. C'est une erreur si `str` n'est pas une chaîne. Le paramètre facultatif `separator` spécifie les caractères à l'intérieur de `str` à propos desquels il doit être divisé en chaîne ou en expression régulière. Si `separator` n'est pas spécifié, la chaîne vide est supposée et `str` sera divisé en un tableau de caractères uniques. C'est une erreur si `separator` n'est pas une chaîne. Le paramètre facultatif `limit` est un nombre qui spécifie le nombre maximum de sous-chaînes à inclure dans le tableau résultant. Toutes les sous-chaînes supplémentaires sont ignorées. Si `limit` n'est pas spécifié, alors `str` est entièrement divisé sans limite à la taille du tableau résultant. C'est une erreur si `limit` n'est pas un nombre non négatif."
},
"$join": {
"args": "array[, separator]",
"desc": "Joint un tableau de chaînes de composants en une seule chaîne concaténée, chaque chaîne de composants étant séparée par le paramètre facultatif `separator`. C'est une erreur si l'entrée `array` contient un élément qui n'est pas une chaîne. Si `séparateur` n'est pas spécifié, il est supposé être la chaîne vide, c'est-à-dire qu'il n'y a pas de `séparateur` entre les chaînes de composants. C'est une erreur si `separator` n'est pas une chaîne."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "Applique la chaîne `str` à l'expression régulière `pattern` et renvoie un tableau d'objets, chaque objet contenant des informations sur chaque occurrence d'une correspondance dans `str`."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "Trouve les occurrences de `pattern` dans `str` et les remplace par `replacement`.\n\nLe paramètre facultatif `limit` est le nombre maximum de remplacements."
},
"$now": {
"args": "$[picture [, timezone]]",
"desc": "Génère un horodatage au format compatible ISO 8601 et le renvoie sous forme de chaîne. Si les paramètres optionnels d'image et de fuseau horaire sont fournis, alors l'horodatage actuel est formaté comme décrit par la fonction `$fromMillis()`"
},
"$base64encode": {
"args": "string",
"desc": "Convertit une chaîne ASCII en une représentation en base 64. Chaque caractère de la chaîne est traité comme un octet de données binaires. Cela nécessite que tous les caractères de la chaîne se trouvent dans la plage 0x00 à 0xFF, qui inclut tous les caractères des chaînes encodées en URI. Les caractères Unicode en dehors de cette plage ne sont pas pris en charge."
},
"$base64decode": {
"args": "string",
"desc": "Convertit les octets encodés en base 64 en une chaîne, à l'aide d'une page de codes Unicode UTF-8."
},
"$number": {
"args": "arg",
"desc": "Convertit le paramètre `arg` en un nombre en utilisant les règles de conversion suivantes :\n\n - Les nombres sont inchangés\n - Les chaînes qui contiennent une séquence de caractères représentant un nombre JSON légal sont converties en ce nombre\n - Toutes les autres valeurs provoquer l'envoi d'une erreur."
},
"$abs": {
"args": "number",
"desc": "Renvoie la valeur absolue du paramètre `nombre`."
},
"$floor": {
"args": "number",
"desc": "Renvoie la valeur de `number` arrondie à l'entier le plus proche inférieur ou égal à `number`."
},
"$ceil": {
"args": "number",
"desc": "Renvoie la valeur de `number` arrondie à l'entier le plus proche supérieur ou égal à `number`."
},
"$round": {
"args": "number [, precision]",
"desc": "Renvoie la valeur du paramètre `number` arrondie au nombre de décimales spécifié par le paramètre facultatif `precision`."
},
"$power": {
"args": "base, exponent",
"desc": "Renvoie la valeur de `base` élevée à la puissance de `exponent`."
},
"$sqrt": {
"args": "number",
"desc": "Renvoie la racine carrée de la valeur du paramètre `number`."
},
"$random": {
"args": "",
"desc": "Renvoie un nombre pseudo-aléatoire supérieur ou égal à zéro et inférieur à un."
},
"$millis": {
"args": "",
"desc": "Renvoie le nombre de millisecondes depuis l'époque Unix (1er janvier 1970 UTC) sous forme de nombre. Tous les appels de `$millis()` dans une évaluation d'une expression renverront toutes la même valeur."
},
"$sum": {
"args": "array",
"desc": "Renvoie la somme arithmétique d'un `tableau` de nombres. C'est une erreur si l'entrée `array` contient un élément qui n'est pas un nombre."
},
"$max": {
"args": "array",
"desc": "Renvoie le nombre maximal dans un `tableau` de nombres. C'est une erreur si l'entrée `array` contient un élément qui n'est pas un nombre."
},
"$min": {
"args": "array",
"desc": "Renvoie le nombre minimum dans un `tableau` de nombres. C'est une erreur si l'entrée `array` contient un élément qui n'est pas un nombre."
},
"$average": {
"args": "array",
"desc": "Renvoie la valeur moyenne d'un `tableau` de nombres. C'est une erreur si l'entrée `array` contient un élément qui n'est pas un nombre."
},
"$boolean": {
"args": "arg",
"desc": "Transforme l'argument en booléen en utilisant les règles suivantes :\n\n - `Boolean` : inchangé\n - `string` : vide : `false`\n - `string` : non vide : `true`\n - `number` : `0` : `false`\n - `number` : non nul : `true`\n - `null` : `false`\n - `array` : vide : `false`\n - `array` : contient un membre qui convertit en `true` : `true`\n - `array` : tous les membres sont transformés en `false` : `false`\n - `object` : vide : `false`\n - `object` : non vide : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "Renvoie un booléen résultat de la négation logique de l'argument"
},
"$exists": {
"args": "arg",
"desc": "Renvoie la valeur booléenne `true` si l'expression `arg` est évaluée à une valeur, ou `false` si l'expression ne correspond à rien (par exemple, un chemin vers une référence de champ inexistante)."
},
"$count": {
"args": "array",
"desc": "Renvoie le nombre d'éléments du tableau"
},
"$append": {
"args": "array, array",
"desc": "Combine deux tableaux"
},
"$sort": {
"args": "array [, function]",
"desc": "Renvoie un tableau contenant toutes les valeurs du paramètre `array`, mais triées dans l'ordre.\n\nSi un comparateur `function` est fourni, alors il doit s'agir d'une fonction qui prend deux paramètres :\n\n`function(left , droite)`\n\nCette fonction est invoquée par l'algorithme de tri pour comparer deux valeurs à gauche et à droite. Si la valeur de `left` doit être placée après la valeur de `right` dans l'ordre de tri souhaité, la fonction doit renvoyer un booléen `true` pour indiquer un échange. Sinon, il doit renvoyer `false`."
},
"$reverse": {
"args": "array",
"desc": "Renvoie un tableau contenant toutes les valeurs du paramètre `array`, mais dans l'ordre inverse."
},
"$shuffle": {
"args": "array",
"desc": "Renvoie un tableau contenant toutes les valeurs du paramètre `array`, mais mélangées dans un ordre aléatoire."
},
"$zip": {
"args": "array, ...",
"desc": "Renvoie un tableau convolué (zippé) contenant des tableaux groupés de valeurs des arguments `array1`...`arrayN` d'index 0, 1, 2...."
},
"$keys": {
"args": "object",
"desc": "Renvoie un tableau contenant les clés de l'objet. Si l'argument est un tableau d'objets, le tableau renvoyé contient une liste dédupliquée de toutes les clés de tous les objets."
},
"$lookup": {
"args": "object, key",
"desc": "Renvoie la valeur associée à la clé dans l'objet. Si le premier argument est un tableau d'objets, tous les objets du tableau sont recherchés et les valeurs associées à toutes les occurrences de key sont renvoyées."
},
"$spread": {
"args": "object",
"desc": "Divise un objet contenant des paires clé/valeur en un tableau d'objets, chacun ayant une seule paire clé/valeur de l'objet d'entrée. Si le paramètre est un tableau d'objets, alors le tableau résultant contient un objet pour chaque paire clé/valeur dans chaque objet du tableau fourni."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Fusionne un tableau d'`objets` en un seul `objet` contenant toutes les paires clé/valeur de chacun des objets du tableau d'entrée. Si l'un des objets d'entrée contient la même clé, alors l'`objet` renvoyé contiendra la valeur du dernier dans le tableau. C'est une erreur si le tableau d'entrée contient un élément qui n'est pas un objet."
},
"$sift": {
"args": "object, function",
"desc": "Renvoie un objet qui contient uniquement les paires clé/valeur du paramètre `object` qui satisfont le prédicat `function` transmis comme second paramètre.\n\nLa `function` qui est fournie comme second paramètre doit avoir la signature suivante :\n\n`fonction(valeur [, clé [, objet]])`"
},
"$each": {
"args": "object, function",
"desc": "Renvoie un tableau contenant les valeurs renvoyées par la `fonction` lorsqu'elle est appliquée à chaque paire clé/valeur dans l'`objet`."
},
"$map": {
"args": "array, function",
"desc": "Renvoie un tableau contenant les résultats de l'application du paramètre `function` à chaque valeur du paramètre `array`.\n\nLa `function` fournie comme second paramètre doit avoir la signature suivante :\n\n`function( valeur [, indice [, tableau]])`"
},
"$filter": {
"args": "array, function",
"desc": "Renvoie un tableau contenant uniquement les valeurs du paramètre `array` qui satisfont le prédicat `function`.\n\nLa `function` fournie comme second paramètre doit avoir la signature suivante :\n\n`function(value [ , indice [, tableau]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "Renvoie une valeur agrégée dérivée de l'application successive du paramètre `function` à chaque valeur de `array` en combinaison avec le résultat de l'application précédente de la fonction.\n\nLa fonction doit accepter deux arguments et se comporte comme un opérateur infixe entre chaque valeur dans le `tableau`. La signature de `function` doit être de la forme : `myfunc($accumulator, $value[, $index[, $array]])`\n\nLe paramètre facultatif `init` est utilisé comme valeur initiale dans l'agrégation ."
},
"$flowContext": {
"args": "string[, string]",
"desc": "Récupère une propriété de contexte de flux.\n\nCeci est une fonction définie par Node-RED."
},
"$globalContext": {
"args": "string[, string]",
"desc": "Récupère une propriété de contexte globale.\n\nCeci est une fonction définie par Node-RED."
},
"$pad": {
"args": "string, width [, char]",
"desc": "Renvoie une copie de la `chaîne` avec un rembourrage supplémentaire, si nécessaire, de sorte que son nombre total de caractères corresponde au moins à la valeur absolue du paramètre `width`.\n\nSi `width` est un nombre positif, alors la chaîne est rembourré à droite; s'il est négatif, il est rempli vers la gauche.\n\nL'argument optionnel `char` spécifie le(s) caractère(s) de remplissage à utiliser. S'il n'est pas spécifié, la valeur par défaut est le caractère espace."
},
"$fromMillis": {
"args": "number, [, picture [, timezone]]",
"desc": "Convertisser le « nombre » représentant les millisecondes depuis l'époque Unix (1er janvier 1970 UTC) en une représentation sous forme de chaîne formatée de l'horodatage tel que spécifié par la chaîne d'image.\n\nSi le paramètre facultatif « image » est omis, l'horodatage est formaté au format ISO 8601.\n\nSi la chaîne facultative `image` est fournie, l'horodatage est formaté selon la représentation spécifiée dans cette chaîne. Le comportement de cette fonction est cohérent avec la version à deux arguments de la fonction XPath/XQuery `format-dateTime` telle que définie dans la spécification XPath F&O 3.1. Le paramètre de chaîne d'image définit la façon dont l'horodatage est formaté et a la même syntaxe que `format-dateTime`.\n\nSi la chaîne facultative `timezone` est fournie, alors l'horodatage formaté sera dans ce fuseau horaire. La chaîne `timezone` doit être au format '±HHMM', où ± est le signe plus ou moins et HHMM est le décalage en heures et minutes par rapport à UTC. Décalage positif pour les fuseaux horaires à l'est de UTC, décalage négatif pour les fuseaux horaires à l'ouest de UTC."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Convertit le `number` en une chaîne et le formate en une représentation décimale comme spécifié par la chaîne `picture`.\n\n Le comportement de cette fonction est cohérent avec la fonction XPath/XQuery `fn:format-number` telle que définie dans le Spécification XPath F&O 3.1. Le paramètre de chaîne d'image définit la façon dont le nombre est formaté et a la même syntaxe que `fn:format-number`.\n\nLe troisième argument facultatif `options` est utilisé pour remplacer les caractères de formatage spécifiques aux paramètres régionaux par défaut, tels que le séparateur décimal. S'il est fourni, cet argument doit être un objet contenant des paires nom/valeur spécifiées dans la section de format décimal de la spécification XPath F&O 3.1."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "Convertit le `number` en une chaîne et le formate en un entier représenté dans la base numérique spécifiée par l'argument `radix`. Si `radix` n'est pas spécifié, la valeur par défaut est la base 10. `radix` peut être compris entre 2 et 36, sinon une erreur est renvoyée."
},
"$toMillis": {
"args": "timestamp",
"desc": "Convertit une chaîne `timestamp` au format ISO 8601 en nombre de millisecondes depuis l'époque Unix (1er janvier 1970 UTC) sous forme de nombre. Une erreur est renvoyée si la chaîne n'est pas au format correct."
},
"$env": {
"args": "arg",
"desc": "Renvoie la valeur d'une variable d'environnement.\n\nCeci est une fonction définie par Node-RED."
},
"$eval": {
"args": "expr [, context]",
"desc": "Analyse et évalue la chaîne `expr` qui contient un JSON littéral ou une expression JSONata en utilisant le contexte actuel comme contexte d'évaluation."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Transforme le `nombre` en une chaîne et le formate en une représentation entière comme spécifié par la chaîne `image`. Le paramètre de chaîne d'image définit la façon dont le nombre est formaté et a la même syntaxe que `fn:format-integer` de la spécification XPath F&O 3.1."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Analyse le contenu du paramètre `string` en un entier (comme un nombre JSON) en utilisant le format spécifié par la chaîne `picture`. Le paramètre de chaîne `picture` a le même format que `$formatInteger`."
},
"$error": {
"args": "[str]",
"desc": "Génère une erreur avec un message. Le `str` facultatif remplacera le message par défaut de la fonction `$error() évaluée`"
},
"$assert": {
"args": "arg, str",
"desc": "Si `arg` est vrai, la fonction renvoie undefined. Si `arg` est faux, une exception est lancée avec `str` comme message de l'exception."
},
"$single": {
"args": "array, function",
"desc": "Renvoie la seule et unique valeur du paramètre `array` qui satisfait le prédicat `function` (c'est-à-dire que la `function` renvoie la valeur booléenne `true` lorsqu'elle est transmise à la valeur). Lève une exception si le nombre de valeurs correspondantes n'est pas exactement un.\n\nLa fonction doit être fournie dans la signature suivante : `function(value [, index [, array]])` où value est chaque entrée du tableau, index est la position de cette valeur et le tableau entier est passé comme troisième argument"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Encode un composant URL (Uniform Resource Locator) en remplaçant chaque instance de certains caractères par une, deux, trois ou quatre séquences d'échappement représentant l'encodage UTF-8 du caractère.\n\nExemple : `$encodeUrlComponent(\"?x =test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrl": {
"args": "str",
"desc": "Encode une URL (Uniform Resource Locator) en remplaçant chaque instance de certains caractères par une, deux, trois ou quatre séquences d'échappement représentant l'encodage UTF-8 du caractère.\n\nExemple : `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0% B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Décode un composant URL (Uniform Resource Locator) précédemment créé par encodeUrlComponent.\n\nExemple : `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Décode une URL (Uniform Resource Locator) précédemment créée par encodeUrl.\n\nExemple : `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "Renvoie un tableau avec les valeurs en double supprimées de `array`"
},
"$type": {
"args": "value",
"desc": "Renvoie le type de `value` sous forme de chaîne. Si `value` n'est pas défini, cela renverra `undefined`"
},
"$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

@@ -23,7 +23,12 @@
"position": "配置",
"enable": "有効",
"disable": "無効",
"upload": "アップロード"
"upload": "アップロード",
"lock": "固定",
"unlock": "固定を解除",
"locked": "固定済み",
"unlocked": "固定なし",
"format": "形式"
},
"type": {
"string": "文字列",
@@ -53,8 +58,10 @@
"confirmDelete": "削除の確認",
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
"dropImageHere": "ここに画像ファイルをドロップしてください",
"addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加",
"closeFlow": "フローを閉じる",
"hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
@@ -68,7 +75,13 @@
"enabled": "有効",
"disabled": "無効",
"info": "詳細",
"selectNodes": "ノードをクリックして選択"
"selectNodes": "ノードをクリックして選択",
"enableFlow": "フローを有効化",
"disableFlow": "フローを無効化",
"lockFlow": "フローを固定",
"unlockFlow": "フローの固定を解除",
"moveToStart": "フローを先頭へ移動",
"moveToEnd": "フローを最後へ移動"
},
"menu": {
"label": {
@@ -101,6 +114,7 @@
"displayStatus": "ノードのステータスを表示",
"displayConfig": "設定ノード",
"import": "読み込み",
"importExample": "フロー例を読み込み",
"export": "書き出し",
"search": "ノードを検索",
"searchInput": "ノードを検索",
@@ -109,7 +123,6 @@
"selectionToSubflow": "選択部分をサブフロー化",
"flows": "フロー",
"add": "フローを新規追加",
"rename": "フロー名を変更",
"delete": "フローを削除",
"keyboardShortcuts": "ショートカットキーの説明",
"login": "ログイン",
@@ -117,6 +130,11 @@
"editPalette": "パレットの管理",
"other": "その他",
"showTips": "ヒントを表示",
"showNodeHelp": "ノードのヘルプを表示",
"enableSelectedNodes": "選択したノードを有効化",
"disableSelectedNodes": "選択したノードを無効化",
"showSelectedNodeLabels": "選択したノードのラベル表示",
"hideSelectedNodeLabels": "選択したノードのラベル非表示",
"showWelcomeTours": "新バージョンのガイドツアーを表示",
"help": "Node-REDウェブサイト",
"projects": "プロジェクト",
@@ -264,8 +282,8 @@
"selected": "選択したフロー",
"current": "現在のタブ",
"all": "全てのタブ",
"compact": "インデントのないJSONフォーマット",
"formatted": "インデント付きのJSONフォーマット",
"compact": "インデントなし",
"formatted": "インデント付き",
"copy": "書き出し",
"export": "ライブラリに書き出し",
"exportAs": "書き出し先",
@@ -286,7 +304,8 @@
"missingType": "不正なフロー - __index__ 番目の要素に'type'プロパティがありません"
},
"conflictNotification1": "読み込もうとしているノードのいくつかは、既にワークスペース内に存在しています。",
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。"
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。",
"alreadyExists": "本ノードは既に存在"
},
"copyMessagePath": "パスをコピーしました",
"copyMessageValue": "値をコピーしました",
@@ -354,8 +373,12 @@
"deleted": "削除",
"flowDeleted": "削除されたフロー",
"flowAdded": "追加されたフロー",
"moved": "移動",
"movedTo": "__id__ へ移動",
"movedFrom": "__id__ から移動"
"movedFrom": "__id__ から移動",
"none": "なし",
"position": "位置",
"wires": "ワイヤー"
},
"nodeCount": "__count__ 個のノード",
"nodeCount_plural": "__count__ 個のノード",
@@ -364,9 +387,14 @@
"reviewChanges": "変更を表示",
"noBinaryFileShowed": "バイナリファイルの中身は表示することができません",
"viewCommitDiff": "コミットの内容を表示",
"commit": "コミット",
"compareChanges": "変更を比較",
"saveConflict": "解決して保存",
"conflictHeader": "<span>__unresolved__</span> 個中 <span>__resolved__</span> 個のコンフリクトを解決",
"localChanges": "ローカルの変更",
"remoteChanges": "リモートの変更",
"useLocalChanges": "ローカルの変更を使用",
"useRemoteChanges": "リモートの変更を使用",
"commonVersionError": "共通バージョンは正しいJSON形式ではありません:",
"oldVersionError": "古いバージョンは正しいJSON形式ではありません:",
"newVersionError": "新しいバージョンは正しいJSON形式ではありません:"
@@ -403,6 +431,7 @@
},
"errors": {
"noNodesSelected": "<strong>サブフローを作成できません</strong>: ノードが選択されていません",
"acrossMultipleGroups": "複数のグループをまたがるサブフローは作成できません",
"multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています"
}
},
@@ -491,12 +520,14 @@
"unassigned": "未割当",
"global": "グローバル",
"workspace": "ワークスペース",
"editor": "編集ダイアログ",
"selectAll": "全てのノードを選択",
"selectNone": "選択を外す",
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
"deleteSelected": "選択部分を削除",
"deleteReconnect": "削除と再接続",
"importNode": "フローの読み込み",
"exportNode": "フローの書き出し",
"nudgeNode": "選択したノードを移動(移動量小)",
@@ -531,7 +562,9 @@
"types": {
"local": "ローカル",
"examples": "サンプル"
}
},
"type": "型",
"name": "名前"
},
"palette": {
"noInfo": "情報がありません",
@@ -571,6 +604,7 @@
"editor": {
"title": "パレットの管理",
"palette": "パレット",
"allCatalogs": "全カタログ",
"times": {
"seconds": "数秒前",
"minutes": "数分前",
@@ -592,6 +626,8 @@
},
"nodeCount": "__label__ 個のノード",
"nodeCount_plural": "__label__ 個のノード",
"pluginCount": "__count__ 個のプラグイン",
"pluginCount_plural": "__count__ 個のプラグイン",
"moduleCount": "__count__ 個のモジュール",
"moduleCount_plural": "__count__ 個のモジュール",
"inuse": "使用中",
@@ -610,6 +646,7 @@
"tab-nodes": "現在のノード",
"tab-install": "ノードを追加",
"sort": "並べ替え:",
"sortRelevance": "関連順",
"sortAZ": "辞書順",
"sortRecent": "日付順",
"more": "+ さらに __count__ 個",
@@ -618,6 +655,7 @@
"errors": {
"catalogLoadFailed": "<p>ノードのカタログの読み込みに失敗しました。</p><p>詳細はブラウザのコンソールを確認してください。</p>",
"installFailed": "<p>追加処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"installTimeout": "<p>バックグラウンドでインストールが継続されます。</p><p>完了した時にノードが表示されます。詳細はログを確認してください。</p>",
"removeFailed": "<p>削除処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"updateFailed": "<p>更新処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"enableFailed": "<p>有効化処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
@@ -632,6 +670,9 @@
"body": "<p>__module__ を削除します。</p><p>Node-REDからードを削除します。ードはNode-REDが再起動されるまで、リソースを使い続ける可能性があります。</p>",
"title": "ノードを削除"
},
"removePlugin": {
"body": "<p>プラグイン __module__ を削除しました。ブラウザを再読み込みして残った表示を消してください。</p>"
},
"update": {
"body": "<p>__module__ を更新します。</p><p>更新を完了するには手動でNode-REDを再起動する必要があります。</p>",
"title": "ノードの更新"
@@ -643,7 +684,8 @@
"review": "ノードの情報を参照",
"install": "追加",
"remove": "削除",
"update": "更新"
"update": "更新",
"understood": "了解"
}
}
}
@@ -683,7 +725,11 @@
"empty": "空",
"globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索"
"find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました",
"showFlow": "表示",
"hideFlow": "非表示"
},
"help": {
"name": "ヘルプ",
@@ -692,6 +738,7 @@
"nodeHelp": "ノードヘルプ",
"showHelp": "ヘルプを表示",
"showInOutline": "アウトラインに表示",
"hideTopics": "トピックを非表示",
"showTopics": "トピックを表示",
"noHelp": "ヘルプのトピックが未選択",
"changeLog": "更新履歴"
@@ -766,6 +813,7 @@
"branches": "ブランチ",
"noBranches": "ブランチなし",
"deleteConfirm": "本当にローカルブランチ'__name__'を削除しますか?削除すると元に戻すことはできません。",
"deleteBranch": "ブランチを削除",
"unmergedConfirm": "ローカルブランチ'__name__'にはマージされていない変更があります。この変更は削除されます。本当に削除しますか?",
"deleteUnmergedBranch": "マージされていないブランチを削除",
"gitRemotes": "Gitリモート",
@@ -887,6 +935,8 @@
}
},
"typedInput": {
"selected": "__count__個を選択",
"selected_plural": "__count__個を選択",
"type": {
"str": "文字列",
"num": "数値",
@@ -897,7 +947,14 @@
"date": "日時",
"jsonata": "JSONata式",
"env": "環境変数",
"cred": "認証情報"
"cred": "認証情報",
"conf-types": "設定ノード"
},
"date": {
"format": {
"timestamp": "エポックからの経過ミリ秒",
"object": "JavaScript日付オブジェクト"
}
}
},
"editableList": {
@@ -984,7 +1041,10 @@
"quote": "引用",
"link": "リンク",
"horizontal-rule": "区切り線",
"toggle-preview": "プレビュー表示切替え"
"toggle-preview": "プレビュー表示切替え",
"mermaid": {
"summary": "Mermaid図"
}
},
"bufferEditor": {
"title": "バッファエディタ",
@@ -1176,21 +1236,11 @@
"diagnostics": {
"title": "システム情報"
},
"languages": {
"de": "ドイツ語",
"en-US": "英語",
"ja": "日本語",
"ko": "韓国語",
"ru": "ロシア語",
"zh-CN": "中国語(簡体)",
"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": "入力パターンが不正",
@@ -1202,11 +1252,17 @@
}
},
"contextMenu": {
"showActionList": "動作一覧を表示",
"insert": "挿入",
"node": "ノード",
"junction": "分岐点",
"linkNodes": "Linkード"
},
"env-var": {
"environment": "環境変数",
"header": "グローバル環境変数",
"revert": "破棄"
},
"action-list": {
"toggle-show-tips": "ヒント表示切替",
"show-about": "Node-REDの説明を表示",
@@ -1291,6 +1347,7 @@
"distribute-selection-vertically": "選択を上下に整列",
"wire-series-of-nodes": "ノードを一続きに接続",
"wire-node-to-multiple": "ノードを複数に接続",
"wire-multiple-to-node": "複数からノードへ接続",
"split-wire-with-link-nodes": "ワイヤーをlinkードで分割",
"generate-node-names": "ノード名を生成",
"show-user-settings": "ユーザ設定を表示",
@@ -1350,6 +1407,14 @@
"show-project-settings": "プロジェクト設定を表示",
"show-version-control-tab": "バージョンコントロールタブを表示",
"start-flows": "フローを開始",
"stop-flows": "フローを停止"
"stop-flows": "フローを停止",
"copy-item-url": "要素のURLをコピー",
"copy-item-edit-url": "要素の編集URLをコピー",
"move-flow-to-start": "フローを先頭に移動",
"move-flow-to-end": "フローを末尾に移動",
"show-global-env": "グローバル環境変数を表示",
"lock-flow": "フローを固定",
"unlock-flow": "フローの固定を解除",
"show-node-help": "ノードのヘルプを表示"
}
}

View File

@@ -53,7 +53,7 @@
},
"$now": {
"args": "$[picture [, timezone]]",
"desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。pictureおよびtimezoneパラメータが指定されている場合、現在時刻を`$fromMillis()`関数の説明に従ってフォーマットします。"
"desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。`picture` および `timezone` パラメータが指定されている場合、現在時刻を `$fromMillis()` 関数の説明に従ってフォーマットします。"
},
"$base64encode": {
"args": "string",
@@ -117,11 +117,11 @@
},
"$boolean": {
"args": "arg",
"desc": "以下のルールを用いて、ブーリアン型へ型変換します。:\n\n - `Boolean` : 変換しない\n - `string`: 空 : `false`\n - `string`: 空でない : `true`\n - `number`: `0` : `false`\n - `number`: 0でない : `true`\n - `null` : `false`\n - `array`: 空 : `false`\n - `array`: `true` に型変換された要素を持つ: `true`\n - `array`: 全ての要素が `false` に型変換: `false`\n - `object`: 空 : `false`\n - `object`: 空でない : `true`\n - `function` : `false`"
"desc": "以下のルールを用いて、真偽型へ型変換します。:\n\n - `Boolean` : 変換しない\n - `string`: 空 : `false`\n - `string`: 空でない : `true`\n - `number`: `0` : `false`\n - `number`: 0でない : `true`\n - `null` : `false`\n - `array`: 空 : `false`\n - `array`: `true` に型変換された要素を持つ: `true`\n - `array`: 全ての要素が `false` に型変換: `false`\n - `object`: 空 : `false`\n - `object`: 空でない : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "引数の否定をブーリアン型で返します。 `arg` は最初にブーリアン型に型変換されます。"
"desc": "引数の否定を真偽型で返します。 `arg` は最初に真偽型に型変換されます。"
},
"$exists": {
"args": "arg",
@@ -137,7 +137,7 @@
},
"$sort": {
"args": "array [, function]",
"desc": "配列 `array` 内の値を並び変えた配列を返します。\n\n比較関数 `function` を用いる場合、比較関数は以下のとおり2つの引数を持つ必要があります。\n\n`function(left, right)`\n\n比較関数は、leftrightの2つの値を比較するために、値を並び替える処理で呼び出されます。もし、求められる並び順にてleftの値をrightの値より後ろに置きたい場合は、比較関数は置き換えを表すブーリアン型の `true` を返す必要があります。一方、置き換えが不要の場合は `false` を返す必要があります。"
"desc": "配列 `array` 内の値を並び変えた配列を返します。\n\n比較関数 `function` を用いる場合、比較関数は以下のとおり2つの引数を持つ必要があります。\n\n`function(left, right)`\n\n比較関数は、`left` と `right` の2つの値を比較するために、値を並び替える処理で呼び出されます。もし、求められる並び順にて `left` の値を `right` の値より後ろに置きたい場合は、比較関数は置き換えを表す真偽型の `true` を返す必要があります。一方、置き換えが不要の場合は `false` を返す必要があります。"
},
"$reverse": {
"args": "array",
@@ -205,7 +205,7 @@
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "`number` を文字列へ変換し、文字列 `picture` に指定した数値表現になるよう書式を変更します。\n\nこの関数の動作は、XPath F&O 3.1の仕様に定義されているXPath/XQuery関数のfn:format-numberの動作と同じです。引数の文字列 picture は、fn:format-numberと同じ構文で数値の書式を定義します。\n\n任意の第三引数 `options` は、小数点記号の様な既定のロケール固有の書式設定文字を上書きするために使用します。この引数を指定する場合、XPath F&O 3.1の仕様の数値形式の項に記述されているname/valueペアを含むオブジェクトでなければなりません。"
"desc": "`number` を文字列へ変換し、文字列 `picture` に指定した数値表現になるよう書式を変更します。\n\nこの関数の動作は、XPath F&O 3.1の仕様に定義されているXPath/XQuery関数の `fn:format-number` の動作と同じです。引数の文字列 `picture` は、 `fn:format-number` と同じ構文で数値の書式を定義します。\n\n任意の第三引数 `options` は、小数点記号の様な既定のロケール固有の書式設定文字を上書きするために使用します。この引数を指定する場合、XPath F&O 3.1の仕様の数値形式の項に記述されているname/valueペアを含むオブジェクトでなければなりません。"
},
"$formatBase": {
"args": "number [, radix]",
@@ -237,7 +237,7 @@
},
"$assert": {
"args": "arg, str",
"desc": "`arg`が真の場合、undefinedを返します。偽の場合、`str`をメッセージとする例外を送出します。"
"desc": "`arg`が真の場合、`undefined`を返します。偽の場合、`str`をメッセージとする例外を送出します。"
},
"$single": {
"args": "array, function",
@@ -257,7 +257,7 @@
},
"$decodeUrl": {
"args": "str",
"desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。 \n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
"desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。\n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
@@ -270,5 +270,9 @@
"$moment": {
"args": "[str]",
"desc": "Momentライブラリを使用して日付オブジェクトを取得します。"
},
"$clone": {
"args": "value",
"desc": "オブジェクトを安全に複製します。"
}
}

2007
packages/node_modules/@node-red/editor-client/locales/ko/editor.json vendored Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,23 @@
{
"info": {
"tip0": "{{core:delete-selection}}를 사용하여 선택된 노드나 링크를 삭제할 수 있습니다.",
"tip1": "{{core:search}}를 활용하여 노드를 검색할 수 있습니다.",
"tip2": "{{core:toggle-sidebar}}를 사용하여 사이드바를 표시/비표시 전환 할 수 있습니다.",
"tip3": "{{core:manage-palette}}를 사용하여 노드 팔레트를 관리 할 수 있습니다.",
"tip4": "플로우 안의 설정노드가 사이드바에 표시됩니다. 메뉴 혹은 {{core:show-config-tab}}를 사용하여 엑세스 할 수 있습니다.",
"tip5": "설정에서 이 팁을 활성화/비활성화 할 수 있습니다.",
"tip6": "[left] [up] [down] [right] 키를 사용하여 선택된 노드를 움직일 수 있습니다. [shift]키를 누른 채로 움직이면 이동폭이 늘어납니다.",
"tip7": "노드를 와이어 사이로 드래그 하여 연결할 수도 있습니다.",
"tip8": "{{core:show-export-dialog}}를 사용하여 선택한 노드 또는 현재탭을 내보낼 수 있습니다.",
"tip9": "JSON파일을 에디터로 드래그하거나 {{core:show-import-dialog}}를 사용하여 플로우 가져올 수 있습니다.",
"tip10": "[shift] [click] 하고서 드래그하여 선택한 와이어를 이동할 수 있습니다.",
"tip11": "{{core:show-info-tab}}를 사용하여 정보탭을 표시하거나 {{core:show-debug-tab}}를 사용하여 디버그탭을 표시할 수 있습니다.",
"tip12": "작업공간에서 [ctrl] [click]을 사용하여 빠른추가 대회상자를 열 수 있습니다.",
"tip13": "[ctrl]을 누른 상태로 노드의 포트를 클릭하여 빠르게 연결할 수 있습니다.",
"tip14": "[shift]를 누른 상태로 노드를 클릭하여 연결된 모든 노드를 선택할 수 있습니다.",
"tip15": "[ctrl]을 누른 상태로 노드를 클릭하여 현재 선택영역에 노드를 추가/제거 할 수 있습니다.",
"tip16": "{{core:show-previous-tab}}와 {{core:show-next-tab}}를 사용하여 탭을 전환할 수 있습니다.",
"tip17": "노드 편집 창에서 {{core : confirm-edit-tray}}로 변경 사항을 확인하거나 {{core : cancel-edit-tray}}로 취소 할 수 있습니다.",
"tip18": "{{core : edit-selected-node}}를 누르면 현재 선택 영역의 첫 번째 노드가 편집됩니다."
}
}
{
"info": {
"tip0": "{{core:delete-selection}}를 사용하여 선택된 노드나 링크를 삭제할 수 있습니다.",
"tip1": "{{core:search}}를 활용하여 노드를 검색할 수 있습니다.",
"tip2": "{{core:toggle-sidebar}}를 사용하여 사이드바를 표시/비표시 전환 할 수 있습니다.",
"tip3": "{{core:manage-palette}}를 사용하여 노드 팔레트를 관리 할 수 있습니다.",
"tip4": "플로우 안의 설정노드가 사이드바에 표시됩니다. 메뉴 혹은 {{core:show-config-tab}}를 사용하여 엑세스 할 수 있습니다.",
"tip5": "설정에서 이 팁을 활성화/비활성화 할 수 있습니다.",
"tip6": "[left] [up] [down] [right] 키를 사용하여 선택된 노드를 움직일 수 있습니다. [shift]키를 누른 채로 움직이면 이동폭이 늘어납니다.",
"tip7": "노드를 와이어 사이로 드래그 하여 연결할 수도 있습니다.",
"tip8": "{{core:show-export-dialog}}를 사용하여 선택한 노드 또는 현재탭을 내보낼 수 있습니다.",
"tip9": "JSON파일을 에디터로 드래그하거나 {{core:show-import-dialog}}를 사용하여 플로우 가져올 수 있습니다.",
"tip10": "[shift] [click] 하고서 드래그하여 선택한 와이어를 이동할 수 있습니다.",
"tip11": "{{core:show-info-tab}}를 사용하여 정보탭을 표시하거나 {{core:show-debug-tab}}를 사용하여 디버그탭을 표시할 수 있습니다.",
"tip12": "작업공간에서 [ctrl] [click]을 사용하여 빠른추가 대회상자를 열 수 있습니다.",
"tip13": "[ctrl]을 누른 상태로 노드의 포트를 클릭하여 빠르게 연결할 수 있습니다.",
"tip14": "[shift]를 누른 상태로 노드를 클릭하여 연결된 모든 노드를 선택할 수 있습니다.",
"tip15": "[ctrl]을 누른 상태로 노드를 클릭하여 현재 선택영역에 노드를 추가/제거 할 수 있습니다.",
"tip16": "{{core:show-previous-tab}}와 {{core:show-next-tab}}를 사용하여 탭을 전환할 수 있습니다.",
"tip17": "노드 편집 창에서 {{core : confirm-edit-tray}}로 변경 사항을 확인하거나 {{core : cancel-edit-tray}}로 취소 할 수 있습니다.",
"tip18": "{{core : edit-selected-node}}를 누르면 현재 선택 영역의 첫 번째 노드가 편집됩니다."
}
}

444
packages/node_modules/@node-red/editor-client/locales/ko/jsonata.json vendored Executable file → Normal file
View File

@@ -1,222 +1,222 @@
{
"$string": {
"args": "arg",
"desc": "다음과 같은 규칙을 사용하여 인수 *arg*를 문자열로 변환합니다. \n\n - 문자열은 변경되지 않습니다. \n - 함수는 빈 문자열로 변환됩니다. \n - 무한대와 NaN은 JSON수치로 표현할 수 없기 때문에 오류처리 됩니다. \n - 다른 모든 값은 `JSON.stringify` 함수를 사용하여 JSON 문자열로 변환됩니다."
},
"$length": {
"args": "str",
"desc": "문자열 `str`의 문자 수를 반환합니다. `str`가 문자열이 아닌 경우 에러를 반환합니다."
},
"$substring": {
"args": "str, start[, length]",
"desc": "(zero-offset)의 `start`에서 시작하는 첫번째 인수 `str`의 문자열을 반환합니다. 만약 `length`가 지정된 경우, 부분 문자열은 최대 `length`의 크기를 갖습니다. 만약 `start` 인수가 음수이면 `str`의 끝에서부터의 문자수를 나타냅니다."
},
"$substringBefore": {
"args": "str, chars",
"desc": "`str`에 `chars`문자가 처음으로 나오기 전까지의 부분문자열을 반환합니다. 만약 `chars`가 없으면 `str`을 반환합니다."
},
"$substringAfter": {
"args": "str, chars",
"desc": "`str`에 `chars`문자가 처음으로 나온 이후의 부분문자열을 반환합니다. 만약 `chars`가 없으면 `str`을 반환합니다."
},
"$uppercase": {
"args": "str",
"desc": "`str`의 문자를 대문자로 반환합니다."
},
"$lowercase": {
"args": "str",
"desc": "`str`의 문자를 소문자로 반환합니다."
},
"$trim": {
"args": "str",
"desc": "다음의 순서대로 `str`의 모든 공백을 자르고 정규화 합니다:\n\n - 모든 탭, 캐리지 리턴 및 줄 바꿈은 공백으로 대체됩니다. \n- 연속된 공백은 하나로 줄입니다.\n- 후행 및 선행 공백은 삭제됩니다.\n\n 만일 `str`이 지정되지 않으면 (예: 이 함수를 인수없이 호출), context값을 `str`의 값으로 사용합니다. `str`이 문자열이 아니면 에러가 발생합니다."
},
"$contains": {
"args": "str, pattern",
"desc": "`str`이 `pattern`과 일치하면 `true`를, 일치하지 않으면 `false`를 반환합니다. 만약 `str`이 지정되지 않으면 (예: 이 함수를 인수없이 호출), context값을 `str`의 값으로 사용합니다. `pattern` 인수는 문자열이나 정규표현으로 할 수 있습니다."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "`str`인수를 분할하여 부분문자열로 배열합니다. `str`이 문자열이 아니면 에러가 발생합니다. 생략가능한 인수 `separator`는 `str`을 분할하는 문자를 문자열 또는 정규표현으로 지정합니다. `separator`를 지정하지 않은 경우, 공백의 문자열로 간주하여 `str`은 단일 문자의 배열로 분리됩니다. `separator`가 문자열이 아니면 에러가 발생합니다. 생략가능한 인수 'limit`는 결과의 배열이 갖는 부분문자열의 최대수를 지정합니다. 이 수를 넘는 부분문자열은 파기됩니다. `limit`가 지정되지 않으면`str`은 결과 배열의 크기의 제한없이 완전히 분리됩니다. `limit`이 음수인 경우 에러가 발생합니다."
},
"$join": {
"args": "array[, separator]",
"desc": "문자열의 배열을 생략가능한 인수 `separator`로 구분한 하나의 문자열로 연결합니다. 배열 `array`가 문자열이 아닌 요소를 포함하는 경우, 에러가 발생합니다. `separator`를 지정하지 않은 경우, 공백의 문자열로 간주합니다(예: 문자열간의 `separator`없음). `separator`가 문자열이 아닌 경우, 에러가 발생합니다."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "`str`문자열에 `pattern`를 적용하여, 오브젝트 배열을 반환합니다. 배열요소의 오브젝트는 `str`중 일치하는 부분의 정보를 보유합니다."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "`str`문자열에서 `pattern` 패턴을 검색하여, `replacement`로 대체합니다.\n\n임의이ㅡ 인수 `limit`는 대체 횟수의 상한값을 지정합니다."
},
"$now": {
"args": "",
"desc": "ISO 8601 호환 형식으로 타임 스탬프를 생성하고 이를 문자열로 반환합니다."
},
"$base64encode": {
"args": "string",
"desc": "ASCII 문자열을 base 64 표현으로 변환합니다. 문자열의 각 문자는 이진 데이터의 바이트로 처리됩니다. 이렇게 하려면 문자열의 모든 문자가 URI로 인코딩 된 문자열을 포함하고, 0x00에서 0xFF 범위에 있어야합니다. 해당 범위를 벗어난 유니 코드 문자는 지원되지 않습니다"
},
"$base64decode": {
"args": "string",
"desc": "UTF-8코드페이지를 이용하여, Base 64형식의 바이트값을 문자열로 변환합니다."
},
"$number": {
"args": "arg",
"desc": "`arg`를 다음과 같은 규칙을 사요하여 숫자로 변환합니다. :\n\n - 숫자는 변경되지 않습니다.\n 올바른 JSON의 숫자는 숫자 그대로 변환됩니다.\n 그 외의 형식은 에러를 발생합니다."
},
"$abs": {
"args": "number",
"desc": "`number`의 절대값을 반환합니다."
},
"$floor": {
"args": "number",
"desc": "`number`를 `number`보다 같거나 작은 정수로 내림하여 반환합니다."
},
"$ceil": {
"args": "number",
"desc": "`number`를 `number`와 같거나 큰 정수로 올림하여 반환합니다."
},
"$round": {
"args": "number [, precision]",
"desc": "인수 `number`를 반올림한 값을 반환합니다. 임의의 인수 `precision`에는 반올립에서 사용할 소수점이하의 자릿수를 지정합니다."
},
"$power": {
"args": "base, exponent",
"desc": "기수 `base`의 값을 지수 `exponent`만큼의 거듭 제곱으로 반환합니다."
},
"$sqrt": {
"args": "number",
"desc": "인수 `number`의 제곱근을 반환합니다."
},
"$random": {
"args": "",
"desc": "0이상 1미만의 의사난수를 반환합니다."
},
"$millis": {
"args": "",
"desc": "Unix Epoch (1970 년 1 월 1 일 UTC)부터 경과된 밀리 초 수를 숫자로 반환합니다. 평가대상식에 포함되는 $millis()의 모든 호출은 모두 같은 값을 반환합니다."
},
"$sum": {
"args": "array",
"desc": "숫자 배열 `array`의 합계를 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$max": {
"args": "array",
"desc": "숫자 배열 `array`에서 최대값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$min": {
"args": "array",
"desc": "숫자 배열 `array`에서 최소값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$average": {
"args": "array",
"desc": "숫자 배열 `array`에서 평균값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$boolean": {
"args": "arg",
"desc": "`arg` 값을 다음의 규칙에 의해 Boolean으로 변환합니다::\n\n - `Boolean` : 변환하지 않음\n - `string`: 비어있음 : `false`\n - `string`: 비어있지 않음 : `true`\n - `number`: `0` : `false`\n - `number`: 0이 아님 : `true`\n - `null` : `false`\n - `array`: 비어있음 : `false`\n - `array`: `true`로 변환된 요소를 가짐 : `true`\n - `array`: 모든 요소가 `false`로 변환 : `false`\n - `object`: 비어있음 : `false`\n - `object`: 비어있지 않음 : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "인수의 부정을 Boolean으로 변환합니다. `arg`는 가장먼저boolean으로 변환됩니다."
},
"$exists": {
"args": "arg",
"desc": "`arg` 식의 평가값이 존재하는 경우 `true`, 식의 평가결과가 미정의인 경우 (예: 존재하지 않는 참조필드로의 경로)는 `false`를 반환합니다."
},
"$count": {
"args": "array",
"desc": "`array`의 요소 갯수를 반환합니다."
},
"$append": {
"args": "array, array",
"desc": "두개의 `array`를 병합합니다."
},
"$sort": {
"args": "array [, function]",
"desc": "배열 `array`의 모든 값을 순서대로 정렬하여 반환합니다. \n\n 비교함수 `function`을 이용하는 경우, 비교함수는 아래와 같은 두개의 인수를 가져야 합니다. \n\n `function(left,right)` \n\n 비교함수는 left와 right의 두개의 값을 비교하기에, 값을 정렬하는 처리에서 호출됩니다. 만약 요구되는 정렬에서 left값을 right값보다 뒤로 두고싶은 경우에는, 비교함수는 치환을 나타내는 Boolean형의 ``true`를, 그렇지 않은 경우에는 `false`를 반환해야 합니다."
},
"$reverse": {
"args": "array",
"desc": "`array`에 포함된 모든 값의 순서를 역순으로 변환하여 반환합니다."
},
"$shuffle": {
"args": "array",
"desc": "`array`에 포함된 모든 값의 순서를 랜덤으로 반환합니다."
},
"$zip": {
"args": "array, ...",
"desc": "배열 `array1` ... arrayN`의 위치 0, 1, 2…. 의 값으로 구성된 convolved (zipped) 배열을 반환합니다."
},
"$keys": {
"args": "object",
"desc": "`object` 키를 포함하는 배열을 반환합니다. 인수가 오브젝트의 배열이면 반환되는 배열은 모든 오브젝트에있는 모든 키의 중복되지 않은 목록이 됩니다."
},
"$lookup": {
"args": "object, key",
"desc": "`object` 내의 `key`가 갖는 값을 반환합니다. 최초의 인수가 객체의 배열 인 경우, 배열 내의 모든 오브젝트를 검색하여, 존재하는 모든 키가 갖는 값을 반환합니다."
},
"$spread": {
"args": "object",
"desc": "`object`의 키/값 쌍별로 각 요소가 하나인 오브젝트 배열로 분할합니다. 만일 오브젝트 배열인 경우, 배열의 결과는 각 오브젝트에서 얻은 키/값 쌍의 오브젝트를 갖습니다."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "`object`배열을 하나의 `object`로 병합합니다. 병합결과의 오브젝트는 입력배열내의 각 오브젝트의 키/값 쌍을 포함합니다. 입력 오브젝트가 같은 키를 가질경우, 반환 된 `object`에는 배열 마지막의 오브젝트의 키/값이 격납됩니다. 입력 배열이 오브젝트가 아닌 요소를 포함하는 경우, 에러가 발생합니다."
},
"$sift": {
"args": "object, function",
"desc": "함수 `function`을 충족시키는 `object` 인수 키/값 쌍만 포함하는 오브젝트를 반환합니다. \n\n 함수 `function` 다음과 같은 인수를 가져야 합니다 : \n\n `function(value [, key [, object]])`"
},
"$each": {
"args": "object, function",
"desc": "`object`의 각 키/값 쌍에, 함수`function`을 적용한 값의 배열을 반환합니다."
},
"$map": {
"args": "array, function",
"desc": "`array`의 각 값에 `function`을 적용한 결과로 이루어진 배열을 반환합니다. \n\n 함수 `function`은 다음과 같은 인수를 가져야 합니다. \n\n `function(value[, index[, array]])`"
},
"$filter": {
"args": "array, function",
"desc": "`array`의 값중, 함수 `function`의 조건을 만족하는 값으로 이루어진 배열을 반환합니다. \n\n 함수 `function`은 다음과 같은 형식을 가져야 합니다. \n\n `function(value[, index[, array]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "배열의 각 요소값에 함수 `function`을 연속적으로 적용하여 얻어지는 집계값을 반환합니다. `function`의 적용에는 직전의 `function`의 적용결과와 요소값이 인수로 주어집니다. \n\n 함수 `function`은 인수를 두개 뽑아, 배열의 각 요소 사이에 배치하는 중치연산자처럼 작용해야 합니다. \n\n 임의의 인수 `init`에는 집약시의 초기값을 설정합니다."
},
"$flowContext": {
"args": "string[, string]",
"desc": "플로우 컨텍스트 속성을 취득합니다."
},
"$globalContext": {
"args": "string[, string]",
"desc": "플로우의 글로벌 컨텍스트 속성을 취득합니다."
},
"$pad": {
"args": "string, width [, char]",
"desc": "문자수가 인수 `width`의 절대값이상이 되도록, 필요한 경우 여분의 패딩을 사용하여 `string`의 복사본을 반환합니다. \n\n `width`가 양수인 경우, 오른쪽으로 채워지고, 음수이면 왼쪽으로 채워집니다. \n\n 임의의 `char`인수에는 이 함수에서 사용할 패딩을 지정합니다. 지정하지 않는 경우에는, 기본값으로 공백을 사용합니다."
},
"$fromMillis": {
"args": "number",
"desc": "Unix Epoch (1970 년 1 월 1 일 UTC) 이후의 밀리 초를 나타내는 숫자를 ISO 8601 형식의 타임 스탬프 문자열로 변환합니다."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "`number`를 문자열로 변환하고 `picture` 문자열에 지정된 표현으로 서식을 변경합니다. \n\n 이 함수의 동작은 XPath F&O 3.1사양에 정의된 XPath/XQuery함수의 fn:format-number의 동작과 같습니다. 인수의 문자열 picture은 fn:format-number 과 같은 구문으로 수치의 서식을 정의합니다. \n\n 임의의 제3 인수 `option`은 소수점기호와 같은 기본 로케일 고유의 서식설정문자를 덮어쓰는데에 사용됩니다. 이 인수를 지정할 경우, XPath F&O 3.1사양의 수치형식에 기술되어있는 name/value 쌍을 포함하는 오브젝트여야 합니다."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "`number`를 인수 `radix`에 지정한 값을 기수로하는 문자열로 변환합니다. `radix`가 지정되지 않은 경우, 기수 10이 기본값으로 설정됩니다. `radix`에는 2~36의 값을 설정할 수 있고, 그 외의 값의 경우에는 에러가 발생합니다."
},
"$toMillis": {
"args": "timestamp",
"desc": "ISO 8601 형식의 `timestamp`를 Unix Epoch (1970 년 1 월 1 일 UTC) 이후의 밀리 초 수로 변환합니다. 문자열이 올바른 형식이 아닌 경우 에러가 발생합니다."
},
"$env": {
"args": "arg",
"desc": "환경변수를 값으로 반환합니다.\n\n 이 함수는 Node-RED 정의 함수입니다."
}
}
{
"$string": {
"args": "arg",
"desc": "다음과 같은 규칙을 사용하여 인수 *arg*를 문자열로 변환합니다.\n\n - 문자열은 변경되지 않습니다.\n - 함수는 빈 문자열로 변환됩니다.\n - 무한대와 NaN은 JSON수치로 표현할 수 없기 때문에 오류처리 됩니다.\n - 다른 모든 값은 `JSON.stringify` 함수를 사용하여 JSON 문자열로 변환됩니다."
},
"$length": {
"args": "str",
"desc": "문자열 `str`의 문자 수를 반환합니다. `str`가 문자열이 아닌 경우 에러를 반환합니다."
},
"$substring": {
"args": "str, start[, length]",
"desc": "(zero-offset)의 `start`에서 시작하는 첫번째 인수 `str`의 문자열을 반환합니다. 만약 `length`가 지정된 경우, 부분 문자열은 최대 `length`의 크기를 갖습니다. 만약 `start` 인수가 음수이면 `str`의 끝에서부터의 문자수를 나타냅니다."
},
"$substringBefore": {
"args": "str, chars",
"desc": "`str`에 `chars`문자가 처음으로 나오기 전까지의 부분문자열을 반환합니다. 만약 `chars`가 없으면 `str`을 반환합니다."
},
"$substringAfter": {
"args": "str, chars",
"desc": "`str`에 `chars`문자가 처음으로 나온 이후의 부분문자열을 반환합니다. 만약 `chars`가 없으면 `str`을 반환합니다."
},
"$uppercase": {
"args": "str",
"desc": "`str`의 문자를 대문자로 반환합니다."
},
"$lowercase": {
"args": "str",
"desc": "`str`의 문자를 소문자로 반환합니다."
},
"$trim": {
"args": "str",
"desc": "다음의 순서대로 `str`의 모든 공백을 자르고 정규화 합니다:\n\n - 모든 탭, 캐리지 리턴 및 줄 바꿈은 공백으로 대체됩니다.\n- 연속된 공백은 하나로 줄입니다.\n- 후행 및 선행 공백은 삭제됩니다.\n\n 만일 `str`이 지정되지 않으면 (예: 이 함수를 인수없이 호출), context값을 `str`의 값으로 사용합니다. `str`이 문자열이 아니면 에러가 발생합니다."
},
"$contains": {
"args": "str, pattern",
"desc": "`str`이 `pattern`과 일치하면 `true`를, 일치하지 않으면 `false`를 반환합니다. 만약 `str`이 지정되지 않으면 (예: 이 함수를 인수없이 호출), context값을 `str`의 값으로 사용합니다. `pattern` 인수는 문자열이나 정규표현으로 할 수 있습니다."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "`str`인수를 분할하여 부분문자열로 배열합니다. `str`이 문자열이 아니면 에러가 발생합니다. 생략가능한 인수 `separator`는 `str`을 분할하는 문자를 문자열 또는 정규표현으로 지정합니다. `separator`를 지정하지 않은 경우, 공백의 문자열로 간주하여 `str`은 단일 문자의 배열로 분리됩니다. `separator`가 문자열이 아니면 에러가 발생합니다. 생략가능한 인수 'limit`는 결과의 배열이 갖는 부분문자열의 최대수를 지정합니다. 이 수를 넘는 부분문자열은 파기됩니다. `limit`가 지정되지 않으면`str`은 결과 배열의 크기의 제한없이 완전히 분리됩니다. `limit`이 음수인 경우 에러가 발생합니다."
},
"$join": {
"args": "array[, separator]",
"desc": "문자열의 배열을 생략가능한 인수 `separator`로 구분한 하나의 문자열로 연결합니다. 배열 `array`가 문자열이 아닌 요소를 포함하는 경우, 에러가 발생합니다. `separator`를 지정하지 않은 경우, 공백의 문자열로 간주합니다(예: 문자열간의 `separator`없음). `separator`가 문자열이 아닌 경우, 에러가 발생합니다."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "`str`문자열에 `pattern`를 적용하여, 오브젝트 배열을 반환합니다. 배열요소의 오브젝트는 `str`중 일치하는 부분의 정보를 보유합니다."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "`str`문자열에서 `pattern` 패턴을 검색하여, `replacement`로 대체합니다.\n\n임의이ㅡ 인수 `limit`는 대체 횟수의 상한값을 지정합니다."
},
"$now": {
"args": "",
"desc": "ISO 8601 호환 형식으로 타임 스탬프를 생성하고 이를 문자열로 반환합니다."
},
"$base64encode": {
"args": "string",
"desc": "ASCII 문자열을 base 64 표현으로 변환합니다. 문자열의 각 문자는 이진 데이터의 바이트로 처리됩니다. 이렇게 하려면 문자열의 모든 문자가 URI로 인코딩 된 문자열을 포함하고, 0x00에서 0xFF 범위에 있어야합니다. 해당 범위를 벗어난 유니 코드 문자는 지원되지 않습니다"
},
"$base64decode": {
"args": "string",
"desc": "UTF-8코드페이지를 이용하여, Base 64형식의 바이트값을 문자열로 변환합니다."
},
"$number": {
"args": "arg",
"desc": "`arg`를 다음과 같은 규칙을 사요하여 숫자로 변환합니다. :\n\n - 숫자는 변경되지 않습니다.\n 올바른 JSON의 숫자는 숫자 그대로 변환됩니다.\n 그 외의 형식은 에러를 발생합니다."
},
"$abs": {
"args": "number",
"desc": "`number`의 절대값을 반환합니다."
},
"$floor": {
"args": "number",
"desc": "`number`를 `number`보다 같거나 작은 정수로 내림하여 반환합니다."
},
"$ceil": {
"args": "number",
"desc": "`number`를 `number`와 같거나 큰 정수로 올림하여 반환합니다."
},
"$round": {
"args": "number [, precision]",
"desc": "인수 `number`를 반올림한 값을 반환합니다. 임의의 인수 `precision`에는 반올립에서 사용할 소수점이하의 자릿수를 지정합니다."
},
"$power": {
"args": "base, exponent",
"desc": "기수 `base`의 값을 지수 `exponent`만큼의 거듭 제곱으로 반환합니다."
},
"$sqrt": {
"args": "number",
"desc": "인수 `number`의 제곱근을 반환합니다."
},
"$random": {
"args": "",
"desc": "0이상 1미만의 의사난수를 반환합니다."
},
"$millis": {
"args": "",
"desc": "Unix Epoch (1970 년 1 월 1 일 UTC)부터 경과된 밀리 초 수를 숫자로 반환합니다. 평가대상식에 포함되는 $millis()의 모든 호출은 모두 같은 값을 반환합니다."
},
"$sum": {
"args": "array",
"desc": "숫자 배열 `array`의 합계를 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$max": {
"args": "array",
"desc": "숫자 배열 `array`에서 최대값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$min": {
"args": "array",
"desc": "숫자 배열 `array`에서 최소값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$average": {
"args": "array",
"desc": "숫자 배열 `array`에서 평균값을 반환합니다. `array`에 숫자가 아닌 요소가 있는 경우, 에러가 발생합니다."
},
"$boolean": {
"args": "arg",
"desc": "`arg` 값을 다음의 규칙에 의해 Boolean으로 변환합니다::\n\n - `Boolean` : 변환하지 않음\n - `string`: 비어있음 : `false`\n - `string`: 비어있지 않음 : `true`\n - `number`: `0` : `false`\n - `number`: 0이 아님 : `true`\n - `null` : `false`\n - `array`: 비어있음 : `false`\n - `array`: `true`로 변환된 요소를 가짐 : `true`\n - `array`: 모든 요소가 `false`로 변환 : `false`\n - `object`: 비어있음 : `false`\n - `object`: 비어있지 않음 : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "인수의 부정을 Boolean으로 변환합니다. `arg`는 가장먼저boolean으로 변환됩니다."
},
"$exists": {
"args": "arg",
"desc": "`arg` 식의 평가값이 존재하는 경우 `true`, 식의 평가결과가 미정의인 경우 (예: 존재하지 않는 참조필드로의 경로)는 `false`를 반환합니다."
},
"$count": {
"args": "array",
"desc": "`array`의 요소 갯수를 반환합니다."
},
"$append": {
"args": "array, array",
"desc": "두개의 `array`를 병합합니다."
},
"$sort": {
"args": "array [, function]",
"desc": "배열 `array`의 모든 값을 순서대로 정렬하여 반환합니다.\n\n 비교함수 `function`을 이용하는 경우, 비교함수는 아래와 같은 두개의 인수를 가져야 합니다.\n\n `function(left,right)`\n\n 비교함수는 `left``right`의 두개의 값을 비교하기에, 값을 정렬하는 처리에서 호출됩니다. 만약 요구되는 정렬에서 left값을 `right`값보다 뒤로 두고싶은 경우에는, 비교함수는 치환을 나타내는 Boolean형의 `true`를, 그렇지 않은 경우에는 `false`를 반환해야 합니다."
},
"$reverse": {
"args": "array",
"desc": "`array`에 포함된 모든 값의 순서를 역순으로 변환하여 반환합니다."
},
"$shuffle": {
"args": "array",
"desc": "`array`에 포함된 모든 값의 순서를 랜덤으로 반환합니다."
},
"$zip": {
"args": "array, ...",
"desc": "배열 `array1` ... arrayN`의 위치 0, 1, 2…. 의 값으로 구성된 convolved (zipped) 배열을 반환합니다."
},
"$keys": {
"args": "object",
"desc": "`object` 키를 포함하는 배열을 반환합니다. 인수가 오브젝트의 배열이면 반환되는 배열은 모든 오브젝트에있는 모든 키의 중복되지 않은 목록이 됩니다."
},
"$lookup": {
"args": "object, key",
"desc": "`object` 내의 `key`가 갖는 값을 반환합니다. 최초의 인수가 객체의 배열 인 경우, 배열 내의 모든 오브젝트를 검색하여, 존재하는 모든 키가 갖는 값을 반환합니다."
},
"$spread": {
"args": "object",
"desc": "`object`의 키/값 쌍별로 각 요소가 하나인 오브젝트 배열로 분할합니다. 만일 오브젝트 배열인 경우, 배열의 결과는 각 오브젝트에서 얻은 키/값 쌍의 오브젝트를 갖습니다."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "`object`배열을 하나의 `object`로 병합합니다. 병합결과의 오브젝트는 입력배열내의 각 오브젝트의 키/값 쌍을 포함합니다. 입력 오브젝트가 같은 키를 가질경우, 반환 된 `object`에는 배열 마지막의 오브젝트의 키/값이 격납됩니다. 입력 배열이 오브젝트가 아닌 요소를 포함하는 경우, 에러가 발생합니다."
},
"$sift": {
"args": "object, function",
"desc": "함수 `function`을 충족시키는 `object` 인수 키/값 쌍만 포함하는 오브젝트를 반환합니다.\n\n 함수 `function` 다음과 같은 인수를 가져야 합니다 :\n\n `function(value [, key [, object]])`"
},
"$each": {
"args": "object, function",
"desc": "`object`의 각 키/값 쌍에, 함수`function`을 적용한 값의 배열을 반환합니다."
},
"$map": {
"args": "array, function",
"desc": "`array`의 각 값에 `function`을 적용한 결과로 이루어진 배열을 반환합니다.\n\n 함수 `function`은 다음과 같은 인수를 가져야 합니다.\n\n `function(value[, index[, array]])`"
},
"$filter": {
"args": "array, function",
"desc": "`array`의 값중, 함수 `function`의 조건을 만족하는 값으로 이루어진 배열을 반환합니다.\n\n 함수 `function`은 다음과 같은 형식을 가져야 합니다.\n\n `function(value[, index[, array]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "배열의 각 요소값에 함수 `function`을 연속적으로 적용하여 얻어지는 집계값을 반환합니다. `function`의 적용에는 직전의 `function`의 적용결과와 요소값이 인수로 주어집니다.\n\n 함수 `function`은 인수를 두개 뽑아, 배열의 각 요소 사이에 배치하는 중치연산자처럼 작용해야 합니다.\n\n 임의의 인수 `init`에는 집약시의 초기값을 설정합니다."
},
"$flowContext": {
"args": "string[, string]",
"desc": "플로우 컨텍스트 속성을 취득합니다."
},
"$globalContext": {
"args": "string[, string]",
"desc": "플로우의 글로벌 컨텍스트 속성을 취득합니다."
},
"$pad": {
"args": "string, width [, char]",
"desc": "문자수가 인수 `width`의 절대값이상이 되도록, 필요한 경우 여분의 패딩을 사용하여 `string`의 복사본을 반환합니다.\n\n `width`가 양수인 경우, 오른쪽으로 채워지고, 음수이면 왼쪽으로 채워집니다.\n\n 임의의 `char`인수에는 이 함수에서 사용할 패딩을 지정합니다. 지정하지 않는 경우에는, 기본값으로 공백을 사용합니다."
},
"$fromMillis": {
"args": "number",
"desc": "Unix Epoch (1970 년 1 월 1 일 UTC) 이후의 밀리 초를 나타내는 숫자를 ISO 8601 형식의 타임 스탬프 문자열로 변환합니다."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "`number`를 문자열로 변환하고 `picture` 문자열에 지정된 표현으로 서식을 변경합니다.\n\n 이 함수의 동작은 XPath F&O 3.1사양에 정의된 XPath/XQuery함수의 `fn:format-number`의 동작과 같습니다. 인수의 문자열 `picture``fn:format-number` 과 같은 구문으로 수치의 서식을 정의합니다.\n\n 임의의 제3 인수 `option`은 소수점기호와 같은 기본 로케일 고유의 서식설정문자를 덮어쓰는데에 사용됩니다. 이 인수를 지정할 경우, XPath F&O 3.1사양의 수치형식에 기술되어있는 name/value 쌍을 포함하는 오브젝트여야 합니다."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "`number`를 인수 `radix`에 지정한 값을 기수로하는 문자열로 변환합니다. `radix`가 지정되지 않은 경우, 기수 10이 기본값으로 설정됩니다. `radix`에는 2~36의 값을 설정할 수 있고, 그 외의 값의 경우에는 에러가 발생합니다."
},
"$toMillis": {
"args": "timestamp",
"desc": "ISO 8601 형식의 `timestamp`를 Unix Epoch (1970 년 1 월 1 일 UTC) 이후의 밀리 초 수로 변환합니다. 문자열이 올바른 형식이 아닌 경우 에러가 발생합니다."
},
"$env": {
"args": "arg",
"desc": "환경변수를 값으로 반환합니다.\n\n 이 함수는 Node-RED 정의 함수입니다."
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"info": {
"tip0": "Você pode remover os nós ou links selecionados com {{core:delete-selection}}",
"tip1": "Procure por nós usando {{core:search}}",
"tip2": "{{core:toggle-sidebar}} irá alternar a visualização desta barra lateral",
"tip3": "Você pode gerenciar sua paleta de nós com {{core:manage-palette}}",
"tip4": "Seus nós de configuração de fluxo são listados no painel da barra lateral. Pode ser acessado a partir do menu ou com{{core:show-config-tab}}",
"tip5": "Habilite ou desabilite essas dicas na opção nas configurações",
"tip6": "Mova os nós selecionados usando o [left] [up] [down] e [right] chaves. Segure [shift] para empurrá-los ainda mais",
"tip7": "Arrastar um nó para um fio o unirá no link",
"tip8": "Exporte os nós selecionados ou a guia atual com {{core:show-export-dialog}}",
"tip9": "Importe um fluxo arrastando seu JSON para o editor ou com {{core:show-import-dialog}}",
"tip10": "[shift] [click] e arraste em uma porta de nó para mover todos os fios conectados ou apenas o selecionado",
"tip11": "Mostre a guia Informações com {{core:show-info-tab}} ou a guia Depurar com {{core:show-debug-tab}}",
"tip12": "[ctrl] [click] na área de trabalho para abrir a caixa de diálogo de adição rápida",
"tip13": "Mantenha pressionado [ctrl] enquanto você [click] em uma porta de nó para habilitar a ligação rápida",
"tip14": "Mantenha pressionado [shift] enquanto você [click] em um nó para também selecionar todos os seus nós conectados",
"tip15": "Mantenha pressionado [ctrl] enquanto você [click] em um nó para adicioná-lo ou removê-lo da seleção atual",
"tip16": "Alternar guias de fluxo com {{core:show-previous-tab}} e {{core:show-next-tab}}",
"tip17": "Você pode confirmar suas alterações na bandeja de edição do nó com {{core:confirm-edit-tray}} ou cancele-os com {{core:cancel-edit-tray}}",
"tip18": "Pressionando {{core:edit-selected-node}} irá editar o primeiro nó na seleção atual"
}
}

View File

@@ -0,0 +1,274 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Converte o tipo do parâmetro `arg` em uma cadeia de caracteres usando as seguintes regras de conversão de tipo:\n\n - Cadeia de caracteres não são alteradas\n - As funções são convertidas para uma cadeia de caracteres vazia\n - os tipos numérico infinito e NaN geram um erro porque não podem ser representados como um número JSON\n - Todos os outros valores são convertidos para uma cadeia de caracteres JSON usando a função `JSON.stringify`. Se `prettify` for verdadeira, então o JSON \"prettified\" é produzido. Isto é, uma linha por campo e as linhas serão indentadas com base na profundidade do campo."
},
"$length": {
"args": "str",
"desc": "Retorna o número de caracteres na cadeia de caracteres `str`. Um erro é gerado se `str` não for uma cadeia de caracteres."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Retorna uma cadeia de caracteres contendo os caracteres no primeiro parâmetro `str` começando na posição `start` (deslocamento zero). Se` length` for especificado, então a sub cadeia de caracteres conterá o máximo `length` de caracteres. Se` start` for negativo isso indica o número de caracteres a partir do fim de `str`."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Retorna a sub cadeia de caracteres antes da primeira ocorrência da sequência de caracteres `chars` em `string`. Se` string` não contiver `chars`, então retorna `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Retorna a sub cadeia de caracteres após a primeira ocorrência da sequência de caracteres `chars` em `string`. Se `string` não contiver `chars`, então retorna `str`."
},
"$uppercase": {
"args": "str",
"desc": "Retorna uma cadeia de caracteres com todos os caracteres de `string` convertidos em maiúsculas."
},
"$lowercase": {
"args": "str",
"desc": "Retorna uma cadeia de caracteres com todos os caracteres de `string` convertidos em minúsculas."
},
"$trim": {
"args": "str",
"desc": "Normaliza e retira todos os caracteres de espaço em branco em `str` aplicando as seguintes etapas:\n\n - Todas as tabulações, retornos de carro e avanços de linha são substituídos por espaços.\n- Sequências contíguas de espaços são reduzidas a um único espaço.\n- Espaços à direita e à esquerda são removidos.\n\n Se `str` não for especificado (isto é, esta função é chamada sem argumentos), então o valor do contexto é usado como o valor de `str`. Um erro é gerado se `str` não for uma cadeia de caracteres."
},
"$contains": {
"args": "str, pattern",
"desc": "Retorna `true` se `str` tiver correspondente em `pattern`, caso contrário, retorna `false`. Se `str` não for especificado (isto é, esta função é chamada com um argumento), então o valor do contexto é usado como o valor de `str`. O parâmetro `pattern` pode ser uma cadeia de caracteres ou uma expressão regular."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Divide o parâmetro `str` em uma matriz de sub cadeia de caracteres. É um erro se `str` não for uma cadeia de caracteres. O parâmetro opcional `separator` especifica os caracteres dentro de `str` sobre os quais devem ser divididos como uma cadeia de caracteres ou expressão regular. Se `separator` não for especificado, a cadeia de caracteres vazia será assumida e `str` será dividido em uma matriz de caracteres únicos. É um erro se `separador` não for uma cadeia de caracteres. O parâmetro opcional `limit` é um número que especifica o número máximo de sub cadeia de caracteres a serem incluídas na matriz resultante. Quaisquer sub cadeia de caracteres adicionais são descartadas. Se `limit` não for especificado, então `str` será totalmente dividido sem limite para o tamanho da matriz resultante . É um erro se `limit` não for um número não negativo."
},
"$join": {
"args": "array[, separator]",
"desc": "Une uma matriz de cadeias de caracteres de componentes em uma única cadeia de caracteres concatenada com cada cadeia de caracteres de componente separada pelo parâmetro opcional `separator`. É um erro se a `matriz` de entrada contiver um item que não seja uma cadeia de caracteres. Se `separator` for não especificado, assume-se que é uma cadeia de caracteres vazia, ou seja, nenhum `separator` entre as cadeias de caracteres do componente. É um erro se `separator` não for uma cadeia de caracteres."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "Aplica a cadeia de caracteres `str` à expressão regular `pattern` e retorna uma matriz de objetos, com cada objeto contendo informações sobre cada ocorrência de uma correspondência dentro de `str`."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "Encontra ocorrências de `pattern` dentro de `str` e as substitui por `replacement`.\n\nO parâmetro opcional `limit` é o número máximo de substituições."
},
"$now": {
"args": "$[picture [, timezone]]",
"desc": "Gera um carimbo de data/hora em formato compatível com ISO 8601 e o retorna como uma cadeia de caracteres. Se os parâmetros opcionais de imagem e fuso horário forem fornecidos, o carimbo de data/hora atual é formatado conforme descrito pela função `$fromMillis()`"
},
"$base64encode": {
"args": "string",
"desc": "Converte uma cadeia de caracteres ASCII em uma representação de base 64. Cada caractere na cadeia de caracteres é tratado como um byte de dados binários. Isso requer que todos os caracteres na cadeia de caracteres estejam no intervalo de 0x00 a 0xFF, o que inclui todos os caracteres em cadeias de caracteres codificadas em URI. Caracteres Unicode fora desse intervalo não são suportados."
},
"$base64decode": {
"args": "string",
"desc": "Converte bytes codificados de base 64 em uma cadeia de caracteres, usando uma página de código UTF-8 Unicode."
},
"$number": {
"args": "arg",
"desc": "Converte o parâmetro `arg` em um número usando as seguintes regras de conversão:\n\n - Os números permanecem inalterados\n - Cadeias de caracteres que contêm uma sequência de caracteres que representam um número JSON válido são convertidos para esse número\n - Todos os outros valores causam a geração de um erro."
},
"$abs": {
"args": "number",
"desc": "Retorna o valor absoluto do parâmetro `number`."
},
"$floor": {
"args": "number",
"desc": "Retorna o valor de `number` arredondado para baixo para o inteiro mais próximo que seja menor ou igual a `number`."
},
"$ceil": {
"args": "number",
"desc": "Retorna o valor de `number` arredondado para o número inteiro mais próximo que é maior ou igual a `number`."
},
"$round": {
"args": "number [, precision]",
"desc": "Retorna o valor do parâmetro `number` arredondado para o número de casas decimais especificado pelo parâmetro opcional `precision`."
},
"$power": {
"args": "base, exponent",
"desc": "Retorna o valor de `base` elevado à potência de `exponent`."
},
"$sqrt": {
"args": "number",
"desc": "Retorna a raiz quadrada do valor do parâmetro `number`."
},
"$random": {
"args": "",
"desc": "Retorna um número pseudoaleatório maior ou igual a zero e menor que um."
},
"$millis": {
"args": "",
"desc": "Retorna o número de milissegundos desde o Unix Epoch (1º de janeiro de 1970 UTC) como um número. Todas as invocações de `$millis()` dentro de uma avaliação de uma expressão retornarão todas o mesmo valor."
},
"$sum": {
"args": "array",
"desc": "Retorna a soma aritmética de uma `array` de números. É um erro se o `array` de entrada contiver um item que não seja um número."
},
"$max": {
"args": "array",
"desc": "Retorna o número máximo em uma `array` de números. É um erro se o `array` de entrada contiver um item que não seja um número."
},
"$min": {
"args": "array",
"desc": "Retorna o número mínimo em uma `array` de números. É um erro se o `array` de entrada contiver um item que não seja um número."
},
"$average": {
"args": "array",
"desc": "Retorna o valor médio de uma `array` de números. É um erro se o `array` de entrada contiver um item que não seja um número."
},
"$boolean": {
"args": "arg",
"desc": "Converte o argumento em um booliano usando as seguintes regras:\n\n - `Boolean` : inalterado\n - `string`: vazio : `false`\n - `string`: não-vazio : `true`\n - `number`: `0` : `false`\n - `number`: não-zero : `true`\n - `null` : `false`\n - `array`: vazio : `false`\n - `array`: contém um membro que converte de tipo para `true` : `true`\n - `array`: todos os membros convertidos de tipo para `false` : `false`\n - `object`: vazio : `false`\n - `object`: não-vazio : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "Retorna booliano NOT no argumento. `Arg` é convertido de tipo primeiro para um booliano"
},
"$exists": {
"args": "arg",
"desc": "Retorna booliano `true` se a expressão `arg` for avaliada como um valor, ou `false` se a expressão não corresponder a nada (por exemplo, um caminho para uma referência de campo inexistente)."
},
"$count": {
"args": "array",
"desc": "Retorna o número de itens na matriz"
},
"$append": {
"args": "array, array",
"desc": "Anexa duas matrizes"
},
"$sort": {
"args": "array [, function]",
"desc": "Retorna uma matriz contendo todos os valores no parâmetro `array`, mas classificados em ordem.\n\nSe um comparador `function` for fornecido, então deve ser uma função que leva dois parâmetros:\n\n`function(left, right)`\n\nEsta função é invocada pelo algoritmo de classificação para comparar dois valores à esquerda e à direita. Se o valor de esquerda deve ser colocado após o valor de direita na ordem de classificação desejada, a função deve retornar o booliano `true` para indicar uma troca. Caso contrário, deve retornar `false`."
},
"$reverse": {
"args": "array",
"desc": "Retorna uma matriz contendo todos os valores do parâmetro `array`, mas na ordem reversa."
},
"$shuffle": {
"args": "array",
"desc": "Retorna uma matriz contendo todos os valores do parâmetro `array`, mas misturados em ordem aleatória."
},
"$zip": {
"args": "array, ...",
"desc": "Retorna uma matriz convolucional (compactada) contendo matrizes agrupadas de valores dos argumentos `array1`… `arrayN` do índice 0, 1, 2 ...."
},
"$keys": {
"args": "object",
"desc": "Retorna uma matriz contendo as chaves do objeto. Se o argumento for uma matriz de objetos, então a matriz retornada contém uma lista não duplicada de todas as chaves em todos os objetos."
},
"$lookup": {
"args": "object, key",
"desc": "Retorna o valor associado à chave no objeto. Se o primeiro argumento for uma matriz de objetos, todos os objetos na matriz são pesquisados e os valores associados a todas as ocorrências da chave são retornados."
},
"$spread": {
"args": "object",
"desc": "Divide um objeto que contém pares de chave/valor em uma matriz de objetos, cada um com um único par de chave/valor do objeto de entrada. Se o parâmetro for uma matriz de objetos, a matriz resultante conterá um objeto para cada par de chave/valor em todo objeto na matriz fornecida."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Mescla uma matriz de `objects` em um único `object` contendo todos os pares de chave/valor de cada um dos objetos na matriz de entrada. Se qualquer um dos objetos de entrada contiver a mesma chave, então o `object` retornado conterá o valor do último na matriz. É um erro se a matriz de entrada contiver um item que não seja um objeto."
},
"$sift": {
"args": "object, function",
"desc": "Retorna um objeto que contém apenas os pares de chave/valor do parâmetro `object` que satisfazem o predicado `function` passado como o segundo parâmetro.\n\nA `function` que é fornecida como o segundo parâmetro deve ter o seguinte assinatura:\n\n`function(value [, key [, object]])`"
},
"$each": {
"args": "object, function",
"desc": "Retorna uma matriz contendo os valores retornados por `function` quando aplicado a cada par chave/valor no `object`."
},
"$map": {
"args": "array, function",
"desc": "Retorna uma matriz contendo os resultados da aplicação do parâmetro `function` a cada valor no parâmetro `array`.\n\nA `function` que é fornecido como o segundo parâmetro deve ter a seguinte assinatura:\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args": "array, function",
"desc": "Retorna uma matriz contendo apenas os valores no parâmetro `array` que satisfazem o predicado `function`.\n\nThe `function` que é fornecido como o segundo parâmetro deve ter a seguinte assinatura:\n\n`function(value [, index [, array]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "Retorna um valor agregado derivado da aplicação do parâmetro `function` sucessivamente a cada valor em `array` em combinação com o resultado da aplicação anterior da função.\n\nA função deve aceitar dois argumentos e se comportar como um operador inserido entre cada valor dentro de `array`. A assinatura da `function` deve estar no formato: `myfunc($accumulator, $value[, $index[, $array]])`\n\nO parâmetro opcional `init` é usado como o valor inicial na agregação."
},
"$flowContext": {
"args": "string[, string]",
"desc": "Recupera uma propriedade de contexto de fluxo.\n\nEsta é uma função definida pelo Node-RED."
},
"$globalContext": {
"args": "string[, string]",
"desc": "Recupera uma propriedade de contexto global.\n\nEsta é uma função definida pelo Node-RED."
},
"$pad": {
"args": "string, width [, char]",
"desc": "Retorna uma cópia da `string` com preenchimento extra, se necessário, de forma que seu número total de caracteres seja pelo menos o valor absoluto do parâmetro `width`.\n\nSe `width` for um número positivo, a cadeia de caracteres será preenchida à direita; se negativo, é preenchida à esquerda.\n\nO argumento opcional `char` especifica os caracteres de preenchimento a serem usados. Se não for especificado, o padrão é o caractere de espaço."
},
"$fromMillis": {
"args": "number, [, picture [, timezone]]",
"desc": "Converta o `number` que representa os milissegundos desde a época do Unix (1 January, 1970 UTC) em uma representação de cadeia de caracteres formatada do carimbo de data/hora conforme especificado pela cadeia de caracteres de imagem.\n\nSe o parâmetro opcional `image` for omitido, o carimbo de data/hora será formatado no formato ISO 8601.\n\nSe a cadeia de caracteresopcional `picture` for fornecida, o carimbo de data/hora é formatado de acordo com a representação especificada nessa cadeia de caracteres. O comportamento desta função é consistente com a versão de dois argumentos da função XPath/XQuery `format-dateTime` conforme definido na especificação XPath F&O 3.1. O parâmetro de cadeia de caracteres de imagem define como o carimbo de data/hora é formatado e tem a mesma sintaxe de `format-dateTime`.\n\nSe a cadeia de caracteres opcional `timezone` for fornecida, o carimbo de data/hora formatado estará nesse fuso horário. A cadeia de caracteres `timezone` deve estar no formato '± HHMM', onde ± é o sinal de mais ou menos e HHMM é o deslocamento em horas e minutos do UTC. Deslocamento positivo para fusos horários a leste do UTC, deslocamento negativo para fusos horários a oeste do UTC."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Converte o tipo de `number` em uma cadeia de caracteres e o formata em uma representação decimal conforme especificado pela cadeia de caracteres `picture`.\n\n O comportamento desta função é consistente com a função XPath/XQuery `fn:format-number` conforme definido na especificação XPath F&O 3.1. O parâmetro de cadeia de caracteres de imagem define como o número é formatado e tem a mesma sintaxe de `fn:format-number`.\n\nO terceiro argumento opcional `options` é usado para substituir os caracteres de formatação específicos da localidade padrão, como o separador decimal. Se fornecido, este argumento deve ser um objeto contendo pares de nome/valor especificados na seção de formato decimal da especificação XPath F&O 3.1."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "Converte o `number` em uma cadeia de caracteres e o formata em um inteiro representado na base do número especificada pelo argumento `radix`. Se `radix` não for especificado, o padrão é a base 10. `radix` pode estar entre 2 e 36, caso contrário, um erro será gerado."
},
"$toMillis": {
"args": "timestamp",
"desc": "Converta o tipo de uma cadeia de caracteres `timestamp` no formato ISO 8601 para o número de milissegundos desde a época do Unix (1 January, 1970 UTC) como um número. Um erro é gerado se a cadeia de caracteres não estiver no formato correto."
},
"$env": {
"args": "arg",
"desc": "Retorna o valor de uma variável de ambiente.\n\nEsta é uma função definida pelo Node-RED."
},
"$eval": {
"args": "expr [, context]",
"desc": "Analisa e avalia a cadeia de caracteres `expr` que contém um JSON literal ou uma expressão JSONata usando o contexto atual como o contexto para avaliação."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Converte o tipo de `number` em uma cadeia de caracteres e o formata em uma representação inteira conforme especificado pela cadeia de caracteres `picture`. O parâmetro da cadeia de caracteres de imagem define como o número é formatado e tem a mesma sintaxe de `fn:format-integer` do Especificação XPath F&O 3.1."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Examina e troca o conteúdo do parâmetro `string` para um inteiro (como um número JSON) usando o formato especificado pela cadeia de caracteres `picture`. O parâmetro da cadeia de caracteres `picture` tem o mesmo formato que `$formatInteger`."
},
"$error": {
"args": "[str]",
"desc": "Gera um erro com uma mensagem. O (parâmetro) opcional `str` substituirá a mensagem padrão de `$error() function evaluated`"
},
"$assert": {
"args": "arg, str",
"desc": "Se `arg` for verdadeiro, a função retorna indefinido. Se `arg` for falso, uma exceção é gerada com `str` como a mensagem da exceção."
},
"$single": {
"args": "array, function",
"desc": "Retorna o único valor no parâmetro `array` que satisfaz o predicado `function` (isto é, O (parâmetro) `function` retorna o booliano `true` quando passado o valor). Gera uma exceção se o número de valores correspondentes não for exatamente um .\n\nA função deve ser fornecida na seguinte assinatura: `function(value [, index [, array]])` onde 'value' é cada entrada da matriz, 'index' é a posição desse valor e toda a matriz é passada como o terceiro argumento"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Codifica um componente Localizador Uniforme de Recursos (URL) substituindo cada instância de certos caracteres por uma, duas, três ou quatro sequências de escape que representam a codificação UTF-8 do caractere.\n\nExemplo: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrl": {
"args": "str",
"desc": "Codifica um Localizador Uniforme de Recursos (URL) substituindo cada instância de certos caracteres por uma, duas, três ou quatro sequências de escape que representam a codificação UTF-8 do caractere.\n\nExemplo: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Decodifica um componente Localizador Uniforme de Recursos (URL) criado anteriormente por encodeUrlComponent.\n\nExemplo: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Decodifica um Localizador Uniforme de Recursos (URL) criado anteriormente por encodeUrl.\n\nExemplo: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "Retorna uma matriz com valores duplicados removidos da `array`"
},
"$type": {
"args": "value",
"desc": "Retorna o tipo de `value` como uma cadeia de caracteres. Se `value` for indefinido, retornará `undefined`"
},
"$moment": {
"args": "[str]",
"desc": "Obtém um objeto de dados usando a biblioteca 'Moment'."
}
}

10
packages/node_modules/@node-red/editor-client/locales/ru/editor.json vendored Executable file → Normal file
View File

@@ -95,7 +95,6 @@
"selectionToSubflow": "Выделение в подпоток",
"flows": "Потоки",
"add": "Добавить",
"rename": "Переименовать",
"delete": "Удалить",
"keyboardShortcuts": "Сочетания клавиш",
"login": "Войти",
@@ -1129,14 +1128,5 @@
"appearance": "Внешний вид",
"preview": "Предпросмотр редактора",
"defaultValue": "Значение по умолчанию"
},
"languages" : {
"de": "Немецкий",
"en-US": "Английский",
"ja": "Японский",
"ko": "Корейский",
"ru": "Русский",
"zh-CN": "Китайский (упрощенный)",
"zh-TW": "Китайский (традиционный)"
}
}

View File

86
packages/node_modules/@node-red/editor-client/locales/ru/jsonata.json vendored Executable file → Normal file
View File

@@ -52,52 +52,52 @@
"desc": "Находит вхождения шаблона `pattern` в строке `str` и заменяет их на строку `replacement`.\n\nНеобязательный параметр `limit` - это максимальное количество замен."
},
"$now": {
"args":"",
"desc":"Создает отметку времени в формате, совместимом с ISO 8601, и возвращает ее как строку."
"args": "",
"desc": "Создает отметку времени в формате, совместимом с ISO 8601, и возвращает ее как строку."
},
"$base64encode": {
"args":"string",
"desc":"Преобразует ASCII-строку в base-64 кодировку. Каждый символ в строке обрабатывается как байт двоичных данных. Для этого необходимо, чтобы все символы в строке находились в диапазоне от 0x00 до 0xFF, который включает все символы строк в URI-кодировке. Символы Юникода за пределами этого диапазона не поддерживаются."
"args": "string",
"desc": "Преобразует ASCII-строку в base-64 кодировку. Каждый символ в строке обрабатывается как байт двоичных данных. Для этого необходимо, чтобы все символы в строке находились в диапазоне от 0x00 до 0xFF, который включает все символы строк в URI-кодировке. Символы Юникода за пределами этого диапазона не поддерживаются."
},
"$base64decode": {
"args":"string",
"desc":"Преобразует байты в кодировке base-64 в строку, используя кодовую страницу Юникод UTF-8."
"args": "string",
"desc": "Преобразует байты в кодировке base-64 в строку, используя кодовую страницу Юникод UTF-8."
},
"$number": {
"args": "arg",
"desc": "Преобразует параметр `arg` в число с использованием следующих правил приведения:\n\n - Числа возвращаются как есть\n - Строки, которые содержат последовательность символов, представляющих допустимое в JSON число, преобразуются в это число\n - Все остальные значения вызывают ошибку."
},
"$abs": {
"args":"number",
"desc":"Возвращает абсолютное значение числа `number`."
"args": "number",
"desc": "Возвращает абсолютное значение числа `number`."
},
"$floor": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое меньше или равно `number`."
"args": "number",
"desc": "Возвращает значение числа `number`, округленное до ближайшего целого числа, которое меньше или равно `number`."
},
"$ceil": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое больше или равно `number`."
"args": "number",
"desc": "Возвращает значение числа `number`, округленное до ближайшего целого числа, которое больше или равно `number`."
},
"$round": {
"args":"number [, precision]",
"desc":"Возвращает значение числа `number`, округленное до количества десятичных знаков, указанных необязательным параметром `precision`."
"args": "number [, precision]",
"desc": "Возвращает значение числа `number`, округленное до количества десятичных знаков, указанных необязательным параметром `precision`."
},
"$power": {
"args":"base, exponent",
"desc":"Возвращает значение числа `base`, возведенное в степень `exponent`."
"args": "base, exponent",
"desc": "Возвращает значение числа `base`, возведенное в степень `exponent`."
},
"$sqrt": {
"args":"number",
"desc":"Возвращает квадратный корень из значения числа `number`."
"args": "number",
"desc": "Возвращает квадратный корень из значения числа `number`."
},
"$random": {
"args":"",
"desc":"Возвращает псевдослучайное число, которе больше или равно нулю и меньше единицы."
"args": "",
"desc": "Возвращает псевдослучайное число, которе больше или равно нулю и меньше единицы."
},
"$millis": {
"args":"",
"desc":"Возвращает число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу) в виде числа. Все вызовы `$millis()` в пределах выполнения выражения будут возвращать одно и то же значение."
"args": "",
"desc": "Возвращает число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу) в виде числа. Все вызовы `$millis()` в пределах выполнения выражения будут возвращать одно и то же значение."
},
"$sum": {
"args": "array",
@@ -117,7 +117,7 @@
},
"$boolean": {
"args": "arg",
"desc": "Приводит аргумент к логическому значению, используя следующие правила: \n\n - Логические значения возвращаются как есть\n - пустая строка: `false`\n - непустая строка: `true`\n - число равное `0`: `false`\n - ненулевое число: `true`\n - `null` : `false`\n - пустой массив: `false`\n - массив, который содержит хотя бы один элемент, приводимый к `true`: `true`\n - массив, все элементы которого приводятся к `false`: `false`\n - пустой объект: `false`\n - непустой объект: `true`\n - функция: `false`"
"desc": "Приводит аргумент к логическому значению, используя следующие правила:\n\n - Логические значения возвращаются как есть\n - пустая строка: `false`\n - непустая строка: `true`\n - число равное `0`: `false`\n - ненулевое число: `true`\n - `null` : `false`\n - пустой массив: `false`\n - массив, который содержит хотя бы один элемент, приводимый к `true`: `true`\n - массив, все элементы которого приводятся к `false`: `false`\n - пустой объект: `false`\n - непустой объект: `true`\n - функция: `false`"
},
"$not": {
"args": "arg",
@@ -136,20 +136,20 @@
"desc": "Присоединяет один массив к другому"
},
"$sort": {
"args":"array [, function]",
"desc":"Возвращает массив, содержащий все значения параметра `array`, но отсортированные по порядку.\n\nЕсли указан компаратор `function`, то это должна быть функция, которая принимает два параметра:\n\n`function(val1, val2)`\n\nЭту функцию вызывает алгоритм сортировки для сравнения двух значений: val1 и val2. Если значение val1 следует поместить после значения val2 в желаемом порядке сортировки, то функция должна возвращать логическое значение `true`, чтобы обозначить замену. В противном случае она должна вернуть `false`."
"args": "array [, function]",
"desc": "Возвращает массив, содержащий все значения параметра `array`, но отсортированные по порядку.\n\nЕсли указан компаратор `function`, то это должна быть функция, которая принимает два параметра:\n\n`function(val1, val2)`\n\nЭту функцию вызывает алгоритм сортировки для сравнения двух значений: val1 и val2. Если значение val1 следует поместить после значения val2 в желаемом порядке сортировки, то функция должна возвращать логическое значение `true`, чтобы обозначить замену. В противном случае она должна вернуть `false`."
},
"$reverse": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но в обратном порядке."
"args": "array",
"desc": "Возвращает массив, содержащий все значения из параметра `array`, но в обратном порядке."
},
"$shuffle": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но перемешанный в случайном порядке."
"args": "array",
"desc": "Возвращает массив, содержащий все значения из параметра `array`, но перемешанный в случайном порядке."
},
"$zip": {
"args":"array, ...",
"desc":"Возвращает свернутый (сжатый) массив, содержащий сгруппированные массивы значений из аргументов `array1` … `arrayN` по индексам 0, 1, 2...."
"args": "array, ...",
"desc": "Возвращает свернутый (сжатый) массив, содержащий сгруппированные массивы значений из аргументов `array1` … `arrayN` по индексам 0, 1, 2...."
},
"$keys": {
"args": "object",
@@ -168,24 +168,24 @@
"desc": "Объединяет массив объектов в один объект, содержащий все пары ключ / значение каждого из объектов входного массива. Если какой-либо из входных объектов содержит один и тот же ключ, возвращаемый объект будет содержать значение последнего в массиве. Вызывает ошибку, если входной массив содержит элемент, который не является объектом."
},
"$sift": {
"args":"object, function",
"desc":"Возвращает объект, который содержит только пары ключ / значение из параметра `object`, которые удовлетворяют предикату `function`, переданному в качестве второго параметра.\n\n`function`, которая передается в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, key [, object]])`"
"args": "object, function",
"desc": "Возвращает объект, который содержит только пары ключ / значение из параметра `object`, которые удовлетворяют предикату `function`, переданному в качестве второго параметра.\n\n`function`, которая передается в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, key [, object]])`"
},
"$each": {
"args":"object, function",
"desc":"Возвращает массив, который содержит значения, возвращаемые функцией `function` при применении к каждой паре ключ/значение из объекта `object`."
"args": "object, function",
"desc": "Возвращает массив, который содержит значения, возвращаемые функцией `function` при применении к каждой паре ключ/значение из объекта `object`."
},
"$map": {
"args":"array, function",
"desc":"Возвращает массив, содержащий результаты применения функции `function` к каждому значению массива `array`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
"args": "array, function",
"desc": "Возвращает массив, содержащий результаты применения функции `function` к каждому значению массива `array`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args":"array, function",
"desc":"Возвращает массив, содержащий только те значения из массива `array`, которые удовлетворяют предикату `function`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
"args": "array, function",
"desc": "Возвращает массив, содержащий только те значения из массива `array`, которые удовлетворяют предикату `function`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$reduce": {
"args":"array, function [, init]",
"desc":"Возвращает агрегированное значение, полученное в результате последовательного применения функции `function` к каждому значению в массиве в сочетании с результатом от предыдущего применения функции.\n\nФункция должна принимать два аргумента и вести себя как инфиксный оператор между каждым значением в массиве `array`. Сигнатура `function` должна иметь форму: `myfunc($accumulator, $value[, $index[, $array]])`\n\nНеобязательный параметр `init` используется в качестве начального значения в агрегации."
"args": "array, function [, init]",
"desc": "Возвращает агрегированное значение, полученное в результате последовательного применения функции `function` к каждому значению в массиве в сочетании с результатом от предыдущего применения функции.\n\nФункция должна принимать два аргумента и вести себя как инфиксный оператор между каждым значением в массиве `array`. Сигнатура `function` должна иметь форму: `myfunc($accumulator, $value[, $index[, $array]])`\n\nНеобязательный параметр `init` используется в качестве начального значения в агрегации."
},
"$flowContext": {
"args": "string[, string]",
@@ -237,7 +237,7 @@
},
"$assert": {
"args": "arg, str",
"desc": "Если значение `arg` равно true, функция возвращает значение undefined. Если значение `arg` равно false, генерируется исключение с `str` в качестве сообщения об исключении."
"desc": "Если значение `arg` равно `true`, функция возвращает значение undefined. Если значение `arg` равно `false`, генерируется исключение с `str` в качестве сообщения об исключении."
},
"$single": {
"args": "array, function",
@@ -257,7 +257,7 @@
},
"$decodeUrl": {
"args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl.\n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",

File diff suppressed because it is too large Load Diff

View File

@@ -117,7 +117,7 @@
},
"$boolean": {
"args": "arg",
"desc": "用下述规则将数据转换成布尔值。:\n\n - 不转换布尔值 `Boolean` 。\n 将空的字符串 `string` 转换为 `false` \n 将不为空的字符串 `string` 转换为 `true` \n 将为0的数字 `number` 转换成 `false` \n 将不为0的数字 `number` 转换成 `true` \n –将 `null` 转换成 `false` \n –将空的数组 `array` 转换成 `false` \n –如果数组 `array` 中含有可以转换成 `true` 的要素则转换成 `true` \n –如果 `array` 中没有可转换成 `true` 的要素则转换成 `false` \n 空的对象 `object` 转换成 `false` \n 非空的对象 `object` 转换成 `true` \n –将函数 `function` 转换成 `false` "
"desc": "用下述规则将数据转换成布尔值。:\n\n - 不转换布尔值 `Boolean` 。\n 将空的字符串 `string` 转换为 `false`\n 将不为空的字符串 `string` 转换为 `true`\n 将为0的数字 `number` 转换成 `false`\n 将不为0的数字 `number` 转换成 `true`\n –将 `null` 转换成 `false`\n –将空的数组 `array` 转换成 `false`\n –如果数组 `array` 中含有可以转换成 `true` 的要素则转换成 `true`\n –如果 `array` 中没有可转换成 `true` 的要素则转换成 `false`\n 空的对象 `object` 转换成 `false`\n 非空的对象 `object` 转换成 `true`\n –将函数 `function` 转换成 `false`"
},
"$not": {
"args": "arg",
@@ -137,7 +137,7 @@
},
"$sort": {
"args": "array [, function]",
"desc": "输出排序后的数组 `array` 。\n\n如果使用了比较函数 `function` ,则下述两个参数需要被指定。\n\n `function(left, right)` \n\n该比较函数是为了比较leftright两个值而被排序算法调用的。如果用户希望left的值被置于right的值之后那么该函数必须输出布尔值 `true` 来表示位置交换。而在不需要位置交换时函数必须输出 `false` 。"
"desc": "输出排序后的数组 `array` 。\n\n如果使用了比较函数 `function` ,则下述两个参数需要被指定。\n\n `function(left, right)`\n\n该比较函数是为了比较`left`和`right`两个值而被排序算法调用的。如果用户希望`left`的值被置于`right`的值之后,那么该函数必须输出布尔值 `true` 来表示位置交换。而在不需要位置交换时函数必须输出 `false` 。"
},
"$reverse": {
"args": "array",
@@ -169,7 +169,7 @@
},
"$sift": {
"args": "object, function",
"desc": "输出参数 `object` 中符合 `function` 的键值对。\n\n `function` 必须含有下述参数。\n\n `function(value [, key [, object]])` "
"desc": "输出参数 `object` 中符合 `function` 的键值对。\n\n `function` 必须含有下述参数。\n\n `function(value [, key [, object]])`"
},
"$each": {
"args": "object, function",
@@ -177,7 +177,7 @@
},
"$map": {
"args": "array, function",
"desc": "将函数 `function` 应用于数组 `array` 中所有的值并输出由返回值组成的数组。\n\n `function` 中必须含有下述参数。\n\n`function(value [, index [, array]])` "
"desc": "将函数 `function` 应用于数组 `array` 中所有的值并输出由返回值组成的数组。\n\n `function` 中必须含有下述参数。\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args": "array, function",
@@ -237,7 +237,7 @@
},
"$assert": {
"args": "arg, str",
"desc": "如果 `arg` 为真,则该函数返回。 如果arg为假则抛出带有str的异常作为异常消息。"
"desc": "如果 `arg` 为真,则该函数返回。 如果`arg`为假,则抛出带有`str`的异常作为异常消息。"
},
"$single": {
"args": "array, function",
@@ -253,11 +253,11 @@
},
"$decodeUrlComponent": {
"args": "str",
"desc": "解码以前由encodeUrlComponent创建的统一资源定位器URL组件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
"desc": "解码以前由encodeUrlComponent创建的统一资源定位器URL组件。\n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "解码先前由encodeUrl创建的统一资源定位符URL \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
"desc": "解码先前由encodeUrl创建的统一资源定位符URL。\n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
@@ -265,10 +265,14 @@
},
"$type": {
"args": "value",
"desc": "以字符串形式返回 `值` 的类型。 如果该 `值` 未定义,则将返回 `未定义` "
"desc": "以字符串形式返回 `值` 的类型。 如果该 `值` 未定义,则将返回 `未定义`"
},
"$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,18 +1188,45 @@
"editor-tab": {
"properties": "屬性",
"envProperties": "環境變量",
"module": "模塊屬性",
"description": "描述",
"appearance": "外觀",
"preview": "UI預覽",
"defaultValue": "默認值",
"env": "環境變量"
"defaultValue": "默認值"
},
"languages": {
"de": "德語",
"en-US": "英語",
"ja": "日語",
"ko": "韓語",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文"
"tourGuide": {
"takeATour": "查看更新內容",
"start": "開始",
"next": "下一個",
"welcomeTours": "歡迎使用 Node-RED"
},
"diagnostics": {
"title": "系统信息"
},
"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

@@ -137,7 +137,7 @@
},
"$sort": {
"args": "array [, function]",
"desc": "輸出排序後的陣列`array`。\n\n如果使用了比較函數`function`,則下述兩個參數需要被指定。\n\n`function(left, right)`\n\n該比較函數是為了比較leftright兩個值而被排序演算法調用的。如果使用者希望left的值被置於right的值之後那麼該函數必須輸出布林值`true`來表示位置交換。而在不需要位置交換時函數必須輸出`false`。"
"desc": "輸出排序後的陣列`array`。\n\n如果使用了比較函數`function`,則下述兩個參數需要被指定。\n\n`function(left, right)`\n\n該比較函數是為了比較`left`和`right`兩個值而被排序演算法調用的。如果使用者希望left的值被置於`right`的值之後,那麼該函數必須輸出布林值`true`來表示位置交換。而在不需要位置交換時函數必須輸出`false`。"
},
"$reverse": {
"args": "array",
@@ -237,7 +237,7 @@
},
"$assert": {
"args": "arg, str",
"desc": "如果`arg`為真,則該函數返回。 如果arg為假則拋出帶有str的異常作為異常消息。"
"desc": "如果`arg`為真,則該函數返回。 如果`arg`為假,則拋出帶有`str`的異常作為異常消息。"
},
"$single": {
"args": "array, function",
@@ -253,11 +253,11 @@
},
"$decodeUrlComponent": {
"args": "str",
"desc": "解碼以前由encodeUrlComponent創建的統一資源定位器URL組件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
"desc": "解碼以前由encodeUrlComponent創建的統一資源定位器URL組件。\n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "解碼先前由encodeUrl創建的統一資源定位符URL \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
"desc": "解碼先前由encodeUrl創建的統一資源定位符URL。\n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
@@ -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.0.2",
"version": "4.0.4",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -26,6 +26,15 @@ RED.comms = (function() {
var reconnectAttempts = 0;
var active = false;
RED.events.on('login', function(username) {
// User has logged in
// Need to upgrade the connection to be authenticated
if (ws && ws.readyState == 1) {
const auth_tokens = RED.settings.get("auth-tokens");
ws.send(JSON.stringify({auth:auth_tokens.access_token}))
}
})
function connectWS() {
active = true;
var wspath;
@@ -56,6 +65,7 @@ RED.comms = (function() {
ws.send(JSON.stringify({subscribe:t}));
}
}
emit('connect')
}
ws = new WebSocket(wspath);
@@ -180,9 +190,53 @@ RED.comms = (function() {
}
}
function send(topic, msg) {
if (ws && ws.readyState == 1) {
ws.send(JSON.stringify({
topic,
data: msg
}))
}
}
const eventHandlers = {};
function on(evt,func) {
eventHandlers[evt] = eventHandlers[evt]||[];
eventHandlers[evt].push(func);
}
function off(evt,func) {
const handler = eventHandlers[evt];
if (handler) {
for (let i=0;i<handler.length;i++) {
if (handler[i] === func) {
handler.splice(i,1);
return;
}
}
}
}
function emit() {
const evt = arguments[0]
const args = Array.prototype.slice.call(arguments,1);
if (eventHandlers[evt]) {
let cpyHandlers = [...eventHandlers[evt]];
for (let i=0;i<cpyHandlers.length;i++) {
try {
cpyHandlers[i].apply(null, args);
} catch(err) {
console.warn("RED.comms.emit error: ["+evt+"] "+(err.toString()));
console.warn(err);
}
}
}
}
return {
connect: connectWS,
subscribe: subscribe,
unsubscribe:unsubscribe
unsubscribe:unsubscribe,
on,
off,
send
}
})();

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

@@ -14,7 +14,7 @@
* limitations under the License.
**/
/**
/**
* An API for undo / redo history buffer
* @namespace RED.history
*/
@@ -29,7 +29,14 @@ RED.history = (function() {
}
return RED.nodes.junction(id);
}
function ensureUnlocked(id, flowsToLock) {
const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null);
const isLocked = flow ? flow.locked : false;
if (flow && isLocked) {
flow.locked = false;
flowsToLock.add(flow)
}
}
function undoEvent(ev) {
var i;
var len;
@@ -59,18 +66,46 @@ RED.history = (function() {
t: 'replace',
config: RED.nodes.createCompleteNodeSet(),
changed: {},
rev: RED.nodes.version()
moved: {},
complete: true,
rev: RED.nodes.version(),
dirty: RED.nodes.dirty()
};
var selectedTab = RED.workspaces.active();
inverseEv.config.forEach(n => {
const node = RED.nodes.node(n.id)
if (node) {
inverseEv.changed[n.id] = node.changed
inverseEv.moved[n.id] = node.moved
}
})
RED.nodes.clear();
var imported = RED.nodes.import(ev.config);
// Clear all change flags from the import
RED.nodes.dirty(false);
const flowsToLock = new Set()
imported.nodes.forEach(function(n) {
if (ev.changed[n.id]) {
ensureUnlocked(n.z, flowsToLock)
n.changed = true;
inverseEv.changed[n.id] = true;
}
if (ev.moved[n.id]) {
ensureUnlocked(n.z, flowsToLock)
n.moved = true;
}
})
flowsToLock.forEach(flow => {
flow.locked = true
})
RED.nodes.version(ev.rev);
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.workspaces.show(selectedTab, true);
RED.sidebar.config.refresh();
} else {
var importMap = {};
ev.config.forEach(function(n) {
@@ -378,7 +413,8 @@ RED.history = (function() {
if (ev.addToGroup) {
RED.group.removeFromGroup(ev.addToGroup,ev.nodes.map(function(n) { return n.n }),false);
inverseEv.removeFromGroup = ev.addToGroup;
} else if (ev.removeFromGroup) {
}
if (ev.removeFromGroup) {
RED.group.addToGroup(ev.removeFromGroup,ev.nodes.map(function(n) { return n.n }));
inverseEv.addToGroup = ev.removeFromGroup;
}
@@ -421,6 +457,9 @@ RED.history = (function() {
ev.node[i] = ev.changes[i];
}
}
ev.node.dirty = true;
ev.node.changed = ev.changed;
var eventType;
switch(ev.node.type) {
case 'tab': eventType = "flows"; break;
@@ -434,7 +473,9 @@ RED.history = (function() {
if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) {
$("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled);
}
if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('locked')) {
$("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!ev.node.locked);
}
if (ev.subflow) {
inverseEv.subflow = {};
@@ -509,8 +550,6 @@ RED.history = (function() {
inverseEv.links.push(ev.createdLinks[i]);
}
}
ev.node.dirty = true;
ev.node.changed = ev.changed;
} else if (ev.t == "createSubflow") {
inverseEv = {
t: "deleteSubflow",
@@ -646,6 +685,12 @@ RED.history = (function() {
ev.groups[i].nodes = [];
RED.nodes.addGroup(ev.groups[i]);
RED.group.addToGroup(ev.groups[i],nodes);
if (ev.groups[i].g) {
const parentGroup = RED.nodes.group(ev.groups[i].g)
if (parentGroup) {
RED.group.addToGroup(parentGroup, ev.groups[i])
}
}
}
}
} else if (ev.t == "addToGroup") {

View File

@@ -0,0 +1,561 @@
RED.multiplayer = (function () {
// activeSessionId - used to identify sessions across websocket reconnects
let activeSessionId
let headerWidget
// Map of session id to { session:'', user:{}, location:{}}
let sessions = {}
// Map of username to { user:{}, sessions:[] }
let users = {}
function addUserSession (session) {
if (sessions[session.session]) {
// This is an existing connection that has been authenticated
const existingSession = sessions[session.session]
if (existingSession.user.username !== session.user.username) {
removeUserHeaderButton(users[existingSession.user.username])
}
}
sessions[session.session] = session
const user = users[session.user.username] = users[session.user.username] || {
user: session.user,
sessions: []
}
if (session.user.profileColor === undefined) {
session.user.profileColor = (1 + Math.floor(Math.random() * 5))
}
session.location = session.location || {}
user.sessions.push(session)
if (session.session === activeSessionId) {
// This is the current user session - do not add a extra button for them
} else {
if (user.sessions.length === 1) {
if (user.button) {
clearTimeout(user.inactiveTimeout)
clearTimeout(user.removeTimeout)
user.button.removeClass('inactive')
} else {
addUserHeaderButton(user)
}
}
sessions[session.session].location = session.location
updateUserLocation(session.session)
}
}
function removeUserSession (sessionId, isDisconnected) {
removeUserLocation(sessionId)
const session = sessions[sessionId]
delete sessions[sessionId]
const user = users[session.user.username]
const i = user.sessions.indexOf(session)
user.sessions.splice(i, 1)
if (isDisconnected) {
removeUserHeaderButton(user)
} else {
if (user.sessions.length === 0) {
// Give the user 5s to reconnect before marking inactive
user.inactiveTimeout = setTimeout(() => {
user.button.addClass('inactive')
// Give the user further 20 seconds to reconnect before removing them
// from the user toolbar entirely
user.removeTimeout = setTimeout(() => {
removeUserHeaderButton(user)
}, 20000)
}, 5000)
}
}
}
function addUserHeaderButton (user) {
user.button = $('<li class="red-ui-multiplayer-user"><button type="button" class="red-ui-multiplayer-user-icon"></button></li>')
.attr('data-username', user.user.username)
.prependTo("#red-ui-multiplayer-user-list");
var button = user.button.find("button")
RED.popover.tooltip(button, user.user.username)
button.on('click', function () {
const location = user.sessions[0].location
revealUser(location)
})
const userProfile = RED.user.generateUserIcon(user.user)
userProfile.appendTo(button)
}
function removeUserHeaderButton (user) {
user.button.remove()
delete user.button
}
function getLocation () {
const location = {
workspace: RED.workspaces.active()
}
const editStack = RED.editor.getEditStack()
for (let i = editStack.length - 1; i >= 0; i--) {
if (editStack[i].id) {
location.node = editStack[i].id
break
}
}
if (isInWorkspace) {
const chart = $('#red-ui-workspace-chart')
const chartOffset = chart.offset()
const scaleFactor = RED.view.scale()
location.cursor = {
x: (lastPosition[0] - chartOffset.left + chart.scrollLeft()) / scaleFactor,
y: (lastPosition[1] - chartOffset.top + chart.scrollTop()) / scaleFactor
}
}
return location
}
let publishLocationTimeout
let lastPosition = [0,0]
let isInWorkspace = false
function publishLocation () {
if (!publishLocationTimeout) {
publishLocationTimeout = setTimeout(() => {
const location = getLocation()
if (location.workspace !== 0) {
log('send', 'multiplayer/location', location)
RED.comms.send('multiplayer/location', location)
}
publishLocationTimeout = null
}, 100)
}
}
function revealUser(location, skipWorkspace) {
if (location.node) {
// Need to check if this is a known node, so we can fall back to revealing
// the workspace instead
const node = RED.nodes.node(location.node)
if (node) {
RED.view.reveal(location.node)
} else if (!skipWorkspace && location.workspace) {
RED.view.reveal(location.workspace)
}
} else if (!skipWorkspace && location.workspace) {
RED.view.reveal(location.workspace)
}
}
const workspaceTrays = {}
function getWorkspaceTray(workspaceId) {
// console.log('get tray for',workspaceId)
if (!workspaceTrays[workspaceId]) {
const tray = $('<div class="red-ui-multiplayer-users-tray"></div>')
const users = []
const userIcons = {}
const userCountIcon = $(`<div class="red-ui-multiplayer-user-location"><span class="red-ui-user-profile red-ui-multiplayer-user-count"><span></span></span></div>`)
const userCountSpan = userCountIcon.find('span span')
userCountIcon.hide()
userCountSpan.text('')
userCountIcon.appendTo(tray)
const userCountTooltip = RED.popover.tooltip(userCountIcon, function () {
const content = $('<div>')
users.forEach(sessionId => {
$('<div>').append($('<a href="#">').text(sessions[sessionId].user.username).on('click', function (evt) {
evt.preventDefault()
revealUser(sessions[sessionId].location, true)
userCountTooltip.close()
})).appendTo(content)
})
return content
},
null,
true
)
const updateUserCount = function () {
const maxShown = 2
const children = tray.children()
children.each(function (index, element) {
const i = users.length - index
if (i > maxShown) {
$(this).hide()
} else if (i >= 0) {
$(this).show()
}
})
if (users.length < maxShown + 1) {
userCountIcon.hide()
} else {
userCountSpan.text('+'+(users.length - maxShown))
userCountIcon.show()
}
}
workspaceTrays[workspaceId] = {
attached: false,
tray,
users,
userIcons,
addUser: function (sessionId) {
if (users.indexOf(sessionId) === -1) {
// console.log(`addUser ws:${workspaceId} session:${sessionId}`)
users.push(sessionId)
const userLocationId = `red-ui-multiplayer-user-location-${sessionId}`
const userLocationIcon = $(`<div class="red-ui-multiplayer-user-location" id="${userLocationId}"></div>`)
RED.user.generateUserIcon(sessions[sessionId].user).appendTo(userLocationIcon)
userLocationIcon.prependTo(tray)
RED.popover.tooltip(userLocationIcon, sessions[sessionId].user.username)
userIcons[sessionId] = userLocationIcon
updateUserCount()
}
},
removeUser: function (sessionId) {
// console.log(`removeUser ws:${workspaceId} session:${sessionId}`)
const userLocationId = `red-ui-multiplayer-user-location-${sessionId}`
const index = users.indexOf(sessionId)
if (index > -1) {
users.splice(index, 1)
userIcons[sessionId].remove()
delete userIcons[sessionId]
}
updateUserCount()
},
updateUserCount
}
}
const trayDef = workspaceTrays[workspaceId]
if (!trayDef.attached) {
const workspaceTab = $(`#red-ui-tab-${workspaceId}`)
if (workspaceTab.length > 0) {
trayDef.attached = true
trayDef.tray.appendTo(workspaceTab)
trayDef.users.forEach(sessionId => {
trayDef.userIcons[sessionId].on('click', function (evt) {
revealUser(sessions[sessionId].location, true)
})
})
}
}
return workspaceTrays[workspaceId]
}
function attachWorkspaceTrays () {
let viewTouched = false
for (let sessionId of Object.keys(sessions)) {
const location = sessions[sessionId].location
if (location) {
if (location.workspace) {
getWorkspaceTray(location.workspace).updateUserCount()
}
if (location.node) {
addUserToNode(sessionId, location.node)
viewTouched = true
}
}
}
if (viewTouched) {
RED.view.redraw()
}
}
function addUserToNode(sessionId, nodeId) {
const node = RED.nodes.node(nodeId)
if (node) {
if (!node._multiplayer) {
node._multiplayer = {
users: [sessionId]
}
node._multiplayer_refresh = true
} else {
if (node._multiplayer.users.indexOf(sessionId) === -1) {
node._multiplayer.users.push(sessionId)
node._multiplayer_refresh = true
}
}
}
}
function removeUserFromNode(sessionId, nodeId) {
const node = RED.nodes.node(nodeId)
if (node && node._multiplayer) {
const i = node._multiplayer.users.indexOf(sessionId)
if (i > -1) {
node._multiplayer.users.splice(i, 1)
}
if (node._multiplayer.users.length === 0) {
delete node._multiplayer
} else {
node._multiplayer_refresh = true
}
}
}
function removeUserLocation (sessionId) {
updateUserLocation(sessionId, {})
removeUserCursor(sessionId)
}
function removeUserCursor (sessionId) {
// return
if (sessions[sessionId]?.cursor) {
sessions[sessionId].cursor.parentNode.removeChild(sessions[sessionId].cursor)
delete sessions[sessionId].cursor
}
}
function updateUserLocation (sessionId, location) {
let viewTouched = false
const oldLocation = sessions[sessionId].location
if (location) {
if (oldLocation.workspace !== location.workspace) {
// console.log('removing', sessionId, oldLocation.workspace)
workspaceTrays[oldLocation.workspace]?.removeUser(sessionId)
}
if (oldLocation.node !== location.node) {
removeUserFromNode(sessionId, oldLocation.node)
viewTouched = true
}
sessions[sessionId].location = location
} else {
location = sessions[sessionId].location
}
// console.log(`updateUserLocation sessionId:${sessionId} oldWS:${oldLocation?.workspace} newWS:${location.workspace}`)
if (location.workspace) {
getWorkspaceTray(location.workspace).addUser(sessionId)
if (location.cursor && location.workspace === RED.workspaces.active()) {
if (!sessions[sessionId].cursor) {
const user = sessions[sessionId].user
const cursorIcon = document.createElementNS("http://www.w3.org/2000/svg","g");
cursorIcon.setAttribute("class", "red-ui-multiplayer-annotation")
cursorIcon.appendChild(createAnnotationUser(user, true))
$(cursorIcon).css({
transform: `translate( ${location.cursor.x}px, ${location.cursor.y}px)`,
transition: 'transform 0.1s linear'
})
$("#red-ui-workspace-chart svg").append(cursorIcon)
sessions[sessionId].cursor = cursorIcon
} else {
const cursorIcon = sessions[sessionId].cursor
$(cursorIcon).css({
transform: `translate( ${location.cursor.x}px, ${location.cursor.y}px)`
})
}
} else if (sessions[sessionId].cursor) {
removeUserCursor(sessionId)
}
}
if (location.node) {
addUserToNode(sessionId, location.node)
viewTouched = true
}
if (viewTouched) {
RED.view.redraw()
}
}
// function refreshUserLocations () {
// for (const session of Object.keys(sessions)) {
// if (session !== activeSessionId) {
// updateUserLocation(session)
// }
// }
// }
function createAnnotationUser(user, pointer = false) {
const radius = 20
const halfRadius = radius/2
const group = document.createElementNS("http://www.w3.org/2000/svg","g");
const badge = document.createElementNS("http://www.w3.org/2000/svg","path");
let shapePath
if (!pointer) {
shapePath = `M 0 ${halfRadius} a ${halfRadius} ${halfRadius} 0 1 1 ${radius} 0 a ${halfRadius} ${halfRadius} 0 1 1 -${radius} 0 z`
} else {
shapePath = `M 0 0 h ${halfRadius} a ${halfRadius} ${halfRadius} 0 1 1 -${halfRadius} ${halfRadius} z`
}
badge.setAttribute('d', shapePath)
badge.setAttribute("class", "red-ui-multiplayer-annotation-background")
group.appendChild(badge)
if (user && user.profileColor !== undefined) {
badge.setAttribute("class", "red-ui-multiplayer-annotation-background red-ui-user-profile-color-" + user.profileColor)
}
if (user && user.image) {
const image = document.createElementNS("http://www.w3.org/2000/svg","image");
image.setAttribute("width", radius)
image.setAttribute("height", radius)
image.setAttribute("href", user.image)
image.setAttribute("clip-path", "circle("+Math.floor(radius/2)+")")
group.appendChild(image)
} else if (user && user.anonymous) {
const anonIconHead = document.createElementNS("http://www.w3.org/2000/svg","circle");
anonIconHead.setAttribute("cx", radius/2)
anonIconHead.setAttribute("cy", radius/2 - 2)
anonIconHead.setAttribute("r", 2.4)
anonIconHead.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
group.appendChild(anonIconHead)
const anonIconBody = document.createElementNS("http://www.w3.org/2000/svg","path");
anonIconBody.setAttribute("class","red-ui-multiplayer-annotation-anon-label");
// anonIconBody.setAttribute("d",`M ${radius/2 - 4} ${radius/2 + 1} h 8 v4 h -8 z`);
anonIconBody.setAttribute("d",`M ${radius/2} ${radius/2 + 5} h -2.5 c -2 1 -2 -5 0.5 -4.5 c 2 1 2 1 4 0 c 2.5 -0.5 2.5 5.5 0 4.5 z`);
group.appendChild(anonIconBody)
} else {
const labelText = user.username ? user.username.substring(0,2) : user
const label = document.createElementNS("http://www.w3.org/2000/svg","text");
if (user.username) {
label.setAttribute("class","red-ui-multiplayer-annotation-label");
label.textContent = user.username.substring(0,2)
} else {
label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count")
label.textContent = user
}
label.setAttribute("text-anchor", "middle")
label.setAttribute("x",radius/2);
label.setAttribute("y",radius/2 + 3);
group.appendChild(label)
}
const border = document.createElementNS("http://www.w3.org/2000/svg","path");
border.setAttribute('d', shapePath)
border.setAttribute("class", "red-ui-multiplayer-annotation-border")
group.appendChild(border)
return group
}
return {
init: function () {
RED.view.annotations.register("red-ui-multiplayer",{
type: 'badge',
align: 'left',
class: "red-ui-multiplayer-annotation",
show: "_multiplayer",
refresh: "_multiplayer_refresh",
element: function(node) {
const containerGroup = document.createElementNS("http://www.w3.org/2000/svg","g");
containerGroup.setAttribute("transform","translate(0,-4)")
if (node._multiplayer) {
let y = 0
for (let i = Math.min(1, node._multiplayer.users.length - 1); i >= 0; i--) {
const user = sessions[node._multiplayer.users[i]].user
const group = createAnnotationUser(user)
group.setAttribute("transform","translate("+y+",0)")
y += 15
containerGroup.appendChild(group)
}
if (node._multiplayer.users.length > 2) {
const group = createAnnotationUser('+'+(node._multiplayer.users.length - 2))
group.setAttribute("transform","translate("+y+",0)")
y += 12
containerGroup.appendChild(group)
}
}
return containerGroup;
},
tooltip: node => { return node._multiplayer.users.map(u => sessions[u].user.username).join('\n') }
});
// activeSessionId = RED.settings.getLocal('multiplayer:sessionId')
// if (!activeSessionId) {
activeSessionId = RED.nodes.id()
// RED.settings.setLocal('multiplayer:sessionId', activeSessionId)
// log('Session ID (new)', activeSessionId)
// } else {
log('Session ID', activeSessionId)
// }
headerWidget = $('<li><ul id="red-ui-multiplayer-user-list"></ul></li>').prependTo('.red-ui-header-toolbar')
RED.comms.on('connect', () => {
const location = getLocation()
const connectInfo = {
session: activeSessionId
}
if (location.workspace !== 0) {
connectInfo.location = location
}
RED.comms.send('multiplayer/connect', connectInfo)
})
RED.comms.subscribe('multiplayer/#', (topic, msg) => {
log('recv', topic, msg)
if (topic === 'multiplayer/init') {
// We have just reconnected, runtime has sent state to
// initialise the world
sessions = {}
users = {}
$('#red-ui-multiplayer-user-list').empty()
msg.sessions.forEach(session => {
addUserSession(session)
})
} else if (topic === 'multiplayer/connection-added') {
addUserSession(msg)
} else if (topic === 'multiplayer/connection-removed') {
removeUserSession(msg.session, msg.disconnected)
} else if (topic === 'multiplayer/location') {
const session = msg.session
delete msg.session
updateUserLocation(session, msg)
}
})
RED.events.on('workspace:change', (event) => {
getWorkspaceTray(event.workspace)
publishLocation()
})
RED.events.on('editor:open', () => {
publishLocation()
})
RED.events.on('editor:close', () => {
publishLocation()
})
RED.events.on('editor:change', () => {
publishLocation()
})
RED.events.on('login', () => {
publishLocation()
})
RED.events.on('flows:loaded', () => {
attachWorkspaceTrays()
})
RED.events.on('workspace:close', (event) => {
// A subflow tab has been closed. Need to mark its tray as detached
if (workspaceTrays[event.workspace]) {
workspaceTrays[event.workspace].attached = false
}
})
RED.events.on('logout', () => {
const disconnectInfo = {
session: activeSessionId
}
RED.comms.send('multiplayer/disconnect', disconnectInfo)
RED.settings.removeLocal('multiplayer:sessionId')
})
const chart = $('#red-ui-workspace-chart')
chart.on('mousemove', function (evt) {
lastPosition[0] = evt.clientX
lastPosition[1] = evt.clientY
publishLocation()
})
chart.on('scroll', function (evt) {
publishLocation()
})
chart.on('mouseenter', function () {
isInWorkspace = true
publishLocation()
})
chart.on('mouseleave', function () {
isInWorkspace = false
publishLocation()
})
}
}
function log() {
if (RED.multiplayer.DEBUG) {
console.log('[multiplayer]', ...arguments)
}
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
RED.plugins = (function() {
var plugins = {};
var pluginsByType = {};
var moduleList = {};
function registerPlugin(id,definition) {
plugins[id] = definition;
@@ -38,9 +39,43 @@ RED.plugins = (function() {
function getPluginsByType(type) {
return pluginsByType[type] || [];
}
function setPluginList(list) {
for(let i=0;i<list.length;i++) {
let p = list[i];
addPlugin(p);
}
}
function addPlugin(p) {
moduleList[p.module] = moduleList[p.module] || {
name:p.module,
version:p.version,
local:p.local,
sets:{},
plugin: true,
id: p.id
};
if (p.pending_version) {
moduleList[p.module].pending_version = p.pending_version;
}
moduleList[p.module].sets[p.name] = p;
RED.events.emit("registry:plugin-module-added",p.module);
}
function getModule(module) {
return moduleList[module];
}
return {
registerPlugin: registerPlugin,
getPlugin: getPlugin,
getPluginsByType: getPluginsByType
getPluginsByType: getPluginsByType,
setPluginList: setPluginList,
addPlugin: addPlugin,
getModule: getModule
}
})();

View File

@@ -25,6 +25,7 @@ var RED = (function() {
cache: false,
url: 'plugins',
success: function(data) {
RED.plugins.setPluginList(data);
loader.reportProgress(RED._("event.loadPlugins"), 13)
RED.i18n.loadPluginCatalogs(function() {
loadPlugins(function() {
@@ -249,8 +250,37 @@ var RED = (function() {
RED.nodes.import(nodes.flows);
RED.nodes.dirty(false);
RED.view.redraw(true);
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6),true);
if (/^#(flow|node|group)\/.+$/.test(currentHash)) {
const hashParts = currentHash.split('/')
const showEditDialog = hashParts.length > 2 && hashParts[2] === 'edit'
if (hashParts[0] === '#flow') {
RED.workspaces.show(hashParts[1], true);
if (showEditDialog) {
RED.workspaces.edit()
}
} else if (hashParts[0] === '#node') {
const nodeToShow = RED.nodes.node(hashParts[1])
if (nodeToShow) {
setTimeout(() => {
RED.view.reveal(nodeToShow.id)
window.location.hash = currentHash
RED.view.select(nodeToShow.id)
if (showEditDialog) {
RED.editor.edit(nodeToShow)
}
}, 50)
}
} else if (hashParts[0] === '#group') {
const nodeToShow = RED.nodes.group(hashParts[1])
if (nodeToShow) {
RED.view.reveal(nodeToShow.id)
window.location.hash = currentHash
RED.view.select(nodeToShow.id)
if (showEditDialog) {
RED.editor.editGroup(nodeToShow)
}
}
}
}
if (RED.workspaces.count() > 0) {
const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
@@ -268,6 +298,7 @@ var RED = (function() {
RED.workspaces.show(workspaces[0]);
}
}
RED.events.emit('flows:loaded')
} catch(err) {
console.warn(err);
RED.notify(
@@ -321,6 +352,8 @@ var RED = (function() {
loader.end()
RED.notify($("<p>").text(message));
RED.sidebar.info.refresh()
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
});
});
return;
@@ -467,6 +500,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);
@@ -494,6 +536,45 @@ var RED = (function() {
RED.view.redrawStatus(node);
}
});
RED.comms.subscribe("notification/plugin/#",function(topic,msg) {
if (topic == "notification/plugin/added") {
RED.settings.refreshSettings(function(err, data) {
let addedPlugins = [];
msg.forEach(function(m) {
let id = m.id;
RED.plugins.addPlugin(m);
m.plugins.forEach((p) => {
addedPlugins.push(p.id);
})
RED.i18n.loadNodeCatalog(id, function() {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'plugins/'+id,
success: function(data) {
appendPluginConfig(data);
}
});
});
});
if (addedPlugins.length) {
let pluginList = "<ul><li>"+addedPlugins.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
// ToDo: Adapt notification (node -> plugin)
RED.notify(RED._("palette.event.nodeAdded", {count:addedPlugins.length})+pluginList,"success");
}
})
}
});
let pendingNodeRemovedNotifications = []
let pendingNodeRemovedTimeout
RED.comms.subscribe("notification/node/#",function(topic,msg) {
var i,m;
var typeList;
@@ -531,8 +612,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();
@@ -641,11 +729,6 @@ var RED = (function() {
]});
menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
null,
{id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
{id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
{id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
@@ -655,7 +738,12 @@ var RED = (function() {
{id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"},
null,
{id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"},
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"}
{id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"},
null,
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"}
]});
menuOptions.push(null);
@@ -671,7 +759,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: [
@@ -700,7 +788,7 @@ var RED = (function() {
}
menuOptions.push({id:"menu-item-help",
label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
href: RED.settings.theme("menu.menu-item-help.url","https://nodered.org/docs")
});
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
@@ -748,9 +836,14 @@ var RED = (function() {
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.init(buildMainMenu);
RED.envVar.init();
RED.nodes.init();
RED.runtime.init()
if (RED.settings.theme("multiplayer.enabled",false)) {
RED.multiplayer.init()
}
RED.comms.connect();
$("#red-ui-main-container").show();

View File

@@ -33,8 +33,8 @@ RED.settings = (function () {
if (!hasLocalStorage()) {
return;
}
if (key === "auth-tokens") {
localStorage.setItem(key, JSON.stringify(value));
if (key.startsWith("auth-tokens")) {
localStorage.setItem(key+this.authTokensSuffix, JSON.stringify(value));
} else {
RED.utils.setMessageProperty(userSettings,key,value);
saveUserSettings();
@@ -52,8 +52,8 @@ RED.settings = (function () {
if (!hasLocalStorage()) {
return undefined;
}
if (key === "auth-tokens") {
return JSON.parse(localStorage.getItem(key));
if (key.startsWith("auth-tokens")) {
return JSON.parse(localStorage.getItem(key+this.authTokensSuffix));
} else {
var v;
try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {}
@@ -71,8 +71,8 @@ RED.settings = (function () {
if (!hasLocalStorage()) {
return;
}
if (key === "auth-tokens") {
localStorage.removeItem(key);
if (key.startsWith("auth-tokens")) {
localStorage.removeItem(key+this.authTokensSuffix);
} else {
delete userSettings[key];
saveUserSettings();
@@ -99,6 +99,8 @@ RED.settings = (function () {
var init = function (options, done) {
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
var path=window.location.pathname.slice(0,-1);
RED.settings.authTokensSuffix=path.replace(/\//g, '-');
if (accessTokenMatch) {
var accessToken = accessTokenMatch[1];
RED.settings.set("auth-tokens",{access_token: accessToken});

View File

@@ -47,7 +47,7 @@ RED.actionList = (function() {
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
searchInput = $('<input type="text" data-i18n="[placeholder]keyboard.filterActions">').appendTo(searchDiv).searchBox({
change: function() {
filterTerm = $(this).val().trim();
filterTerm = $(this).val().trim().toLowerCase();
filterTerms = filterTerm.split(" ");
searchResults.editableList('filter');
searchResults.find("li.selected").removeClass("selected");
@@ -205,7 +205,9 @@ RED.actionList = (function() {
}
function init() {
RED.actions.add("core:show-action-list",show);
if (RED.settings.theme("menu.menu-item-action-list", true)) {
RED.actions.add("core:show-action-list",show);
}
RED.events.on("editor:open",function() { disabled = true; });
RED.events.on("editor:close",function() { disabled = false; });

View File

@@ -26,6 +26,7 @@ RED.clipboard = (function() {
var currentPopoverError;
var activeTab;
var libraryBrowser;
var clipboardTabs;
var activeLibraries = {};
@@ -37,13 +38,13 @@ RED.clipboard = (function() {
// IE11 workaround
// IE does not support data uri scheme for downloading data
var blob = new Blob([data], {
type: "data:text/plain;charset=utf-8"
type: "data:application/json;charset=utf-8"
});
navigator.msSaveBlob(blob, file);
}
else {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(data));
element.setAttribute('download', file);
element.style.display = 'none';
document.body.appendChild(element);
@@ -215,6 +216,13 @@ RED.clipboard = (function() {
open: function( event, ui ) {
RED.keyboard.disable();
},
beforeClose: function(e) {
if (clipboardTabs && activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
const jsonTabIndex = clipboardTabs.getTabIndex('red-ui-clipboard-dialog-export-tab-clipboard-json')
const activeTabIndex = clipboardTabs.activeIndex()
RED.settings.set("editor.dialog.export.json-view", activeTabIndex === jsonTabIndex )
}
},
close: function(e) {
RED.keyboard.enable();
if (popover) {
@@ -228,12 +236,23 @@ RED.clipboard = (function() {
exportNodesDialog =
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
'<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'<div style="display: flex; justify-content: space-between;">'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.export"></label>'+
'<span id="red-ui-clipboard-dialog-export-rng-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-rng-selected" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-flow" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="red-ui-clipboard-dialog-export-rng-full" class="red-ui-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'</div>'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="common.label.format"></label>'+
'<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
'<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
'</span>'+
'</div>'+
'</div>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-box">'+
'<div class="red-ui-clipboard-dialog-tabs">'+
@@ -248,15 +267,9 @@ RED.clipboard = (function() {
'<div id="red-ui-clipboard-dialog-export-tab-clipboard-preview-list"></div>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-json">'+
'<div class="form-row" style="height:calc(100% - 40px)">'+
'<div class="form-row" style="height:calc(100% - 10px)">'+
'<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
'</div>'+
'<div class="form-row" style="text-align: right;">'+
'<span id="red-ui-clipboard-dialog-export-fmt-group" class="button-group">'+
'<a id="red-ui-clipboard-dialog-export-fmt-mini" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
'<a id="red-ui-clipboard-dialog-export-fmt-full" class="red-ui-button red-ui-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
'</span>'+
'</div>'+
'</div>'+
'</div>'+
'<div class="form-row" id="red-ui-clipboard-dialog-export-tab-library-filename">'+
@@ -423,11 +436,10 @@ RED.clipboard = (function() {
}
}
function showImportNodes(mode) {
function showImportNodes(library = 'clipboard') {
if (disabled) {
return;
}
mode = mode || "clipboard";
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
@@ -504,7 +516,7 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
if (RED.workspaces.active() === 0) {
if (RED.workspaces.active() === 0 || RED.workspaces.isLocked()) {
$("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
$("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
} else {
@@ -533,8 +545,8 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-import-file-upload").trigger("click");
})
tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+mode);
if (mode === 'clipboard') {
tabs.activateTab("red-ui-clipboard-dialog-import-tab-"+library);
if (library === 'clipboard') {
setTimeout(function() {
$("#red-ui-clipboard-dialog-import-text").trigger("focus");
},100)
@@ -558,16 +570,19 @@ RED.clipboard = (function() {
});
}
function showExportNodes(mode) {
/**
* Show the export dialog
* @params library which export destination to show
* @params mode whether to default to 'auto' (default) or 'flow'
**/
function showExportNodes(library = 'clipboard', mode = 'auto' ) {
if (disabled) {
return;
}
mode = mode || "clipboard";
dialogContainer.empty();
dialogContainer.append($(exportNodesDialog));
clipboardTabs = null
var tabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tabs",
vertical: true,
@@ -628,7 +643,7 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
var clipboardTabs = RED.tabs.create({
clipboardTabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
onchange: function(tab) {
$(".red-ui-clipboard-dialog-export-tab-clipboard-tab").hide();
@@ -645,6 +660,9 @@ RED.clipboard = (function() {
id: "red-ui-clipboard-dialog-export-tab-clipboard-json",
label: RED._("editor.types.json")
});
if (RED.settings.get("editor.dialog.export.json-view") === true) {
clipboardTabs.activateTab("red-ui-clipboard-dialog-export-tab-clipboard-json");
}
var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: []
@@ -654,7 +672,12 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select();
dialogContainer.i18n();
var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
const userFormat = RED.settings.get("editor.dialog.export.pretty")
if (userFormat === false || userFormat === true) {
format = userFormat ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
}
$("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) {
evt.preventDefault();
@@ -670,7 +693,8 @@ RED.clipboard = (function() {
var nodes = JSON.parse(flow);
format = $(this).attr('id');
if (format === 'red-ui-clipboard-dialog-export-fmt-full') {
const pretty = format === "red-ui-clipboard-dialog-export-fmt-full";
if (pretty) {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
@@ -679,6 +703,7 @@ RED.clipboard = (function() {
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50);
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
RED.settings.set("editor.dialog.export.pretty", pretty)
}
});
@@ -722,7 +747,7 @@ RED.clipboard = (function() {
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
} else if (type === 'full') {
nodes = RED.nodes.createCompleteNodeSet(false);
nodes = RED.nodes.createCompleteNodeSet({ credentials: false });
}
if (nodes !== null) {
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
@@ -766,12 +791,15 @@ RED.clipboard = (function() {
}
}
}
if (mode === 'flow' && !$("#red-ui-clipboard-dialog-export-rng-flow").hasClass('disabled')) {
$("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
}
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
$("#red-ui-clipboard-dialog-export-fmt-full").trigger("click");
} else {
$("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click");
}
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode);
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+library);
var dialogHeight = 400;
var winHeight = $(window).height();
@@ -807,7 +835,7 @@ RED.clipboard = (function() {
flow.forEach(function(node) {
if (node.type === "tab") {
flows[node.id] = {
element: getFlowLabel(node,false),
element: getFlowLabel(node),
deferBuild: type !== "flow",
expanded: type === "flow",
children: []
@@ -988,7 +1016,6 @@ RED.clipboard = (function() {
try {
RED.view.importNodes(newNodes, importOptions);
} catch(error) {
console.log(error.importConfig)
// Thrown for import_conflict
confirmImport(error.importConfig, newNodes, importOptions);
}
@@ -1158,9 +1185,9 @@ RED.clipboard = (function() {
function getNodeElement(n, isConflicted, isSelected, parent) {
var element;
if (n.type === "tab") {
element = getFlowLabel(n, isSelected);
element = getFlowLabel(n, isConflicted);
} else {
element = getNodeLabel(n, isConflicted, isSelected);
element = getNodeLabel(n, isConflicted, isSelected, parent);
}
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
controls.on("click", function(evt) { evt.stopPropagation(); });
@@ -1210,14 +1237,14 @@ RED.clipboard = (function() {
}
}
function getFlowLabel(n) {
function getFlowLabel(n, isConflicted) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow red-ui-node-list-item"});
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
var label = (typeof n === "string")? n : n.label;
var newlineIndex = label.indexOf("\\n");
@@ -1225,11 +1252,17 @@ RED.clipboard = (function() {
label = label.substring(0,newlineIndex)+"...";
}
contentDiv.text(label);
if (!!isConflicted) {
const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
}
// A conflicted flow should not be imported by default.
return div;
}
function getNodeLabel(n, isConflicted) {
function getNodeLabel(n, isConflicted, isSelected, parent) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
@@ -1237,6 +1270,11 @@ RED.clipboard = (function() {
}
var div = $('<div>',{class:"red-ui-node-list-item"});
RED.utils.createNodeIcon(n,true).appendTo(div);
if (!parent && !!isConflicted) {
const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
}
return div;
}
@@ -1266,15 +1304,17 @@ RED.clipboard = (function() {
RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget);
$('#red-ui-workspace-chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
if (!RED.workspaces.isLocked() && (
$.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1)) {
$("#red-ui-drop-target").css({display:'table'}).focus();
}
});
$('#red-ui-drop-target').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
$.inArray("Files",event.originalEvent.dataTransfer.types) != -1 ||
RED.workspaces.isLocked()) {
event.preventDefault();
}
})
@@ -1282,27 +1322,29 @@ RED.clipboard = (function() {
hideDropTarget();
})
.on("drop",function(event) {
try {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
if (!RED.workspaces.isLocked()) {
try {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
}
}
} catch(err) {
// Ensure any errors throw above doesn't stop the drop target from
// being hidden.
}
} catch(err) {
// Ensure any errors throw above doesn't stop the drop target from
// being hidden.
}
hideDropTarget();
event.preventDefault();

View File

@@ -174,12 +174,24 @@
this.uiContainer.width(m[1]);
}
if (this.options.sortable) {
var isCanceled = false; // Flag to track if an item has been canceled from being dropped into a different list
var noDrop = false; // Flag to track if an item is being dragged into a different list
var handle = (typeof this.options.sortable === 'string')?
this.options.sortable :
".red-ui-editableList-item-handle";
var sortOptions = {
axis: "y",
update: function( event, ui ) {
// dont trigger update if the item is being canceled
const targetList = $(event.target);
const draggedItem = ui.item;
const draggedItemParent = draggedItem.parent();
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
noDrop = true;
}
if (isCanceled || noDrop) {
return;
}
if (that.options.sortItems) {
that.options.sortItems(that.items());
}
@@ -189,8 +201,32 @@
tolerance: "pointer",
forcePlaceholderSize:true,
placeholder: "red-ui-editabelList-item-placeholder",
start: function(e, ui){
ui.placeholder.height(ui.item.height()-4);
start: function (event, ui) {
isCanceled = false;
ui.placeholder.height(ui.item.height() - 4);
ui.item.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
},
stop: function (event, ui) {
ui.item.css('cursor', 'auto');
},
receive: function (event, ui) {
if (ui.item.hasClass("red-ui-editableList-item-constrained")) {
isCanceled = true;
$(ui.sender).sortable('cancel');
}
},
over: function (event, ui) {
// if the dragged item is constrained, prevent it from being dropped into a different list
const targetList = $(event.target);
const draggedItem = ui.item;
const draggedItemParent = draggedItem.parent();
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
noDrop = true;
draggedItem.css('cursor', 'no-drop'); // TODO: this doesn't seem to work, use a class instead?
} else {
noDrop = false;
draggedItem.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
}
}
};
if (this.options.connectWith) {
@@ -417,6 +453,9 @@
} else {
return null;
}
},
cancel: function() {
this.element.sortable("cancel");
}
});
})(jQuery);

View File

@@ -94,8 +94,8 @@ RED.menu = (function() {
var link = $(linkContent).appendTo(item);
opt.link = link;
if (typeof opt.onselect === 'string') {
var shortcut = RED.keyboard.getShortcut(opt.onselect);
if (typeof opt.onselect === 'string' || opt.shortcut) {
var shortcut = opt.shortcut || RED.keyboard.getShortcut(opt.onselect);
if (shortcut && shortcut.key) {
opt.shortcutSpan = $('<span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span>').appendTo(link.find(".red-ui-menu-label"));
}

View File

@@ -211,7 +211,7 @@ RED.popover = (function() {
closePopup(true);
});
}
if (trigger === 'hover' && options.interactive) {
if (/*trigger === 'hover' && */options.interactive) {
div.on('mouseenter', function(e) {
clearTimeout(timer);
active = true;
@@ -445,9 +445,12 @@ RED.popover = (function() {
return {
create: createPopover,
tooltip: function(target,content, action) {
tooltip: function(target,content, action, interactive) {
var label = function() {
var label = content;
if (typeof content === 'function') {
label = content()
}
if (action) {
var shortcut = RED.keyboard.getShortcut(action);
if (shortcut && shortcut.key) {
@@ -463,6 +466,7 @@ RED.popover = (function() {
size: "small",
direction: "bottom",
content: label,
interactive,
delay: { show: 750, hide: 50 }
});
popover.setContent = function(newContent) {

View File

@@ -141,7 +141,29 @@ RED.tabs = (function() {
})
}
if (options.contextmenu) {
wrapper.on('contextmenu', function(evt) {
let clickedTab
let target = evt.target
while(target.nodeName !== 'A' && target.nodeName !== 'UL' && target.nodeName !== 'BODY') {
target = target.parentNode
}
if (target.nodeName === 'A') {
const href = target.getAttribute('href')
if (href) {
clickedTab = tabs[href.slice(1)]
}
}
evt.preventDefault()
evt.stopPropagation()
RED.contextMenu.show({
x:evt.clientX-5,
y:evt.clientY-5,
options: options.contextmenu(clickedTab)
})
return false
})
}
var scrollLeft;
var scrollRight;
@@ -161,7 +183,7 @@ RED.tabs = (function() {
// Assume this is wheel event which might not trigger
// the scroll event, so do things manually
var sl = scrollContainer.scrollLeft();
sl -= evt.originalEvent.deltaY;
sl += evt.originalEvent.deltaY;
scrollContainer.scrollLeft(sl);
}
})
@@ -343,7 +365,10 @@ RED.tabs = (function() {
var thisTabA = thisTab.find("a");
if (options.onclick) {
options.onclick(tabs[thisTabA.attr('href').slice(1)]);
options.onclick(tabs[thisTabA.attr('href').slice(1)], evt);
if (evt.isDefaultPrevented() && evt.isPropagationStopped()) {
return false
}
}
activateTab(thisTabA);
if (fireSelectionChanged) {
@@ -526,6 +551,8 @@ RED.tabs = (function() {
ul.find("li.red-ui-tab a")
.on("mousedown", function(evt) { mousedownTab = evt.currentTarget })
.on("mouseup",onTabClick)
// prevent browser-default middle-click behaviour
.on("auxclick", function(evt) { evt.preventDefault() })
.on("click", function(evt) {evt.preventDefault(); })
.on("dblclick", function(evt) {evt.stopPropagation(); evt.preventDefault(); })
@@ -794,6 +821,8 @@ RED.tabs = (function() {
}
link.on("mousedown", function(evt) { mousedownTab = evt.currentTarget })
link.on("mouseup",onTabClick);
// prevent browser-default middle-click behaviour
link.on("auxclick", function(evt) { evt.preventDefault() })
link.on("click", function(evt) { evt.preventDefault(); })
link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); })
@@ -807,23 +836,22 @@ RED.tabs = (function() {
event.preventDefault();
removeTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
}
if (tab.hideable) {
li.addClass("red-ui-tabs-closeable")
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
closeLink.append('<i class="fa fa-eye" />');
closeLink.append('<i class="fa fa-eye-slash" />');
closeLink.on("click",function(event) {
event.preventDefault();
hideTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
RED.popover.tooltip(closeLink,RED._("workspace.closeFlow"));
}
// if (tab.hideable) {
// li.addClass("red-ui-tabs-closeable")
// var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close red-ui-tab-hide"}).appendTo(li);
// closeLink.append('<i class="fa fa-eye" />');
// closeLink.append('<i class="fa fa-eye-slash" />');
// closeLink.on("click",function(event) {
// event.preventDefault();
// hideTab(tab.id);
// });
// RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
// }
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
if (options.onselect) {
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
$('<i class="red-ui-tabs-badge-selected fa fa-check-circle"></i>').appendTo(badges);
}
@@ -938,6 +966,9 @@ RED.tabs = (function() {
activeIndex: function() {
return ul.find("li.active").index()
},
getTabIndex: function (id) {
return ul.find("a[href='#"+id+"']").parent().index()
},
contains: function(id) {
return ul.find("a[href='#"+id+"']").length > 0;
},

View File

@@ -54,25 +54,26 @@
return icon;
}
var autoComplete = function(options) {
function getMatch(value, searchValue) {
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
const len = idx > -1 ? searchValue.length : 0;
return {
index: idx,
found: idx > -1,
pre: value.substring(0,idx),
match: value.substring(idx,idx+len),
post: value.substring(idx+len),
}
}
function generateSpans(match) {
const els = [];
if(match.pre) { els.push($('<span/>').text(match.pre)); }
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
if(match.post) { els.push($('<span/>').text(match.post)); }
return els;
function getMatch(value, searchValue) {
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
const len = idx > -1 ? searchValue.length : 0;
return {
index: idx,
found: idx > -1,
pre: value.substring(0,idx),
match: value.substring(idx,idx+len),
post: value.substring(idx+len),
}
}
function generateSpans(match) {
const els = [];
if(match.pre) { els.push($('<span/>').text(match.pre)); }
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
if(match.post) { els.push($('<span/>').text(match.post)); }
return els;
}
const msgAutoComplete = function(options) {
return function(val) {
var matches = [];
options.forEach(opt => {
@@ -102,6 +103,197 @@
}
}
function getEnvVars (obj, envVars = {}) {
contextKnownKeys.env = contextKnownKeys.env || {}
if (contextKnownKeys.env[obj.id]) {
return contextKnownKeys.env[obj.id]
}
let parent
if (obj.type === 'tab' || obj.type === 'subflow') {
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
parent = conf;
}
})
} else if (obj.g) {
parent = RED.nodes.group(obj.g)
} else if (obj.z) {
parent = RED.nodes.workspace(obj.z) || RED.nodes.subflow(obj.z)
}
if (parent) {
getEnvVars(parent, envVars)
}
if (obj.env) {
obj.env.forEach(env => {
envVars[env.name] = obj
})
}
contextKnownKeys.env[obj.id] = envVars
return envVars
}
const envAutoComplete = function (val) {
const editStack = RED.editor.getEditStack()
if (editStack.length === 0) {
done([])
return
}
const editingNode = editStack.pop()
if (!editingNode) {
return []
}
const envVarsMap = getEnvVars(editingNode)
const envVars = Object.keys(envVarsMap)
const matches = []
const i = val.lastIndexOf('${')
let searchKey = val
let isSubkey = false
if (i > -1) {
if (val.lastIndexOf('}') < i) {
searchKey = val.substring(i+2)
isSubkey = true
}
}
envVars.forEach(v => {
let valMatch = getMatch(v, searchKey);
if (valMatch.found) {
const optSrc = envVarsMap[v]
const element = $('<div>',{style: "display: flex"});
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
valEl.append(generateSpans(valMatch))
valEl.appendTo(element)
if (optSrc) {
const optEl = $('<div>').css({ "font-size": "0.8em" });
let label
if (optSrc.type === 'global-config') {
label = RED._('sidebar.context.global')
} else if (optSrc.type === 'group') {
label = RED.utils.getNodeLabel(optSrc) || (RED._('sidebar.info.group') + ': '+optSrc.id)
} else {
label = RED.utils.getNodeLabel(optSrc) || optSrc.id
}
optEl.append(generateSpans({ match: label }));
optEl.appendTo(element);
}
matches.push({
value: isSubkey ? val + v + '}' : v,
label: element,
i: valMatch.index
});
}
})
matches.sort(function(A,B){return A.i-B.i})
return matches
}
let contextKnownKeys = {}
let contextCache = {}
if (RED.events) {
RED.events.on("editor:close", function () {
contextCache = {}
contextKnownKeys = {}
});
}
const contextAutoComplete = function() {
const that = this
const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
contextKnownKeys[scope] = contextKnownKeys[scope] || {}
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
if (searchKey.length > 0) {
try {
RED.utils.normalisePropertyExpression(searchKey)
} catch (err) {
// Not a valid context key, so don't try looking up
done()
return
}
}
const url = `context/${scope}/${encodeURIComponent(searchKey)}?store=${store}&keysOnly`
if (contextCache[url]) {
// console.log('CACHED', url)
done()
} else {
// console.log('GET', url)
$.getJSON(url, function(data) {
// console.log(data)
contextCache[url] = true
const result = data[store] || {}
const keys = result.keys || []
const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
keys.forEach(key => {
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
contextKnownKeys[scope][store].add(keyPrefix + key)
} else {
contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
}
})
done()
})
}
}
const getContextKeys = function(key, done) {
const keyParts = key.split('.')
const partialKey = keyParts.pop()
let scope = that.propertyType
if (scope === 'flow') {
// Get the flow id of the node we're editing
const editStack = RED.editor.getEditStack()
if (editStack.length === 0) {
done([])
return
}
const editingNode = editStack.pop()
if (editingNode.z) {
scope = `${scope}/${editingNode.z}`
} else {
done([])
return
}
}
const store = (contextStoreOptions.length === 1) ? contextStoreOptions[0].value : that.optionValue
const searchKey = keyParts.join('.')
getContextKeysFromRuntime(scope, store, searchKey, function() {
if (contextKnownKeys[scope][store].has(key) || key.endsWith(']')) {
getContextKeysFromRuntime(scope, store, key, function() {
done(contextKnownKeys[scope][store])
})
}
done(contextKnownKeys[scope][store])
})
}
return function(val, done) {
getContextKeys(val, function (keys) {
const matches = []
keys.forEach(v => {
let optVal = v
let valMatch = getMatch(optVal, val);
if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
// Search key ends in '.' - but doesn't match. Check again
// with [" at the end instead so we match bracket notation
valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
}
if (valMatch.found) {
const element = $('<div>',{style: "display: flex"});
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
valEl.append(generateSpans(valMatch))
valEl.appendTo(element)
matches.push({
value: optVal,
label: element,
});
}
})
matches.sort(function(a, b) { return a.value.localeCompare(b.value) });
done(matches);
})
}
}
// This is a hand-generated list of completions for the core nodes (based on the node help html).
var msgCompletions = [
{ value: "payload" },
@@ -166,66 +358,93 @@
{ value: "_session", source: ["websocket out","tcp out"] },
]
var allOptions = {
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
flow: {value:"flow",label:"flow.",hasValue:true,
options:[],
validate:RED.utils.validatePropertyExpression,
msg: { value: "msg", label: "msg.", validate: RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions) },
flow: { value: "flow", label: "flow.", hasValue: true,
options: [],
validate: RED.utils.validatePropertyExpression,
parse: contextParse,
export: contextExport,
valueLabel: contextLabel
valueLabel: contextLabel,
autoComplete: contextAutoComplete
},
global: {value:"global",label:"global.",hasValue:true,
options:[],
validate:RED.utils.validatePropertyExpression,
global: {
value: "global", label: "global.", hasValue: true,
options: [],
validate: RED.utils.validatePropertyExpression,
parse: contextParse,
export: contextExport,
valueLabel: contextLabel
valueLabel: contextLabel,
autoComplete: contextAutoComplete
},
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]+)?$/},
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
str: { value: "str", label: "string", icon: "red/images/typedInput/az.svg" },
num: { value: "num", label: "number", icon: "red/images/typedInput/09.svg", validate: function (v, o) {
return RED.utils.validateTypedProperty(v, "num", o);
} },
bool: { value: "bool", label: "boolean", icon: "red/images/typedInput/bool.svg", options: ["true", "false"] },
json: {
value:"json",
label:"JSON",
icon:"red/images/typedInput/json.svg",
validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}},
expand: function() {
value: "json",
label: "JSON",
icon: "red/images/typedInput/json.svg",
validate: function (v, o) {
return RED.utils.validateTypedProperty(v, "json", o);
},
expand: function () {
var that = this;
var value = this.value();
try {
value = JSON.stringify(JSON.parse(value),null,4);
} catch(err) {
value = JSON.stringify(JSON.parse(value), null, 4);
} catch (err) {
}
RED.editor.editJSON({
value: value,
stateId: RED.editor.generateViewStateId("typedInput", that, "json"),
focus: true,
complete: function(v) {
complete: function (v) {
var value = v;
try {
value = JSON.stringify(JSON.parse(v));
} catch(err) {
} catch (err) {
}
that.value(value);
}
})
}
},
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
date: {value:"date",label:"timestamp",icon:"fa fa-clock-o",hasValue:false},
re: { value: "re", label: "regular expression", icon: "red/images/typedInput/re.svg" },
date: {
value: "date",
label: "timestamp",
icon: "fa fa-clock-o",
options: [
{
label: 'milliseconds since epoch',
value: ''
},
{
label: 'YYYY-MM-DDTHH:mm:ss.sssZ',
value: 'iso'
},
{
label: 'JavaScript Date Object',
value: 'object'
}
]
},
jsonata: {
value: "jsonata",
label: "expression",
icon: "red/images/typedInput/expr.svg",
validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}},
expand:function() {
validate: function (v, o) {
return RED.utils.validateTypedProperty(v, "jsonata", o);
},
expand: function () {
var that = this;
RED.editor.editExpression({
value: this.value().replace(/\t/g,"\n"),
value: this.value().replace(/\t/g, "\n"),
stateId: RED.editor.generateViewStateId("typedInput", that, "jsonata"),
focus: true,
complete: function(v) {
that.value(v.replace(/\n/g,"\t"));
complete: function (v) {
that.value(v.replace(/\n/g, "\t"));
}
})
}
@@ -234,13 +453,13 @@
value: "bin",
label: "buffer",
icon: "red/images/typedInput/bin.svg",
expand: function() {
expand: function () {
var that = this;
RED.editor.editBuffer({
value: this.value(),
stateId: RED.editor.generateViewStateId("typedInput", that, "bin"),
focus: true,
complete: function(v) {
complete: function (v) {
that.value(v);
}
})
@@ -249,15 +468,16 @@
env: {
value: "env",
label: "env variable",
icon: "red/images/typedInput/env.svg"
icon: "red/images/typedInput/env.svg",
autoComplete: envAutoComplete
},
node: {
value: "node",
label: "node",
icon: "red/images/typedInput/target.svg",
valueLabel: function(container,value) {
valueLabel: function (container, value) {
var node = RED.nodes.node(value);
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).css({
var nodeDiv = $('<div>', { class: "red-ui-search-result-node" }).css({
"margin-top": "2px",
"margin-left": "3px"
}).appendTo(container);
@@ -266,133 +486,190 @@
"margin-left": "6px"
}).appendTo(container);
if (node) {
var colour = RED.utils.getNodeColor(node.type,node._def);
var icon_url = RED.utils.getNodeIcon(node._def,node);
var colour = RED.utils.getNodeColor(node.type, node._def);
var icon_url = RED.utils.getNodeIcon(node._def, node);
if (node.type === 'tab') {
colour = "#C0DEED";
}
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
nodeDiv.css('backgroundColor', colour);
var iconContainer = $('<div/>', { class: "red-ui-palette-icon-container" }).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
var l = RED.utils.getNodeLabel(node,node.id);
var l = RED.utils.getNodeLabel(node, node.id);
nodeLabel.text(l);
} else {
nodeDiv.css({
'backgroundColor': '#eee',
'border-style' : 'dashed'
'border-style': 'dashed'
});
}
},
expand: function() {
expand: function () {
var that = this;
RED.tray.hide();
RED.view.selectNodes({
single: true,
selected: [that.value()],
onselect: function(selection) {
onselect: function (selection) {
that.value(selection.id);
RED.tray.show();
},
oncancel: function() {
oncancel: function () {
RED.tray.show();
}
})
}
},
cred:{
value:"cred",
label:"credential",
icon:"fa fa-lock",
cred: {
value: "cred",
label: "credential",
icon: "fa fa-lock",
inputType: "password",
valueLabel: function(container,value) {
valueLabel: function (container, value) {
var that = this;
container.css("pointer-events","none");
container.css("flex-grow",0);
container.css("pointer-events", "none");
container.css("flex-grow", 0);
this.elementDiv.hide();
var buttons = $('<div>').css({
position: "absolute",
right:"6px",
right: "6px",
top: "6px",
"pointer-events":"all"
"pointer-events": "all"
}).appendTo(container);
var eyeButton = $('<button type="button" class="red-ui-button red-ui-button-small"></button>').css({
width:"20px"
}).appendTo(buttons).on("click", function(evt) {
width: "20px"
}).appendTo(buttons).on("click", function (evt) {
evt.preventDefault();
var cursorPosition = that.input[0].selectionStart;
var currentType = that.input.attr("type");
if (currentType === "text") {
that.input.attr("type","password");
that.input.attr("type", "password");
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
setTimeout(function() {
setTimeout(function () {
that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50);
}, 50);
} else {
that.input.attr("type","text");
that.input.attr("type", "text");
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
setTimeout(function() {
setTimeout(function () {
that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50);
}, 50);
}
}).hide();
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left", "-2px").appendTo(eyeButton);
if (value === "__PWRD__") {
var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
padding:"6px 6px",
borderRadius:"4px"
padding: "6px 6px",
borderRadius: "4px"
}).addClass("red-ui-typedInput-value-label-inactive").appendTo(container);
var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function(evt) {
var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function (evt) {
evt.preventDefault();
innerContainer.hide();
container.css("background","none");
container.css("pointer-events","none");
container.css("background", "none");
container.css("pointer-events", "none");
that.input.val("");
that.element.val("");
that.elementDiv.show();
editButton.hide();
cancelButton.show();
eyeButton.show();
setTimeout(function() {
setTimeout(function () {
that.input.focus();
},50);
}, 50);
});
var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left","3px").appendTo(buttons).on("click", function(evt) {
var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left", "3px").appendTo(buttons).on("click", function (evt) {
evt.preventDefault();
innerContainer.show();
container.css("background","");
container.css("background", "");
that.input.val("__PWRD__");
that.element.val("__PWRD__");
that.elementDiv.hide();
editButton.show();
cancelButton.hide();
eyeButton.hide();
that.input.attr("type","password");
that.input.attr("type", "password");
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
}).hide();
} else {
container.css("background","none");
container.css("pointer-events","none");
container.css("background", "none");
container.css("pointer-events", "none");
this.elementDiv.show();
eyeButton.show();
}
}
},
'conf-types': {
value: "conf-types",
label: "config",
icon: "fa fa-cog",
// hasValue: false,
valueLabel: function (container, value) {
// get the selected option (for access to the "name" and "module" properties)
const _options = this._optionsCache || this.typeList.find(opt => opt.value === value)?.options || []
const selectedOption = _options.find(opt => opt.value === value) || {
title: '',
name: '',
module: ''
}
container.attr("title", selectedOption.title) // set tooltip to the full path/id of the module/node
container.text(selectedOption.name) // apply the "name" of the selected option
// set "line-height" such as to make the "name" appear further up, giving room for the "module" to be displayed below the value
container.css("line-height", "1.4em")
// add the module name in smaller, lighter font below the value
$('<div></div>').text(selectedOption.module).css({
// "font-family": "var(--red-ui-monospace-font)",
color: "var(--red-ui-tertiary-text-color)",
"font-size": "0.8em",
"line-height": "1em",
opacity: 0.8
}).appendTo(container);
},
// hasValue: false,
options: function () {
if (this._optionsCache) {
return this._optionsCache
}
const configNodes = RED.nodes.registry.getNodeDefinitions({configOnly: true, filter: (def) => def.type !== "global-config"}).map((def) => {
// create a container with with 2 rows (row 1 for the name, row 2 for the module name in smaller, lighter font)
const container = $('<div style="display: flex; flex-direction: column; justify-content: space-between; row-gap: 1px;">')
const row1Name = $('<div>').text(def.type)
const row2Module = $('<div style="font-size: 0.8em; color: var(--red-ui-tertiary-text-color);">').text(def.set.module)
container.append(row1Name, row2Module)
return {
value: def.type,
name: def.type,
enabled: def.set.enabled ?? true,
local: def.set.local,
title: def.set.id, // tooltip e.g. "node-red-contrib-foo/bar"
module: def.set.module,
icon: container[0].outerHTML.trim(), // the typeInput will interpret this as html text and render it in the anchor
}
})
this._optionsCache = configNodes
return configNodes
}
}
};
// For a type with options, check value is a valid selection
// If !opt.multiple, returns the valid option object
// if opt.multiple, returns an array of valid option objects
// If not valid, returns null;
function isOptionValueValid(opt, currentVal) {
let _options = opt.options
if (typeof _options === "function") {
_options = _options.call(this)
}
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
for (var i=0;i<_options.length;i++) {
op = _options[i];
if (typeof op === "string" && op === currentVal) {
return {value:currentVal}
} else if (op.value === currentVal) {
@@ -409,8 +686,8 @@
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
for (var i=0;i<_options.length;i++) {
op = _options[i];
var val = typeof op === "string" ? op : op.value;
if (currentValues.hasOwnProperty(val)) {
delete currentValues[val];
@@ -425,6 +702,7 @@
}
var nlsd = false;
let contextStoreOptions;
$.widget( "nodered.typedInput", {
_create: function() {
@@ -436,7 +714,7 @@
}
}
var contextStores = RED.settings.context.stores;
var contextOptions = contextStores.map(function(store) {
contextStoreOptions = contextStores.map(function(store) {
return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database"></i>'}
}).sort(function(A,B) {
if (A.value === RED.settings.context.default) {
@@ -447,13 +725,17 @@
return A.value.localeCompare(B.value);
}
})
if (contextOptions.length < 2) {
if (contextStoreOptions.length < 2) {
allOptions.flow.options = [];
allOptions.global.options = [];
} else {
allOptions.flow.options = contextOptions;
allOptions.global.options = contextOptions;
allOptions.flow.options = contextStoreOptions;
allOptions.global.options = contextStoreOptions;
}
// Translate timestamp options
allOptions.date.options.forEach(opt => {
opt.label = RED._("typedInput.date.format." + (opt.value || 'timestamp'), {defaultValue: opt.label})
})
}
nlsd = true;
var that = this;
@@ -542,7 +824,7 @@
that.element.trigger('paste',evt);
});
this.input.on('keydown', function(evt) {
if (that.typeMap[that.propertyType].autoComplete) {
if (that.typeMap[that.propertyType].autoComplete || that.input.hasClass('red-ui-autoComplete')) {
return
}
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
@@ -732,12 +1014,12 @@
}
if (menu.opts.multiple) {
var selected = {};
this.value().split(",").forEach(function(f) {
selected[f] = true;
})
this.value().split(",").forEach(function(f) {
selected[f] = true;
});
menu.find('input[type="checkbox"]').each(function() {
$(this).prop("checked",selected[$(this).data('value')])
})
$(this).prop("checked", selected[$(this).data('value')] || false);
});
}
@@ -828,7 +1110,7 @@
this.input.trigger('change',[this.propertyType,this.value()]);
}
} else {
this.optionSelectLabel.text(o.length+" selected");
this.optionSelectLabel.text(RED._("typedInput.selected", { count: o.length }));
}
}
},
@@ -836,7 +1118,9 @@
if (this.optionMenu) {
this.optionMenu.remove();
}
this.menu.remove();
if (this.menu) {
this.menu.remove();
}
this.uiSelect.remove();
},
types: function(types) {
@@ -869,7 +1153,7 @@
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
if (!firstCall) {
this.type(this.typeList[0].value);
this.type(this.typeList[0]?.value || ""); // permit empty typeList
}
} else {
this.propertyType = null;
@@ -906,6 +1190,11 @@
var selectedOption = [];
var valueToCheck = value;
if (opt.options) {
let _options = opt.options
if (typeof opt.options === "function") {
_options = opt.options.call(this)
}
if (opt.hasValue && opt.parse) {
var parts = opt.parse(value);
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
@@ -919,8 +1208,8 @@
checkValues = valueToCheck.split(",");
}
checkValues.forEach(function(valueToCheck) {
for (var i=0;i<opt.options.length;i++) {
var op = opt.options[i];
for (var i=0;i<_options.length;i++) {
var op = _options[i];
if (typeof op === "string") {
if (op === valueToCheck || op === ""+valueToCheck) {
selectedOption.push(that.activeOptions[op]);
@@ -955,7 +1244,7 @@
},
type: function(type) {
if (!arguments.length) {
return this.propertyType;
return this.propertyType || this.options?.default || '';
} else {
var that = this;
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
@@ -965,6 +1254,9 @@
// If previousType is !null, then this is a change of the type, rather than the initialisation
var previousType = this.typeMap[this.propertyType];
previousValue = this.input.val();
if (this.input.hasClass('red-ui-autoComplete')) {
this.input.autoComplete("destroy");
}
if (previousType && this.typeChanged) {
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
@@ -1011,7 +1303,9 @@
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
}
if (previousType.autoComplete) {
this.input.autoComplete("destroy");
if (this.input.hasClass('red-ui-autoComplete')) {
this.input.autoComplete("destroy");
}
}
}
this.propertyType = type;
@@ -1051,6 +1345,10 @@
this.optionMenu = null;
}
if (opt.options) {
let _options = opt.options
if (typeof _options === "function") {
_options = opt.options.call(this);
}
if (this.optionExpandButton) {
this.optionExpandButton.hide();
this.optionExpandButton.shown = false;
@@ -1067,7 +1365,7 @@
this.valueLabelContainer.hide();
}
this.activeOptions = {};
opt.options.forEach(function(o) {
_options.forEach(function(o) {
if (typeof o === 'string') {
that.activeOptions[o] = {label:o,value:o};
} else {
@@ -1087,7 +1385,7 @@
if (validValues) {
that._updateOptionSelectLabel(validValues)
} else {
op = opt.options[0];
op = _options[0] || {value:""}; // permit zero options
if (typeof op === "string") {
this.value(op);
that._updateOptionSelectLabel({value:op});
@@ -1106,7 +1404,7 @@
that._updateOptionSelectLabel(validValues);
}
} else {
var selectedOption = this.optionValue||opt.options[0];
var selectedOption = this.optionValue||_options[0];
if (opt.parse) {
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
var parts = opt.parse(this.input.val(),selectedOptionObj);
@@ -1139,8 +1437,18 @@
} else {
this.optionSelectTrigger.hide();
}
if (opt.autoComplete) {
let searchFunction = opt.autoComplete
if (searchFunction.length === 0) {
searchFunction = opt.autoComplete.call(this)
}
this.input.autoComplete({
search: searchFunction,
minLength: 0
})
}
}
this.optionMenu = this._createMenu(opt.options,opt,function(v){
this.optionMenu = this._createMenu(_options,opt,function(v){
if (!opt.multiple) {
that._updateOptionSelectLabel(that.activeOptions[v]);
if (!opt.hasValue) {
@@ -1181,8 +1489,12 @@
this.valueLabelContainer.hide();
this.elementDiv.show();
if (opt.autoComplete) {
let searchFunction = opt.autoComplete
if (searchFunction.length === 0) {
searchFunction = opt.autoComplete.call(this)
}
this.input.autoComplete({
search: opt.autoComplete,
search: searchFunction,
minLength: 0
})
}
@@ -1231,26 +1543,48 @@
}
}
},
validate: function() {
var result;
var value = this.value();
var type = this.type();
validate: function(options) {
let valid = true;
const value = this.value();
const type = this.type();
if (this.typeMap[type] && this.typeMap[type].validate) {
var val = this.typeMap[type].validate;
if (typeof val === 'function') {
result = val(value);
const validate = this.typeMap[type].validate;
if (typeof validate === 'function') {
valid = validate(value, {});
} else {
result = val.test(value);
// Regex
valid = validate.test(value);
if (!valid) {
valid = RED._("validator.errors.invalid-regexp");
}
}
}
if ((typeof valid === "string") || !valid) {
this.element.addClass("input-error");
this.uiSelect.addClass("input-error");
if (typeof valid === "string") {
let tooltip = this.element.data("tooltip");
if (tooltip) {
tooltip.setContent(valid);
} else {
tooltip = RED.popover.tooltip(this.elementDiv, valid);
this.element.data("tooltip", tooltip);
}
}
} else {
result = true;
this.element.removeClass("input-error");
this.uiSelect.removeClass("input-error");
const tooltip = this.element.data("tooltip");
if (tooltip) {
this.element.data("tooltip", null);
tooltip.delete();
}
}
if (result) {
this.uiSelect.removeClass('input-error');
} else {
this.uiSelect.addClass('input-error');
if (options?.returnErrorMessage === true) {
return valid;
}
return result;
// Must return a boolean for no 3.x validator
return (typeof valid === "string") ? false : valid;
},
show: function() {
this.uiSelect.show();

View File

@@ -1,21 +1,6 @@
RED.contextMenu = (function () {
let menu;
function createMenu() {
// menu = RED.popover.menu({
// options: [
// {
// label: 'delete selection',
// onselect: function() {
// RED.actions.invoke('core:delete-selection')
// RED.view.focus()
// }
// },
// { label: 'world' }
// ],
// width: 200,
// })
}
function disposeMenu() {
$(document).off("mousedown.red-ui-workspace-context-menu");
@@ -28,114 +13,207 @@ RED.contextMenu = (function () {
if (menu) {
menu.remove()
}
const selection = RED.view.selection()
const noSelection = !selection || Object.keys(selection).length === 0
const hasSelection = (selection.nodes && selection.nodes.length > 0);
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
const hasLinks = wireLinks.length > 0;
const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
const canDelete = hasSelection || hasLinks
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
const offset = $("#red-ui-workspace-chart").offset()
// addX/addY must be the position in the workspace accounting for both scroll and scale
// The +5 is because we display the contextMenu -5,-5 to actual click position
let addX = (options.x + 5 - offset.left + $("#red-ui-workspace-chart").scrollLeft()) / RED.view.scale()
let addY = (options.y + 5 - offset.top + $("#red-ui-workspace-chart").scrollTop()) / RED.view.scale()
const menuItems = [
{ onselect: 'core:show-action-list', onpostselect: function () { } },
{
label: RED._("contextMenu.insert"),
options: [
{
label: RED._("contextMenu.node"),
onselect: function () {
RED.view.showQuickAddDialog({
position: [addX, addY],
touchTrigger: true,
splice: isSingleLink ? selection.links[0] : undefined,
// spliceMultiple: isMultipleLinks
})
},
onpostselect: function() {
// ensure quick add dialog search input has focus
$('#red-ui-type-search-input').trigger('focus')
let menuItems = []
if (options.options) {
menuItems = options.options
} else if (options.type === 'workspace') {
const selection = RED.view.selection()
const noSelection = !selection || Object.keys(selection).length === 0
const hasSelection = (selection.nodes && selection.nodes.length > 0);
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
const virtulLinks = (selection.links && selection.links.filter(e => !!e.link)) || [];
const wireLinks = (selection.links && selection.links.filter(e => !e.link)) || [];
const hasLinks = wireLinks.length > 0;
const isSingleLink = !hasSelection && hasLinks && wireLinks.length === 1
const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
const canDelete = hasSelection || hasLinks
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
const canEdit = !RED.workspaces.isLocked()
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
if (hasSelection) {
const nodes = selection.nodes.slice();
while (nodes.length) {
const n = nodes.shift();
if (n.type === 'group') {
hasGroup = true;
nodes.push(...n.nodes);
} else {
isAllGroups = false;
if (n.d) {
hasDisabledNode = true;
} else {
hasEnabledNode = true;
}
},
(hasLinks) ? { // has least 1 wire selected
label: RED._("contextMenu.junction"),
onselect: 'core:split-wires-with-junctions',
disabled: !hasLinks
} : {
label: RED._("contextMenu.junction"),
onselect: function () {
const nn = {
_def: { defaults: {} },
type: 'junction',
z: RED.workspaces.active(),
id: RED.nodes.id(),
x: addX,
y: addY,
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
}
const historyEvent = {
dirty: RED.nodes.dirty(),
t: 'add',
junctions: [nn]
}
RED.nodes.addJunction(nn);
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.view.select({nodes: [nn] });
RED.view.redraw(true)
}
},
{
label: RED._("contextMenu.linkNodes"),
onselect: 'core:split-wire-with-link-nodes',
disabled: !hasLinks
}
]
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()
let addY = options.y - offset.top + $("#red-ui-workspace-chart").scrollTop()
if (RED.view.snapGrid) {
const gridSize = RED.view.gridSize()
addX = gridSize * Math.floor(addX / gridSize)
addY = gridSize * Math.floor(addY / gridSize)
}
if (RED.settings.theme("menu.menu-item-action-list", true)) {
menuItems.push(
{ onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
)
}
const insertOptions = []
menuItems.push({ label: RED._("contextMenu.insert"), options: insertOptions })
insertOptions.push(
{
label: RED._("contextMenu.node"),
onselect: function () {
RED.view.showQuickAddDialog({
position: [addX, addY],
touchTrigger: 'ontouchstart' in window,
splice: isSingleLink ? selection.links[0] : undefined,
// spliceMultiple: isMultipleLinks
})
},
disabled: !canEdit
},
(hasLinks) ? { // has least 1 wire selected
label: RED._("contextMenu.junction"),
onselect: 'core:split-wires-with-junctions',
disabled: !canEdit || !hasLinks
} : {
label: RED._("contextMenu.junction"),
onselect: function () {
const nn = {
_def: { defaults: {} },
type: 'junction',
z: RED.workspaces.active(),
id: RED.nodes.id(),
x: addX,
y: addY,
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true,
moved: true
}
const junction = RED.nodes.addJunction(nn);
const historyEvent = {
dirty: RED.nodes.dirty(),
t: 'add',
junctions: [junction]
}
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.view.select({nodes: [junction] });
RED.view.redraw(true)
},
disabled: !canEdit
},
{
label: RED._("contextMenu.linkNodes"),
onselect: 'core:split-wire-with-link-nodes',
disabled: !canEdit || !hasLinks
},
null
)
if (RED.settings.theme("menu.menu-item-import-library", true)) {
insertOptions.push(
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
)
}
if (hasSelection && canEdit) {
const nodeOptions = []
if (!hasMultipleSelection && !isGroup) {
nodeOptions.push(
{ onselect: 'core:show-node-help', label: RED._('menu.label.showNodeHelp') },
null
)
}
nodeOptions.push(
{ 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', 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'),
options: nodeOptions
})
menuItems.push({
label: RED._('sidebar.info.group'),
options: [
{ onselect: 'core:group-selection', label: RED._("menu.label.groupSelection") },
{ onselect: 'core:ungroup-selection', label: RED._("menu.label.ungroupSelection"), disabled: !hasGroup },
]
})
if (hasGroup) {
menuItems[menuItems.length - 1].options.push(
{ onselect: 'core:merge-selection-to-group', label: RED._("menu.label.groupMergeSelection") }
)
}
if (canRemoveFromGroup) {
menuItems[menuItems.length - 1].options.push(
{ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }
)
}
menuItems[menuItems.length - 1].options.push(
null,
{ 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) {
menuItems.push({
label: RED._('menu.label.arrange'),
options: [
{ label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"},
{ label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"},
{ label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"},
null,
{ label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"},
{ label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"},
{ label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"},
null,
{ label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"},
{ label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"}
]
})
}
]
menuItems.push(
null,
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
null,
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !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: !RED.view.clipboard() },
{ onselect: 'core:delete-selection', disabled: !canDelete },
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
{ onselect: 'core:select-all-nodes' }
)
if (hasSelection) {
menuItems.push(
null,
isGroup ?
{ onselect: 'core:ungroup-selection', disabled: !isGroup }
: { onselect: 'core:group-selection', disabled: !hasSelection }
{ 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', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
)
if (canRemoveFromGroup) {
menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
if (RED.settings.theme("menu.menu-item-export-library", true)) {
menuItems.push(
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }
)
}
menuItems.push(
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") }
)
}
var direction = "right";

View File

@@ -34,6 +34,8 @@ RED.deploy = (function() {
var currentDiff = null;
var activeBackgroundDeployNotification;
function changeDeploymentType(type) {
deploymentType = type;
$("#red-ui-header-button-deploy-icon").attr("src",deploymentTypes[type].img);
@@ -61,7 +63,7 @@ RED.deploy = (function() {
'<img src="red/images/spin.svg"/>'+
'</span>'+
'</a>'+
'<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
'<a id="red-ui-header-button-deploy-options" class="red-ui-deploy-button" href="#"><i class="fa fa-caret-down"></i><i class="fa fa-lock"></i></a>'+
'</span></li>').prependTo(".red-ui-header-toolbar");
const mainMenuItems = [
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.svg",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
@@ -112,53 +114,80 @@ RED.deploy = (function() {
RED.actions.add("core:set-deploy-type-to-modified-nodes",function() { RED.menu.setSelected("deploymenu-item-node",true); });
}
window.addEventListener('beforeunload', function (event) {
if (RED.nodes.dirty()) {
event.preventDefault();
event.stopImmediatePropagation()
event.returnValue = RED._("deploy.confirm.undeployedChanges");
return
}
})
RED.events.on('workspace:dirty',function(state) {
if (RED.settings.user?.permissions === 'read') {
return
}
if (state.dirty) {
window.onbeforeunload = function() {
return RED._("deploy.confirm.undeployedChanges");
}
// window.onbeforeunload = function() {
// return
// }
$("#red-ui-header-button-deploy").removeClass("disabled");
} else {
window.onbeforeunload = null;
// window.onbeforeunload = null;
$("#red-ui-header-button-deploy").addClass("disabled");
}
});
var activeNotifyMessage;
RED.comms.subscribe("notification/runtime-deploy",function(topic,msg) {
if (!activeNotifyMessage) {
var currentRev = RED.nodes.version();
if (currentRev === null || deployInflight || currentRev === msg.revision) {
return;
}
var message = $('<p>').text(RED._('deploy.confirm.backgroundUpdate'));
activeNotifyMessage = RED.notify(message,{
modal: true,
fixed: true,
buttons: [
{
text: RED._('deploy.confirm.button.ignore'),
click: function() {
activeNotifyMessage.close();
activeNotifyMessage = null;
}
},
{
text: RED._('deploy.confirm.button.review'),
class: "primary",
click: function() {
activeNotifyMessage.close();
var nns = RED.nodes.createCompleteNodeSet();
resolveConflict(nns,false);
activeNotifyMessage = null;
}
var currentRev = RED.nodes.version();
if (currentRev === null || deployInflight || currentRev === msg.revision) {
return;
}
if (activeBackgroundDeployNotification?.hidden && !activeBackgroundDeployNotification?.closed) {
activeBackgroundDeployNotification.showNotification()
return
}
const message = $('<p>').text(RED._('deploy.confirm.backgroundUpdate'));
const options = {
id: 'background-update',
type: 'compact',
modal: false,
fixed: true,
timeout: 10000,
buttons: [
{
text: RED._('deploy.confirm.button.review'),
class: "primary",
click: function() {
activeBackgroundDeployNotification.hideNotification();
var nns = RED.nodes.createCompleteNodeSet();
resolveConflict(nns,false);
}
]
});
}
]
}
if (!activeBackgroundDeployNotification || activeBackgroundDeployNotification.closed) {
activeBackgroundDeployNotification = RED.notify(message, options)
} else {
activeBackgroundDeployNotification.update(message, options)
}
});
updateLockedState()
RED.events.on('login', updateLockedState)
}
function updateLockedState() {
if (RED.settings.user?.permissions === 'read') {
$(".red-ui-deploy-button-group").addClass("readOnly");
$("#red-ui-header-button-deploy").addClass("disabled");
} else {
$(".red-ui-deploy-button-group").removeClass("readOnly");
if (RED.nodes.dirty()) {
$("#red-ui-header-button-deploy").removeClass("disabled");
}
}
}
function getNodeInfo(node) {
@@ -213,7 +242,11 @@ RED.deploy = (function() {
class: "primary disabled",
click: function() {
if (!$("#red-ui-deploy-dialog-confirm-deploy-review").hasClass('disabled')) {
RED.diff.showRemoteDiff();
RED.diff.showRemoteDiff(null, {
onmerge: function () {
activeBackgroundDeployNotification.close()
}
});
conflictNotification.close();
}
}
@@ -226,6 +259,7 @@ RED.deploy = (function() {
if (!$("#red-ui-deploy-dialog-confirm-deploy-merge").hasClass('disabled')) {
RED.diff.mergeDiff(currentDiff);
conflictNotification.close();
activeBackgroundDeployNotification.close()
}
}
}
@@ -238,6 +272,7 @@ RED.deploy = (function() {
click: function() {
save(true,activeDeploy);
conflictNotification.close();
activeBackgroundDeployNotification.close()
}
})
}
@@ -248,21 +283,17 @@ RED.deploy = (function() {
buttons: buttons
});
var now = Date.now();
RED.diff.getRemoteDiff(function(diff) {
var ellapsed = Math.max(1000 - (Date.now()-now), 0);
currentDiff = diff;
setTimeout(function() {
conflictCheck.hide();
var d = Object.keys(diff.conflicts);
if (d.length === 0) {
conflictAutoMerge.show();
$("#red-ui-deploy-dialog-confirm-deploy-merge").removeClass('disabled')
} else {
conflictManualMerge.show();
}
$("#red-ui-deploy-dialog-confirm-deploy-review").removeClass('disabled')
},ellapsed);
conflictCheck.hide();
var d = Object.keys(diff.conflicts);
if (d.length === 0) {
conflictAutoMerge.show();
$("#red-ui-deploy-dialog-confirm-deploy-merge").removeClass('disabled')
} else {
conflictManualMerge.show();
}
$("#red-ui-deploy-dialog-confirm-deploy-review").removeClass('disabled')
})
}
function cropList(list) {
@@ -557,7 +588,19 @@ RED.deploy = (function() {
} else {
RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
}
const flowsToLock = new Set()
// Node's properties cannot be modified if its workspace is locked.
function ensureUnlocked(id) {
// TODO: `RED.nodes.subflow` is useless
const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null);
const isLocked = flow ? flow.locked : false;
if (flow && isLocked) {
flow.locked = false;
flowsToLock.add(flow)
}
}
RED.nodes.eachNode(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
@@ -570,21 +613,65 @@ RED.deploy = (function() {
delete node.credentials;
}
});
RED.nodes.eachGroup(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
}
if (node.moved) {
node.dirty = true;
node.moved = false;
}
})
RED.nodes.eachJunction(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
}
if (node.moved) {
node.dirty = true;
node.moved = false;
}
})
RED.nodes.eachConfig(function (confNode) {
if (confNode.z) {
ensureUnlocked(confNode.z)
}
confNode.changed = false;
if (confNode.credentials) {
delete confNode.credentials;
}
});
// Subflow cannot be locked
RED.nodes.eachSubflow(function (subflow) {
subflow.changed = false;
if (subflow.changed) {
subflow.changed = false;
RED.events.emit("subflows:change", subflow);
}
});
RED.nodes.eachWorkspace(function (ws) {
ws.changed = false;
if (ws.changed || ws.added) {
// Ensure the Workspace is unlocked to modify its properties.
ensureUnlocked(ws.id);
ws.changed = false;
delete ws.added
if (flowsToLock.has(ws)) {
ws.locked = true;
flowsToLock.delete(ws);
}
RED.events.emit("flows:change", ws)
}
});
// Ensures all workspaces to be locked have been locked.
flowsToLock.forEach(flow => {
flow.locked = true
})
// Once deployed, cannot undo back to a clean state
RED.history.markAllDirty();
RED.view.redraw();
RED.sidebar.config.refresh();
RED.events.emit("deploy");
}).fail(function (xhr, textStatus, err) {
RED.nodes.dirty(true);

View File

@@ -1,5 +1,4 @@
RED.diff = (function() {
var currentDiff = {};
var diffVisible = false;
var diffList;
@@ -62,12 +61,14 @@ RED.diff = (function() {
addedCount:0,
deletedCount:0,
changedCount:0,
movedCount:0,
unchangedCount: 0
},
remote: {
addedCount:0,
deletedCount:0,
changedCount:0,
movedCount:0,
unchangedCount: 0
},
conflicts: 0
@@ -138,7 +139,7 @@ RED.diff = (function() {
$(this).parent().toggleClass('collapsed');
});
createNodePropertiesTable(def,tab,localTabNode,remoteTabNode,conflicts).appendTo(div);
createNodePropertiesTable(def,tab,localTabNode,remoteTabNode).appendTo(div);
selectState = "";
if (conflicts[tab.id]) {
flowStats.conflicts++;
@@ -208,19 +209,26 @@ RED.diff = (function() {
var localStats = $('<span>',{class:"red-ui-diff-list-flow-stats"}).appendTo(localCell);
$('<span class="red-ui-diff-status"></span>').text(RED._('diff.nodeCount',{count:localNodeCount})).appendTo(localStats);
if (flowStats.conflicts + flowStats.local.addedCount + flowStats.local.changedCount + flowStats.local.deletedCount > 0) {
if (flowStats.conflicts + flowStats.local.addedCount + flowStats.local.changedCount + flowStats.local.movedCount + flowStats.local.deletedCount > 0) {
$('<span class="red-ui-diff-status"> [ </span>').appendTo(localStats);
if (flowStats.conflicts > 0) {
$('<span class="red-ui-diff-status-conflict"><span class="red-ui-diff-status"><i class="fa fa-exclamation"></i> '+flowStats.conflicts+'</span></span>').appendTo(localStats);
}
if (flowStats.local.addedCount > 0) {
$('<span class="red-ui-diff-status-added"><span class="red-ui-diff-status"><i class="fa fa-plus-square"></i> '+flowStats.local.addedCount+'</span></span>').appendTo(localStats);
const cell = $('<span class="red-ui-diff-status-added"><span class="red-ui-diff-status"><i class="fa fa-plus-square"></i> '+flowStats.local.addedCount+'</span></span>').appendTo(localStats);
RED.popover.tooltip(cell, RED._('diff.type.added'))
}
if (flowStats.local.changedCount > 0) {
$('<span class="red-ui-diff-status-changed"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.local.changedCount+'</span></span>').appendTo(localStats);
const cell = $('<span class="red-ui-diff-status-changed"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.local.changedCount+'</span></span>').appendTo(localStats);
RED.popover.tooltip(cell, RED._('diff.type.changed'))
}
if (flowStats.local.movedCount > 0) {
const cell = $('<span class="red-ui-diff-status-moved"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.local.movedCount+'</span></span>').appendTo(localStats);
RED.popover.tooltip(cell, RED._('diff.type.moved'))
}
if (flowStats.local.deletedCount > 0) {
$('<span class="red-ui-diff-status-deleted"><span class="red-ui-diff-status"><i class="fa fa-minus-square"></i> '+flowStats.local.deletedCount+'</span></span>').appendTo(localStats);
const cell = $('<span class="red-ui-diff-status-deleted"><span class="red-ui-diff-status"><i class="fa fa-minus-square"></i> '+flowStats.local.deletedCount+'</span></span>').appendTo(localStats);
RED.popover.tooltip(cell, RED._('diff.type.deleted'))
}
$('<span class="red-ui-diff-status"> ] </span>').appendTo(localStats);
}
@@ -246,19 +254,26 @@ RED.diff = (function() {
}
var remoteStats = $('<span>',{class:"red-ui-diff-list-flow-stats"}).appendTo(remoteCell);
$('<span class="red-ui-diff-status"></span>').text(RED._('diff.nodeCount',{count:remoteNodeCount})).appendTo(remoteStats);
if (flowStats.conflicts + flowStats.remote.addedCount + flowStats.remote.changedCount + flowStats.remote.deletedCount > 0) {
if (flowStats.conflicts + flowStats.remote.addedCount + flowStats.remote.changedCount + flowStats.remote.movedCount + flowStats.remote.deletedCount > 0) {
$('<span class="red-ui-diff-status"> [ </span>').appendTo(remoteStats);
if (flowStats.conflicts > 0) {
$('<span class="red-ui-diff-status-conflict"><span class="red-ui-diff-status"><i class="fa fa-exclamation"></i> '+flowStats.conflicts+'</span></span>').appendTo(remoteStats);
}
if (flowStats.remote.addedCount > 0) {
$('<span class="red-ui-diff-status-added"><span class="red-ui-diff-status"><i class="fa fa-plus-square"></i> '+flowStats.remote.addedCount+'</span></span>').appendTo(remoteStats);
const cell = $('<span class="red-ui-diff-status-added"><span class="red-ui-diff-status"><i class="fa fa-plus-square"></i> '+flowStats.remote.addedCount+'</span></span>').appendTo(remoteStats);
RED.popover.tooltip(cell, RED._('diff.type.added'))
}
if (flowStats.remote.changedCount > 0) {
$('<span class="red-ui-diff-status-changed"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.remote.changedCount+'</span></span>').appendTo(remoteStats);
const cell = $('<span class="red-ui-diff-status-changed"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.remote.changedCount+'</span></span>').appendTo(remoteStats);
RED.popover.tooltip(cell, RED._('diff.type.changed'))
}
if (flowStats.remote.movedCount > 0) {
const cell = $('<span class="red-ui-diff-status-moved"><span class="red-ui-diff-status"><i class="fa fa-square"></i> '+flowStats.remote.movedCount+'</span></span>').appendTo(remoteStats);
RED.popover.tooltip(cell, RED._('diff.type.moved'))
}
if (flowStats.remote.deletedCount > 0) {
$('<span class="red-ui-diff-status-deleted"><span class="red-ui-diff-status"><i class="fa fa-minus-square"></i> '+flowStats.remote.deletedCount+'</span></span>').appendTo(remoteStats);
const cell = $('<span class="red-ui-diff-status-deleted"><span class="red-ui-diff-status"><i class="fa fa-minus-square"></i> '+flowStats.remote.deletedCount+'</span></span>').appendTo(remoteStats);
RED.popover.tooltip(cell, RED._('diff.type.deleted'))
}
$('<span class="red-ui-diff-status"> ] </span>').appendTo(remoteStats);
}
@@ -293,7 +308,7 @@ RED.diff = (function() {
if (options.mode === "merge") {
diffPanel.addClass("red-ui-diff-panel-merge");
}
var diffList = createDiffTable(diffPanel, diff);
var diffList = createDiffTable(diffPanel, diff, options);
var localDiff = diff.localDiff;
var remoteDiff = diff.remoteDiff;
@@ -482,7 +497,7 @@ RED.diff = (function() {
}
})
if (c === 0) {
result.text("none");
result.text(RED._("diff.type.none"));
} else {
list.appendTo(result);
}
@@ -516,7 +531,6 @@ RED.diff = (function() {
var hasChanges = false; // exists in original and local/remote but with changes
var unChanged = true; // existing in original,local,remote unchanged
var localChanged = false;
if (localDiff.added[node.id]) {
stats.local.addedCount++;
@@ -535,12 +549,20 @@ RED.diff = (function() {
unChanged = false;
}
if (localDiff.changed[node.id]) {
stats.local.changedCount++;
if (localDiff.positionChanged[node.id]) {
stats.local.movedCount++
} else {
stats.local.changedCount++;
}
hasChanges = true;
unChanged = false;
}
if (remoteDiff && remoteDiff.changed[node.id]) {
stats.remote.changedCount++;
if (remoteDiff.positionChanged[node.id]) {
stats.remote.movedCount++
} else {
stats.remote.changedCount++;
}
hasChanges = true;
unChanged = false;
}
@@ -605,27 +627,32 @@ RED.diff = (function() {
localNodeDiv.addClass("red-ui-diff-status-moved");
var localMovedMessage = "";
if (node.z === localN.z) {
localMovedMessage = RED._("diff.type.movedFrom",{id:(localDiff.currentConfig.all[node.id].z||'global')});
const movedFromNodeTab = localDiff.currentConfig.all[localDiff.currentConfig.all[node.id].z]
const movedFromLabel = `'${movedFromNodeTab ? (movedFromNodeTab.label || movedFromNodeTab.id) : 'global'}'`
localMovedMessage = RED._("diff.type.movedFrom",{id: movedFromLabel});
} else {
localMovedMessage = RED._("diff.type.movedTo",{id:(localN.z||'global')});
const movedToNodeTab = localDiff.newConfig.all[localN.z]
const movedToLabel = `'${movedToNodeTab ? (movedToNodeTab.label || movedToNodeTab.id) : 'global'}'`
localMovedMessage = RED._("diff.type.movedTo",{id:movedToLabel});
}
$('<span class="red-ui-diff-status"><i class="fa fa-caret-square-o-right"></i> '+localMovedMessage+'</span>').appendTo(localNodeDiv);
}
localChanged = true;
} else if (localDiff.deleted[node.z]) {
localNodeDiv.addClass("red-ui-diff-empty");
localChanged = true;
} else if (localDiff.deleted[node.id]) {
localNodeDiv.addClass("red-ui-diff-status-deleted");
$('<span class="red-ui-diff-status"><i class="fa fa-minus-square"></i> <span data-i18n="diff.type.deleted"></span></span>').appendTo(localNodeDiv);
localChanged = true;
} else if (localDiff.changed[node.id]) {
if (localDiff.newConfig.all[node.id].z !== node.z) {
localNodeDiv.addClass("red-ui-diff-empty");
} else {
localNodeDiv.addClass("red-ui-diff-status-changed");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.changed"></span></span>').appendTo(localNodeDiv);
localChanged = true;
if (localDiff.positionChanged[node.id]) {
localNodeDiv.addClass("red-ui-diff-status-moved");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.moved"></span></span>').appendTo(localNodeDiv);
} else {
localNodeDiv.addClass("red-ui-diff-status-changed");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.changed"></span></span>').appendTo(localNodeDiv);
}
}
} else {
if (localDiff.newConfig.all[node.id].z !== node.z) {
@@ -646,9 +673,13 @@ RED.diff = (function() {
remoteNodeDiv.addClass("red-ui-diff-status-moved");
var remoteMovedMessage = "";
if (node.z === remoteN.z) {
remoteMovedMessage = RED._("diff.type.movedFrom",{id:(remoteDiff.currentConfig.all[node.id].z||'global')});
const movedFromNodeTab = remoteDiff.currentConfig.all[remoteDiff.currentConfig.all[node.id].z]
const movedFromLabel = `'${movedFromNodeTab ? (movedFromNodeTab.label || movedFromNodeTab.id) : 'global'}'`
remoteMovedMessage = RED._("diff.type.movedFrom",{id:movedFromLabel});
} else {
remoteMovedMessage = RED._("diff.type.movedTo",{id:(remoteN.z||'global')});
const movedToNodeTab = remoteDiff.newConfig.all[remoteN.z]
const movedToLabel = `'${movedToNodeTab ? (movedToNodeTab.label || movedToNodeTab.id) : 'global'}'`
remoteMovedMessage = RED._("diff.type.movedTo",{id:movedToLabel});
}
$('<span class="red-ui-diff-status"><i class="fa fa-caret-square-o-right"></i> '+remoteMovedMessage+'</span>').appendTo(remoteNodeDiv);
}
@@ -661,8 +692,13 @@ RED.diff = (function() {
if (remoteDiff.newConfig.all[node.id].z !== node.z) {
remoteNodeDiv.addClass("red-ui-diff-empty");
} else {
remoteNodeDiv.addClass("red-ui-diff-status-changed");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.changed"></span></span>').appendTo(remoteNodeDiv);
if (remoteDiff.positionChanged[node.id]) {
remoteNodeDiv.addClass("red-ui-diff-status-moved");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.moved"></span></span>').appendTo(remoteNodeDiv);
} else {
remoteNodeDiv.addClass("red-ui-diff-status-changed");
$('<span class="red-ui-diff-status"><i class="fa fa-square"></i> <span data-i18n="diff.type.changed"></span></span>').appendTo(remoteNodeDiv);
}
}
} else {
if (remoteDiff.newConfig.all[node.id].z !== node.z) {
@@ -785,10 +821,10 @@ RED.diff = (function() {
conflict = true;
}
row = $("<tr>").appendTo(nodePropertiesTableBody);
$("<td>",{class:"red-ui-diff-list-cell-label"}).text("position").appendTo(row);
$("<td>",{class:"red-ui-diff-list-cell-label"}).text(RED._("diff.type.position")).appendTo(row);
localCell = $("<td>",{class:"red-ui-diff-list-cell red-ui-diff-list-node-local"}).appendTo(row);
if (localNode) {
localCell.addClass("red-ui-diff-status-"+(localChanged?"changed":"unchanged"));
localCell.addClass("red-ui-diff-status-"+(localChanged?"moved":"unchanged"));
$('<span class="red-ui-diff-status">'+(localChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(localCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(localCell);
var localPosition = {x:localNode.x,y:localNode.y};
@@ -813,7 +849,7 @@ RED.diff = (function() {
if (remoteNode !== undefined) {
remoteCell = $("<td>",{class:"red-ui-diff-list-cell red-ui-diff-list-node-remote"}).appendTo(row);
remoteCell.addClass("red-ui-diff-status-"+(remoteChanged?"changed":"unchanged"));
remoteCell.addClass("red-ui-diff-status-"+(remoteChanged?"moved":"unchanged"));
if (remoteNode) {
$('<span class="red-ui-diff-status">'+(remoteChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(remoteCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(remoteCell);
@@ -863,7 +899,7 @@ RED.diff = (function() {
conflict = true;
}
row = $("<tr>").appendTo(nodePropertiesTableBody);
$("<td>",{class:"red-ui-diff-list-cell-label"}).text("wires").appendTo(row);
$("<td>",{class:"red-ui-diff-list-cell-label"}).text(RED._("diff.type.wires")).appendTo(row);
localCell = $("<td>",{class:"red-ui-diff-list-cell red-ui-diff-list-node-local"}).appendTo(row);
if (localNode) {
if (!conflict) {
@@ -989,9 +1025,10 @@ RED.diff = (function() {
}
if (localNode && remoteNode && typeof localNode[d] === "string") {
if (/\n/.test(localNode[d]) || /\n/.test(remoteNode[d])) {
$('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
var textDiff = $('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
showTextDiff(localNode[d],remoteNode[d]);
}).appendTo(propertyNameCell);
RED.popover.tooltip(textDiff, RED._("diff.compareChanges"));
}
}
@@ -1098,11 +1135,11 @@ RED.diff = (function() {
// var diff = generateDiff(originalFlow,nns);
// showDiff(diff);
// }
function showRemoteDiff(diff) {
if (diff === undefined) {
getRemoteDiff(showRemoteDiff);
function showRemoteDiff(diff, options = {}) {
if (!diff) {
getRemoteDiff((remoteDiff) => showRemoteDiff(remoteDiff, options));
} else {
showDiff(diff,{mode:'merge'});
showDiff(diff,{...options, mode:'merge'});
}
}
function parseNodes(nodeList) {
@@ -1143,23 +1180,53 @@ RED.diff = (function() {
}
}
function generateDiff(currentNodes,newNodes) {
var currentConfig = parseNodes(currentNodes);
var newConfig = parseNodes(newNodes);
var added = {};
var deleted = {};
var changed = {};
var moved = {};
const currentConfig = parseNodes(currentNodes);
const newConfig = parseNodes(newNodes);
const added = {};
const deleted = {};
const changed = {};
const positionChanged = {};
const moved = {};
Object.keys(currentConfig.all).forEach(function(id) {
var node = RED.nodes.workspace(id)||RED.nodes.subflow(id)||RED.nodes.node(id);
const node = RED.nodes.workspace(id)||RED.nodes.subflow(id)||RED.nodes.node(id);
if (!newConfig.all.hasOwnProperty(id)) {
deleted[id] = true;
} else if (JSON.stringify(currentConfig.all[id]) !== JSON.stringify(newConfig.all[id])) {
return
}
const currentConfigJSON = JSON.stringify(currentConfig.all[id])
const newConfigJSON = JSON.stringify(newConfig.all[id])
if (currentConfigJSON !== newConfigJSON) {
changed[id] = true;
if (currentConfig.all[id].z !== newConfig.all[id].z) {
moved[id] = true;
} else if (
currentConfig.all[id].x !== newConfig.all[id].x ||
currentConfig.all[id].y !== newConfig.all[id].y ||
currentConfig.all[id].w !== newConfig.all[id].w ||
currentConfig.all[id].h !== newConfig.all[id].h
) {
// This node's position on its parent has changed. We want to
// check if this is the *only* change for this given node
const currentNodeClone = JSON.parse(currentConfigJSON)
const newNodeClone = JSON.parse(newConfigJSON)
delete currentNodeClone.x
delete currentNodeClone.y
delete currentNodeClone.w
delete currentNodeClone.h
delete newNodeClone.x
delete newNodeClone.y
delete newNodeClone.w
delete newNodeClone.h
if (JSON.stringify(currentNodeClone) === JSON.stringify(newNodeClone)) {
// Only the position has changed - everything else is the same
positionChanged[id] = true
}
}
}
});
Object.keys(newConfig.all).forEach(function(id) {
@@ -1168,13 +1235,14 @@ RED.diff = (function() {
}
});
var diff = {
currentConfig: currentConfig,
newConfig: newConfig,
added: added,
deleted: deleted,
changed: changed,
moved: moved
const diff = {
currentConfig,
newConfig,
added,
deleted,
changed,
positionChanged,
moved
};
return diff;
}
@@ -1239,12 +1307,14 @@ RED.diff = (function() {
return diff;
}
function showDiff(diff,options) {
function showDiff(diff, options) {
if (diffVisible) {
return;
}
options = options || {};
var mode = options.mode || 'merge';
options.hidePositionChanges = true
var localDiff = diff.localDiff;
var remoteDiff = diff.remoteDiff;
@@ -1314,6 +1384,9 @@ RED.diff = (function() {
if (!$("#red-ui-diff-view-diff-merge").hasClass('disabled')) {
refreshConflictHeader(diff);
mergeDiff(diff);
if (options.onmerge) {
options.onmerge()
}
RED.tray.close();
}
}
@@ -1344,6 +1417,7 @@ RED.diff = (function() {
var newConfig = [];
var node;
var nodeChangedStates = {};
var nodeMovedStates = {};
var localChangedStates = {};
for (id in localDiff.newConfig.all) {
if (localDiff.newConfig.all.hasOwnProperty(id)) {
@@ -1351,12 +1425,14 @@ RED.diff = (function() {
if (resolutions[id] === 'local') {
if (node) {
nodeChangedStates[id] = node.changed;
nodeMovedStates[id] = node.moved;
}
newConfig.push(localDiff.newConfig.all[id]);
} else if (resolutions[id] === 'remote') {
if (!remoteDiff.deleted[id] && remoteDiff.newConfig.all.hasOwnProperty(id)) {
if (node) {
nodeChangedStates[id] = node.changed;
nodeMovedStates[id] = node.moved;
}
localChangedStates[id] = 1;
newConfig.push(remoteDiff.newConfig.all[id]);
@@ -1380,8 +1456,9 @@ RED.diff = (function() {
}
return {
config: newConfig,
nodeChangedStates: nodeChangedStates,
localChangedStates: localChangedStates
nodeChangedStates,
nodeMovedStates,
localChangedStates
}
}
@@ -1392,6 +1469,7 @@ RED.diff = (function() {
var newConfig = appliedDiff.config;
var nodeChangedStates = appliedDiff.nodeChangedStates;
var nodeMovedStates = appliedDiff.nodeMovedStates;
var localChangedStates = appliedDiff.localChangedStates;
var isDirty = RED.nodes.dirty();
@@ -1400,33 +1478,56 @@ RED.diff = (function() {
t:"replace",
config: RED.nodes.createCompleteNodeSet(),
changed: nodeChangedStates,
moved: nodeMovedStates,
complete: true,
dirty: isDirty,
rev: RED.nodes.version()
}
RED.history.push(historyEvent);
var originalFlow = RED.nodes.originalFlow();
// originalFlow is what the editor things it loaded
// - add any newly added nodes from remote diff as they are now part of the record
for (var id in diff.remoteDiff.added) {
if (diff.remoteDiff.added.hasOwnProperty(id)) {
if (diff.remoteDiff.newConfig.all.hasOwnProperty(id)) {
originalFlow.push(JSON.parse(JSON.stringify(diff.remoteDiff.newConfig.all[id])));
}
}
}
// var originalFlow = RED.nodes.originalFlow();
// // originalFlow is what the editor thinks it loaded
// // - add any newly added nodes from remote diff as they are now part of the record
// for (var id in diff.remoteDiff.added) {
// if (diff.remoteDiff.added.hasOwnProperty(id)) {
// if (diff.remoteDiff.newConfig.all.hasOwnProperty(id)) {
// originalFlow.push(JSON.parse(JSON.stringify(diff.remoteDiff.newConfig.all[id])));
// }
// }
// }
RED.nodes.clear();
var imported = RED.nodes.import(newConfig);
// Restore the original flow so subsequent merge resolutions can properly
// identify new-vs-old
RED.nodes.originalFlow(originalFlow);
// // Restore the original flow so subsequent merge resolutions can properly
// // identify new-vs-old
// RED.nodes.originalFlow(originalFlow);
// Clear all change flags from the import
RED.nodes.dirty(false);
const flowsToLock = new Set()
function ensureUnlocked(id) {
const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null);
const isLocked = flow ? flow.locked : false;
if (flow && isLocked) {
flow.locked = false;
flowsToLock.add(flow)
}
}
imported.nodes.forEach(function(n) {
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
if (nodeChangedStates[n.id]) {
ensureUnlocked(n.z)
n.changed = true;
}
if (nodeMovedStates[n.id]) {
ensureUnlocked(n.z)
n.moved = true;
}
})
flowsToLock.forEach(flow => {
flow.locked = true
})
RED.nodes.version(diff.remoteDiff.rev);
@@ -1928,15 +2029,14 @@ RED.diff = (function() {
if (!isSeparator) {
var isOurs = /^..<<<<<<</.test(lineText);
if (isOurs) {
$('<span>').text("<<<<<<< Local Changes").appendTo(line);
$('<span>').text("<<<<<<< " + RED._("diff.localChanges")).appendTo(line);
hunk.localChangeStart = actualLineNumber;
} else {
hunk.remoteChangeEnd = actualLineNumber;
$('<span>').text(">>>>>>> Remote Changes").appendTo(line);
$('<span>').text(">>>>>>> " + RED._("diff.remoteChanges")).appendTo(line);
}
diffRow.addClass("mergeHeader-"+(isOurs?"ours":"theirs"));
$('<button class="red-ui-button red-ui-button-small" style="float: right; margin-right: 20px;"><i class="fa fa-angle-double-'+(isOurs?"down":"up")+'"></i> use '+(isOurs?"local":"remote")+' changes</button>')
$('<button class="red-ui-button red-ui-button-small" style="float: right; margin-right: 20px;"><i class="fa fa-angle-double-'+(isOurs?"down":"up")+'"></i> '+RED._(isOurs?"diff.useLocalChanges":"diff.useRemoteChanges")+'</button>')
.appendTo(line)
.on("click", function(evt) {
evt.preventDefault();
@@ -2018,7 +2118,7 @@ RED.diff = (function() {
$("<h3>").text(commit.title).appendTo(content);
$('<div class="commit-body"></div>').text(commit.comment).appendTo(content);
var summary = $('<div class="commit-summary"></div>').appendTo(content);
$('<div style="float: right">').text("Commit "+commit.sha).appendTo(summary);
$('<div style="float: right">').text(RED._('diff.commit')+" "+commit.sha).appendTo(summary);
$('<div>').text((commit.authorName||commit.author)+" - "+options.date).appendTo(summary);
if (commit.files) {

View File

@@ -45,11 +45,13 @@ RED.editor = (function() {
var hasChanged;
if (node.type.indexOf("subflow:")===0) {
subflow = RED.nodes.subflow(node.type.substring(8));
isValid = subflow.valid;
hasChanged = subflow.changed;
if (isValid === undefined) {
isValid = validateNode(subflow);
if (subflow){
isValid = subflow.valid;
hasChanged = subflow.changed;
if (isValid === undefined) {
isValid = validateNode(subflow);
hasChanged = subflow.changed;
}
}
validationErrors = validateNodeProperties(node, node._def.defaults, node);
node.valid = isValid && validationErrors.length === 0;
@@ -113,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);
}
}
@@ -154,6 +157,12 @@ RED.editor = (function() {
}
}
if (valid && "validate" in definition[property]) {
if (definition[property].hasOwnProperty("required") &&
definition[property].required === false) {
if (value === "") {
return true;
}
}
try {
var opt = {};
if (label) {
@@ -163,7 +172,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
@@ -179,6 +188,25 @@ RED.editor = (function() {
error: err.message
});
}
} else if (valid) {
if (definition[property].hasOwnProperty("required") && definition[property].required === false) {
if (value === "") {
return true;
}
}
// 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", { returnErrorMessage: true });
if (typeof valid === "string") {
return label ? label + ": " + valid : valid;
}
}
}
}
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
if (!value || value == "_ADD_") {
@@ -234,10 +262,13 @@ RED.editor = (function() {
var value = input.val();
if (defaults[property].hasOwnProperty("format") && defaults[property].format !== "" && input[0].nodeName === "DIV") {
value = input.text();
} else if (input.attr("type") === "checkbox") {
value = input.prop("checked");
}
var valid = validateNodeProperty(node, defaults, property,value);
if (((typeof valid) === "string") || !valid) {
input.addClass("input-error");
input.next(".red-ui-typedInput-container").addClass("input-error");
if ((typeof valid) === "string") {
var tooltip = input.data("tooltip");
if (tooltip) {
@@ -250,6 +281,7 @@ RED.editor = (function() {
}
} else {
input.removeClass("input-error");
input.next(".red-ui-typedInput-container").removeClass("input-error");
var tooltip = input.data("tooltip");
if (tooltip) {
input.data("tooltip", null);
@@ -310,49 +342,101 @@ RED.editor = (function() {
/**
* Create a config-node select box for this property
* @param node - the node being edited
* @param property - the name of the field
* @param type - the type of the config-node
* @param {Object} node - the node being edited
* @param {String} property - the name of the node property
* @param {String} type - the type of the config-node
* @param {"node-config-input"|"node-input"|"node-input-subflow-env"} prefix - the prefix to use in the input element ids
* @param {Function} [filter] - a function to filter the list of config nodes
* @param {Object} [env] - the environment variable object (only used for subflow env vars)
*/
function prepareConfigNodeSelect(node,property,type,prefix,filter) {
var input = $("#"+prefix+"-"+property);
if (input.length === 0 ) {
function prepareConfigNodeSelect(node, property, type, prefix, filter, env) {
let nodeValue
if (prefix === 'node-input-subflow-env') {
nodeValue = env?.value
} else {
nodeValue = node[property]
}
const addBtnId = `${prefix}-btn-${property}-add`;
const editBtnId = `${prefix}-btn-${property}-edit`;
const selectId = prefix + '-' + property;
const input = $(`#${selectId}`);
if (input.length === 0) {
return;
}
var newWidth = input.width();
var attrStyle = input.attr('style');
var m;
const attrStyle = input.attr('style');
let newWidth;
let m;
if ((m = /(^|\s|;)width\s*:\s*([^;]+)/i.exec(attrStyle)) !== null) {
newWidth = m[2].trim();
} else {
newWidth = "70%";
}
var outerWrap = $("<div></div>").css({
const outerWrap = $("<div></div>").css({
width: newWidth,
display:'inline-flex'
display: 'inline-flex'
});
var select = $('<select id="'+prefix+'-'+property+'"></select>').appendTo(outerWrap);
const select = $('<select id="' + selectId + '"></select>').appendTo(outerWrap);
input.replaceWith(outerWrap);
// set the style attr directly - using width() on FF causes a value of 114%...
select.css({
'flex-grow': 1
});
updateConfigNodeSelect(property,type,node[property],prefix,filter);
$('<a id="'+prefix+'-lookup-'+property+'" class="red-ui-button"><i class="fa fa-pencil"></i></a>')
.css({"margin-left":"10px"})
updateConfigNodeSelect(property, type, nodeValue, prefix, filter);
// create the edit button
const editButton = $('<a id="' + editBtnId + '" class="red-ui-button"><i class="fa fa-pencil"></i></a>')
.css({ "margin-left": "10px" })
.appendTo(outerWrap);
$('#'+prefix+'-lookup-'+property).on("click", function(e) {
showEditConfigNodeDialog(property,type,select.find(":selected").val(),prefix,node);
RED.popover.tooltip(editButton, RED._('editor.editConfig', { type }));
// create the add button
const addButton = $('<a id="' + addBtnId + '" class="red-ui-button"><i class="fa fa-plus"></i></a>')
.css({ "margin-left": "10px" })
.appendTo(outerWrap);
RED.popover.tooltip(addButton, RED._('editor.addNewConfig', { type }));
const disableButton = function(button, disabled) {
$(button).prop("disabled", !!disabled)
$(button).toggleClass("disabled", !!disabled)
};
// add the click handler
addButton.on("click", function (e) {
if (addButton.prop("disabled")) { return }
showEditConfigNodeDialog(property, type, "_ADD_", prefix, node);
e.preventDefault();
});
editButton.on("click", function (e) {
const selectedOpt = select.find(":selected")
if (selectedOpt.data('env')) { return } // don't show the dialog for env vars items (MVP. Future enhancement: lookup the env, if present, show the associated edit dialog)
if (editButton.prop("disabled")) { return }
showEditConfigNodeDialog(property, type, selectedOpt.val(), prefix, node);
e.preventDefault();
});
var label = "";
var configNode = RED.nodes.node(node[property]);
var node_def = RED.nodes.getType(type);
if (configNode) {
label = RED.utils.getNodeLabel(configNode,configNode.id);
}
input.val(label);
// dont permit the user to click the button if the selected option is an env var
select.on("change", function () {
const selectedOpt = select.find(":selected");
const optionsLength = select.find("option").length;
if (selectedOpt?.data('env')) {
disableButton(addButton, true);
disableButton(editButton, true);
// disable the edit button if no options available or 'none' selected
} else if (optionsLength === 1 || selectedOpt.val() === "_ADD_") {
disableButton(addButton, false);
disableButton(editButton, true);
} else {
disableButton(addButton, false);
disableButton(editButton, false);
}
});
// If the value is "", 'add new...' option if no config node available or 'none' option
// Otherwise, it's a config node
select.val(nodeValue || '_ADD_');
}
/**
@@ -716,15 +800,25 @@ RED.editor = (function() {
if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
oldValues[d] = editing_node[d];
} else {
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
// Dont clone the group node `nodes` array
if (editing_node.type !== 'group' || d !== "nodes") {
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
}
}
}
}
try {
var rc = editing_node._def.oneditsave.call(editing_node);
const rc = editing_node._def.oneditsave.call(editing_node);
if (rc === true) {
editState.changed = true;
} else if (typeof rc === 'object' && rc !== null ) {
if (rc.changed === true) {
editState.changed = true
}
if (Array.isArray(rc.history) && rc.history.length > 0) {
editState.history = rc.history
}
}
} catch(err) {
console.warn("oneditsave",editing_node.id,editing_node.type,err.toString());
@@ -749,12 +843,9 @@ RED.editor = (function() {
}
function defaultConfigNodeSort(A,B) {
if (A.__label__ < B.__label__) {
return -1;
} else if (A.__label__ > B.__label__) {
return 1;
}
return 0;
// sort case insensitive so that `[env] node-name` items are at the top and
// not mixed inbetween the the lower and upper case items
return (A.__label__ || '').localeCompare((B.__label__ || ''), undefined, {sensitivity: 'base'})
}
function updateConfigNodeSelect(name,type,value,prefix,filter) {
@@ -769,7 +860,7 @@ RED.editor = (function() {
}
$("#"+prefix+"-"+name).val(value);
} else {
let inclSubflowEnvvars = false
var select = $("#"+prefix+"-"+name);
var node_def = RED.nodes.getType(type);
select.children().remove();
@@ -777,6 +868,7 @@ RED.editor = (function() {
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
if (!activeWorkspace) {
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
inclSubflowEnvvars = true
}
var configNodes = [];
@@ -792,6 +884,31 @@ RED.editor = (function() {
}
}
});
// as includeSubflowEnvvars is true, this is a subflow.
// include any 'conf-types' env vars as a list of avaiable configs
// in the config dropdown as `[env] node-name`
if (inclSubflowEnvvars && activeWorkspace.env) {
const parentEnv = activeWorkspace.env.filter(env => env.ui?.type === 'conf-types' && env.type === type)
if (parentEnv && parentEnv.length > 0) {
const locale = RED.i18n.lang()
for (let i = 0; i < parentEnv.length; i++) {
const tenv = parentEnv[i]
const ui = tenv.ui || {}
const labels = ui.label || {}
const labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"] || tenv.name, locale)
const config = {
env: tenv,
id: '${' + tenv.name + '}',
type: type,
label: labelText,
__label__: `[env] ${labelText}`
}
configNodes.push(config)
}
}
}
var configSortFn = defaultConfigNodeSort;
if (typeof node_def.sort == "function") {
configSortFn = node_def.sort;
@@ -803,7 +920,10 @@ RED.editor = (function() {
}
configNodes.forEach(function(cn) {
$('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
const option = $('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
if (cn.env) {
option.data('env', cn.env) // set a data attribute to indicate this is an env var (to inhibit the edit button)
}
delete cn.__label__;
});
@@ -816,7 +936,14 @@ RED.editor = (function() {
}
}
select.append('<option value="_ADD_"'+(value===""?" selected":"")+'>'+RED._("editor.addNewType", {type:label})+'</option>');
if (!configNodes.length) {
// Add 'add new...' option
select.append('<option value="_ADD_" selected>' + RED._("editor.addNewType", { type: label }) + '</option>');
} else {
// Add 'none' option
select.append('<option value="_ADD_">' + RED._("editor.inputs.none") + '</option>');
}
window.setTimeout(function() { select.trigger("change");},50);
}
}
@@ -858,6 +985,7 @@ RED.editor = (function() {
function showEditDialog(node, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
if (node.z && RED.workspaces.isLocked(node.z)) { return }
var editing_node = node;
var removeInfoEditorOnClose = false;
var skipInfoRefreshOnClose = false;
@@ -894,6 +1022,17 @@ RED.editor = (function() {
dirty: startDirty
}
if (editing_node.g) {
const group = RED.nodes.group(editing_node.g);
// Don't use RED.group.removeFromGroup as that emits
// a change event on the node - but we're deleting it
const index = group?.nodes.indexOf(editing_node) ?? -1;
if (index > -1) {
group.nodes.splice(index, 1);
RED.group.markDirty(group);
}
}
RED.nodes.dirty(true);
RED.view.redraw(true);
RED.history.push(historyEvent);
@@ -995,7 +1134,7 @@ RED.editor = (function() {
}
});
}
var historyEvent = {
let historyEvent = {
t:'edit',
node:editing_node,
changes:editState.changes,
@@ -1011,6 +1150,15 @@ RED.editor = (function() {
instances:subflowInstances
}
}
if (editState.history) {
historyEvent = {
t: 'multi',
events: [ historyEvent, ...editState.history ],
dirty: wasDirty
}
}
RED.history.push(historyEvent);
}
editing_node.dirty = true;
@@ -1043,6 +1191,13 @@ RED.editor = (function() {
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.sidebar.help.show(editing_node.type);
}).appendTo(trayFooterLeft);
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
$('<input id="node-input-node-disabled" type="checkbox">').prop("checked",!!node.d).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
@@ -1146,6 +1301,8 @@ RED.editor = (function() {
var editing_config_node = RED.nodes.node(id);
var activeEditPanes = [];
if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return }
var configNodeScope = ""; // default to global
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
if (activeSubflow) {
@@ -1188,6 +1345,13 @@ RED.editor = (function() {
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.sidebar.help.show(editing_config_node.type);
}).appendTo(trayFooterLeft);
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
$('<input id="node-config-input-node-disabled" type="checkbox">').prop("checked",!!editing_config_node.d).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
@@ -1195,7 +1359,11 @@ RED.editor = (function() {
})
if (node_def.hasUsers !== false) {
$('<span><i class="fa fa-info-circle"></i> <span id="red-ui-editor-config-user-count"></span></span>').css("margin-left", "10px").appendTo(trayFooterLeft);
// $('<span><i class="fa fa-info-circle"></i> <span id="red-ui-editor-config-user-count"></span></span>').css("margin-left", "10px").appendTo(trayFooterLeft);
$('<button type="button" class="red-ui-button"><i class="fa fa-user"></i><span id="red-ui-editor-config-user-count"></span></button>').on('click', function() {
RED.sidebar.info.outliner.search('uses:'+editing_config_node.id)
RED.sidebar.info.show()
}).appendTo(trayFooterLeft);
}
trayFooter.append('<span class="red-ui-tray-footer-right"><span id="red-ui-editor-config-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="red-ui-editor-config-scope"></select></span>');
@@ -1253,7 +1421,8 @@ RED.editor = (function() {
});
}
if (node_def.hasUsers !== false) {
$("#red-ui-editor-config-user-count").text(RED._("editor.nodesUse", {count:editing_config_node.users.length})).parent().show();
$("#red-ui-editor-config-user-count").text(editing_config_node.users.length).parent().show();
RED.popover.tooltip($("#red-ui-editor-config-user-count").parent(), function() { return RED._('editor.nodesUse',{count:editing_config_node.users.length})});
}
trayBody.i18n();
trayFooter.i18n();
@@ -1442,9 +1611,16 @@ RED.editor = (function() {
}
RED.tray.close(function() {
var filter = null;
if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') {
filter = function(n) {
return editContext._def.defaults[configProperty].filter.call(editContext,n);
// when editing a config via subflow edit panel, the `configProperty` will not
// necessarily be a property of the editContext._def.defaults object
// Also, when editing via dashboard sidebar, editContext can be null
// so we need to guard both scenarios
if (editContext?._def) {
const isSubflow = (editContext._def.type === 'subflow' || /subflow:.*/.test(editContext._def.type))
if (editContext && !isSubflow && typeof editContext._def.defaults?.[configProperty]?.filter === 'function') {
filter = function(n) {
return editContext._def.defaults[configProperty].filter.call(editContext,n);
}
}
}
updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter);
@@ -1505,7 +1681,7 @@ RED.editor = (function() {
RED.history.push(historyEvent);
RED.tray.close(function() {
var filter = null;
if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') {
if (editContext && typeof editContext._def.defaults[configProperty]?.filter === 'function') {
filter = function(n) {
return editContext._def.defaults[configProperty].filter.call(editContext,n);
}
@@ -1582,8 +1758,8 @@ RED.editor = (function() {
}
if (!isSameObj(old_env, new_env)) {
editing_node.env = new_env;
editState.changes.env = editing_node.env;
editing_node.env = new_env;
editState.changed = true;
}
@@ -1692,6 +1868,7 @@ RED.editor = (function() {
function showEditGroupDialog(group, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
if (group.z && RED.workspaces.isLocked(group.z)) { return }
var editing_node = group;
editStack.push(group);
RED.view.state(RED.state.EDITING);
@@ -1851,11 +2028,15 @@ RED.editor = (function() {
workspace.disabled = disabled;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
if (workspace.id === RED.workspaces.active()) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
}
var locked = $("#node-input-locked").prop("checked");
if (workspace.locked !== locked) {
editState.changes.locked = workspace.locked;
editState.changed = true;
workspace.locked = locked;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
}
if (editState.changed) {
var historyEvent = {
t: "edit",
@@ -1896,6 +2077,7 @@ RED.editor = (function() {
var trayBody = tray.find('.red-ui-tray-body');
trayBody.parent().css('overflow','hidden');
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var trayFooterRight = $('<div class="red-ui-tray-footer-right"></div>').appendTo(trayFooter)
var nodeEditPanes = [
'editor-tab-flow-properties',
@@ -1910,6 +2092,18 @@ RED.editor = (function() {
disabledIcon: "fa-ban",
invertState: true
})
if (!workspace.hasOwnProperty("locked")) {
workspace.locked = false;
}
$('<input id="node-input-locked" type="checkbox">').prop("checked",workspace.locked).appendTo(trayFooterRight).toggleButton({
enabledLabel: RED._("common.label.unlocked"),
enabledIcon: "fa-unlock-alt",
disabledLabel: RED._("common.label.locked"),
disabledIcon: "fa-lock",
invertState: true
})
prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", defaultTab, function(_activeEditPanes) {
activeEditPanes = _activeEditPanes;
trayBody.i18n();
@@ -2028,6 +2222,7 @@ RED.editor = (function() {
}
},
editBuffer: function(options) { showTypeEditor("_buffer", options) },
getEditStack: function () { return [...editStack] },
buildEditForm: buildEditForm,
validateNode: validateNode,
updateNodeProperties: updateNodeProperties,
@@ -2072,6 +2267,7 @@ RED.editor = (function() {
filteredEditPanes[type] = filter
}
editPanes[type] = definition;
}
},
prepareConfigNodeSelect: prepareConfigNodeSelect,
}
})();

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

@@ -45,6 +45,9 @@
selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
initialised = selectedCodeEditor.init();
}
$('<div id="red-ui-image-drop-target"><div data-i18n="[append]workspace.dropImageHere"><i class="fa fa-download"></i><br></div></div>').appendTo('#red-ui-editor');
$("#red-ui-image-drop-target").hide();
}
function create(options) {
@@ -64,6 +67,7 @@
options = {};
}
var editor = null;
if (this.editor.type === MONACO) {
// compatibility (see above note)
if (!options.element && !options.id) {
@@ -74,10 +78,14 @@
console.warn("createEditor() options.element or options.id is not valid", options);
$("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />');
}
return this.editor.create(options);
editor = this.editor.create(options);
} else {
return this.editor.create(options);//fallback to ACE
editor = this.editor.create(options);//fallback to ACE
}
if (options.mode === "ace/mode/markdown") {
RED.editor.customEditTypes['_markdown'].postInit(editor, options);
}
return editor;
}
return {

View File

@@ -59,18 +59,21 @@ RED.editor.codeEditor.monaco = (function() {
//TODO: get from externalModules.js For now this is enough for feature parity with ACE (and then some).
const knownModules = {
"assert": {package: "node", module: "assert", path: "node/assert.d.ts" },
"assert/strict": {package: "node", module: "assert/strict", path: "node/assert/strict.d.ts" },
"async_hooks": {package: "node", module: "async_hooks", path: "node/async_hooks.d.ts" },
"buffer": {package: "node", module: "buffer", path: "node/buffer.d.ts" },
"child_process": {package: "node", module: "child_process", path: "node/child_process.d.ts" },
"cluster": {package: "node", module: "cluster", path: "node/cluster.d.ts" },
"console": {package: "node", module: "console", path: "node/console.d.ts" },
"constants": {package: "node", module: "constants", path: "node/constants.d.ts" },
"crypto": {package: "node", module: "crypto", path: "node/crypto.d.ts" },
"dgram": {package: "node", module: "dgram", path: "node/dgram.d.ts" },
"diagnostics_channel.d": {package: "node", module: "diagnostics_channel", path: "node/diagnostics_channel.d.ts" },
"dns": {package: "node", module: "dns", path: "node/dns.d.ts" },
"dns/promises": {package: "node", module: "dns/promises", path: "node/dns/promises.d.ts" },
"domain": {package: "node", module: "domain", path: "node/domain.d.ts" },
"events": {package: "node", module: "events", path: "node/events.d.ts" },
"fs": {package: "node", module: "fs", path: "node/fs.d.ts" },
"fs/promises": {package: "node", module: "fs/promises", path: "node/fs/promises.d.ts" },
"globals": {package: "node", module: "globals", path: "node/globals.d.ts" },
"http": {package: "node", module: "http", path: "node/http.d.ts" },
"http2": {package: "node", module: "http2", path: "node/http2.d.ts" },
@@ -84,8 +87,13 @@ RED.editor.codeEditor.monaco = (function() {
"querystring": {package: "node", module: "querystring", path: "node/querystring.d.ts" },
"readline": {package: "node", module: "readline", path: "node/readline.d.ts" },
"stream": {package: "node", module: "stream", path: "node/stream.d.ts" },
"stream/consumers": {package: "node", module: "stream/consumers", path: "node/stream/consumers.d.ts" },
"stream/promises": {package: "node", module: "stream/promises", path: "node/stream/promises.d.ts" },
"stream/web": {package: "node", module: "stream/web", path: "node/stream/web.d.ts" },
"string_decoder": {package: "node", module: "string_decoder", path: "node/string_decoder.d.ts" },
"test": {package: "node", module: "test", path: "node/test.d.ts" },
"timers": {package: "node", module: "timers", path: "node/timers.d.ts" },
"timers/promises": {package: "node", module: "timers/promises", path: "node/timers/promises.d.ts" },
"tls": {package: "node", module: "tls", path: "node/tls.d.ts" },
"trace_events": {package: "node", module: "trace_events", path: "node/trace_events.d.ts" },
"tty": {package: "node", module: "tty", path: "node/tty.d.ts" },
@@ -100,7 +108,7 @@ RED.editor.codeEditor.monaco = (function() {
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
}
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"], knownModules["timers"] , knownModules["util"] ];
const modulesCache = {};
@@ -157,7 +165,13 @@ RED.editor.codeEditor.monaco = (function() {
//Handles orphaned models
//ensure loaded models that are not explicitly destroyed by a call to .destroy() are disposed
RED.events.on("editor:close",function() {
let models = window.monaco ? monaco.editor.getModels() : null;
if (!window.monaco) { return; }
const editors = window.monaco.editor.getEditors()
const orphanEditors = editors.filter(editor => editor && !document.body.contains(editor.getDomNode()))
orphanEditors.forEach(editor => {
editor.dispose();
});
let models = monaco.editor.getModels()
if(models && models.length) {
console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().")
for (let index = 0; index < models.length; index++) {
@@ -506,7 +520,7 @@ RED.editor.codeEditor.monaco = (function() {
_monaco.languages.json.jsonDefaults.setDiagnosticsOptions(diagnosticOptions);
if(modeConfiguration) { _monaco.languages.json.jsonDefaults.setModeConfiguration(modeConfiguration); }
} catch (error) {
console.warn("monaco - Error setting up json options", err)
console.warn("monaco - Error setting up json options", error)
}
}
@@ -518,7 +532,7 @@ RED.editor.codeEditor.monaco = (function() {
if(htmlDefaults) { _monaco.languages.html.htmlDefaults.setOptions(htmlDefaults); }
if(handlebarDefaults) { _monaco.languages.html.handlebarDefaults.setOptions(handlebarDefaults); }
} catch (error) {
console.warn("monaco - Error setting up html options", err)
console.warn("monaco - Error setting up html options", error)
}
}
@@ -538,7 +552,7 @@ RED.editor.codeEditor.monaco = (function() {
if(lessDefaults_modeConfiguration) { _monaco.languages.css.cssDefaults.setDiagnosticsOptions(lessDefaults_modeConfiguration); }
if(scssDefaults_modeConfiguration) { _monaco.languages.css.cssDefaults.setDiagnosticsOptions(scssDefaults_modeConfiguration); }
} catch (error) {
console.warn("monaco - Error setting up CSS/SCSS/LESS options", err)
console.warn("monaco - Error setting up CSS/SCSS/LESS options", error)
}
}
@@ -577,7 +591,7 @@ RED.editor.codeEditor.monaco = (function() {
createMonacoCompletionItem("set (flow context)", 'flow.set("${1:name}", ${1:value});','Set a value in flow context',range),
createMonacoCompletionItem("get (global context)", 'global.get("${1:name}");','Get a value from global context',range),
createMonacoCompletionItem("set (global context)", 'global.set("${1:name}", ${1:value});','Set a value in global context',range),
createMonacoCompletionItem("get (env)", 'env.get("${1|NR_NODE_ID,NR_NODE_NAME,NR_NODE_PATH,NR_GROUP_ID,NR_GROUP_NAME,NR_FLOW_ID,NR_FLOW_NAME|}");','Get env variable value',range),
createMonacoCompletionItem("get (env)", 'env.get("${1|NR_NODE_ID,NR_NODE_NAME,NR_NODE_PATH,NR_GROUP_ID,NR_GROUP_NAME,NR_FLOW_ID,NR_FLOW_NAME,NR_SUBFLOW_NAME,NR_SUBFLOW_ID,NR_SUBFLOW_PATH|}");','Get env variable value',range),
createMonacoCompletionItem("cloneMessage (RED.util)", 'RED.util.cloneMessage(${1:msg});',
["```typescript",
"RED.util.cloneMessage<T extends registry.NodeMessage>(msg: T): T",
@@ -958,12 +972,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
@@ -1118,6 +1130,7 @@ RED.editor.codeEditor.monaco = (function() {
$(el).remove();
$(toolbarRow).remove();
ed.dispose();
}
ed.resize = function resize() {
@@ -1160,19 +1173,19 @@ RED.editor.codeEditor.monaco = (function() {
// Warning: 4
// Error: 8
ed.getAnnotations = function getAnnotations() {
var aceCompatibleMarkers = [];
let aceCompatibleMarkers;
try {
var _model = ed.getModel();
const _model = ed.getModel();
if (_model !== null) {
var id = _model._languageId; // e.g. javascript
var ra = _model._associatedResource.authority; //e.g. model
var rp = _model._associatedResource.path; //e.g. /18
var rs = _model._associatedResource.scheme; //e.g. inmemory
var modelMarkers = monaco.editor.getModelMarkers(_model) || [];
var thisEditorsMarkers = modelMarkers.filter(function (marker) {
var _ra = marker.resource.authority; //e.g. model
var _rp = marker.resource.path; //e.g. /18
var _rs = marker.resource.scheme; //e.g. inmemory
const id = _model.getLanguageId(); // e.g. javascript
const ra = _model.uri.authority; // e.g. model
const rp = _model.uri.path; // e.g. /18
const rs = _model.uri.scheme; // e.g. inmemory
const modelMarkers = monaco.editor.getModelMarkers(_model) || [];
const thisEditorsMarkers = modelMarkers.filter(function (marker) {
const _ra = marker.resource.authority; // e.g. model
const _rp = marker.resource.path; // e.g. /18
const _rs = marker.resource.scheme; // e.g. inmemory
return marker.owner == id && _ra === ra && _rp === rp && _rs === rs;
})
aceCompatibleMarkers = thisEditorsMarkers.map(function (marker) {

View File

@@ -1,8 +1,9 @@
RED.editor.envVarList = (function() {
var currentLocale = 'en-US';
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred'];
const DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
const DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES = ['str','num','bool','json','bin','env','conf-types'];
const DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
/**
* Create env var edit interface
@@ -10,8 +11,8 @@ RED.editor.envVarList = (function() {
* @param node - subflow node
*/
function buildPropertiesList(envContainer, node) {
var isTemplateNode = (node.type === "subflow");
if(RED.editor.envVarList.debug) { console.log('envVarList: buildPropertiesList', envContainer, node) }
const isTemplateNode = (node.type === "subflow");
envContainer
.css({
@@ -83,7 +84,14 @@ RED.editor.envVarList = (function() {
// if `opt.ui` does not exist, then apply defaults. If these
// defaults do not change then they will get stripped off
// before saving.
if (opt.type === 'cred') {
if (opt.type === 'conf-types') {
opt.ui = opt.ui || {
icon: "fa fa-cog",
type: "conf-types",
opts: {opts:[]}
}
opt.ui.type = "conf-types";
} else if (opt.type === 'cred') {
opt.ui = opt.ui || {
icon: "",
type: "cred"
@@ -119,7 +127,7 @@ RED.editor.envVarList = (function() {
}
});
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
buildEnvEditRow(uiRow, opt, nameField, valueField);
nameField.trigger('change');
}
},
@@ -181,21 +189,23 @@ RED.editor.envVarList = (function() {
* @param nameField - name field of env var
* @param valueField - value field of env var
*/
function buildEnvEditRow(container, ui, nameField, valueField) {
function buildEnvEditRow(container, opt, nameField, valueField) {
const ui = opt.ui
if(RED.editor.envVarList.debug) { console.log('envVarList: buildEnvEditRow', container, ui, nameField, valueField) }
container.addClass("red-ui-editor-subflow-env-ui-row")
var topRow = $('<div></div>').appendTo(container);
$('<div></div>').appendTo(topRow);
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
$('<div>').text(RED._("editor.label")).appendTo(topRow);
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
$('<div class="red-env-ui-input-type-col">').text(RED._("editor.inputType")).appendTo(topRow);
var row = $('<div></div>').appendTo(container);
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
var typeOptions = {
'input': {types:DEFAULT_ENV_TYPE_LIST},
'select': {opts:[]},
'spinner': {},
'cred': {}
'input': {types:DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES},
'select': {opts:[]},
'spinner': {},
'cred': {}
};
if (ui.opts) {
typeOptions[ui.type] = ui.opts;
@@ -260,15 +270,16 @@ RED.editor.envVarList = (function() {
labelInput.attr("placeholder",$(this).val())
});
var inputCell = $('<div></div>').appendTo(row);
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
var inputCell = $('<div class="red-env-ui-input-type-col"></div>').appendTo(row);
var uiInputTypeInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
if (ui.type === "input") {
inputCellInput.val(ui.opts.types.join(","));
uiInputTypeInput.val(ui.opts.types.join(","));
}
var checkbox;
var selectBox;
inputCellInput.typedInput({
// the options presented in the UI section for an "input" type selection
uiInputTypeInput.typedInput({
types: [
{
value:"input",
@@ -429,7 +440,7 @@ RED.editor.envVarList = (function() {
}
});
ui.opts.opts = vals;
inputCellInput.typedInput('value',Date.now())
uiInputTypeInput.typedInput('value',Date.now())
}
}
}
@@ -496,12 +507,13 @@ RED.editor.envVarList = (function() {
} else {
delete ui.opts.max;
}
inputCellInput.typedInput('value',Date.now())
uiInputTypeInput.typedInput('value',Date.now())
}
}
}
}
},
'conf-types',
{
value:"none",
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
@@ -519,14 +531,20 @@ RED.editor.envVarList = (function() {
// In the case of 'input' type, the typedInput uses the multiple-option
// mode. Its value needs to be set to a comma-separately list of the
// selected options.
inputCellInput.typedInput('value',ui.opts.types.join(","))
uiInputTypeInput.typedInput('value',ui.opts.types.join(","))
} else if (ui.type === 'conf-types') {
// In the case of 'conf-types' type, the typedInput will be populated
// with a list of all config nodes types installed.
// Restore the value to the last selected type
uiInputTypeInput.typedInput('value', opt.type)
} else {
// No other type cares about `value`, but doing this will
// force a refresh of the label now that `ui.opts` has
// been updated.
inputCellInput.typedInput('value',Date.now())
uiInputTypeInput.typedInput('value',Date.now())
}
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:typedinputtypechange. ui.type = ' + ui.type) }
switch (ui.type) {
case 'input':
valueField.typedInput('types',ui.opts.types);
@@ -544,7 +562,7 @@ RED.editor.envVarList = (function() {
valueField.typedInput('types',['cred']);
break;
default:
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
valueField.typedInput('types', DEFAULT_ENV_TYPE_LIST);
}
if (ui.type === 'checkbox') {
valueField.typedInput('type','bool');
@@ -556,8 +574,46 @@ RED.editor.envVarList = (function() {
}
}).on("change", function(evt,type) {
if (ui.type === 'input') {
var types = inputCellInput.typedInput('value');
const selectedType = $(this).typedInput('type') // the UI typedInput type
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:change. selectedType = ' + selectedType) }
if (selectedType === 'conf-types') {
const selectedConfigType = $(this).typedInput('value') || opt.type
let activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
if (!activeWorkspace) {
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
}
// get a list of all config nodes matching the selectedValue
const configNodes = [];
RED.nodes.eachConfig(function(config) {
if (config.type == selectedConfigType && (!config.z || config.z === activeWorkspace.id)) {
const modulePath = config._def?.set?.id || ''
let label = RED.utils.getNodeLabel(config, config.id) || config.id;
label += config.d ? ' ['+RED._('workspace.disabled')+']' : '';
const _config = {
_type: selectedConfigType,
value: config.id,
label: label,
title: modulePath ? modulePath + ' - ' + label : label,
enabled: config.d !== true,
disabled: config.d === true,
}
configNodes.push(_config);
}
});
const tiTypes = {
value: selectedConfigType,
label: "config",
icon: "fa fa-cog",
options: configNodes,
}
valueField.typedInput('types', [tiTypes]);
valueField.typedInput('type', selectedConfigType);
valueField.typedInput('value', opt.value);
} else if (ui.type === 'input') {
var types = uiInputTypeInput.typedInput('value');
ui.opts.types = (types === "") ? ["str"] : types.split(",");
valueField.typedInput('types',ui.opts.types);
}
@@ -569,7 +625,7 @@ RED.editor.envVarList = (function() {
})
// Set the input to the right type. This will trigger the 'typedinputtypechange'
// event handler (just above ^^) to update the value if needed
inputCellInput.typedInput('type',ui.type)
uiInputTypeInput.typedInput('type',ui.type)
}
function setLocale(l, list) {

View File

@@ -294,32 +294,37 @@
}
try {
var result = expr.evaluate(legacyMode?{msg:parsedData}:parsedData);
if (usesContext) {
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return;
}
if (usesEnv) {
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
return;
}
if (usesMoment) {
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
return;
}
if (usesClone) {
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
return;
}
var formattedResult;
if (result !== undefined) {
formattedResult = JSON.stringify(result,null,4);
} else {
formattedResult = RED._("expressionEditor.noMatch");
}
testResultEditor.setValue(formattedResult,-1);
} catch(err) {
expr.evaluate(legacyMode?{msg:parsedData}:parsedData, null, (err, result) => {
if (err) {
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
} else {
if (usesContext) {
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return;
}
if (usesEnv) {
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
return;
}
if (usesMoment) {
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
return;
}
if (usesClone) {
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
return;
}
var formattedResult;
if (result !== undefined) {
formattedResult = JSON.stringify(result,null,4);
} else {
formattedResult = RED._("expressionEditor.noMatch");
}
testResultEditor.setValue(formattedResult,-1);
}
});
} catch(err) {
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
}
}

View File

@@ -14,6 +14,61 @@
* limitations under the License.
**/
(function() {
/**
* Converts dropped image file to date URL
*/
function file2base64Image(file, cb) {
var reader = new FileReader();
reader.onload = (function (fd) {
return function (e) {
cb(e.target.result);
};
})(file);
reader.readAsDataURL(file);
}
var initialized = false;
var currentEditor = null;
/**
* Initialize handler for image file drag events
*/
function initImageDrag(elem, editor) {
$(elem).on("dragenter", function (ev) {
ev.preventDefault();
$("#red-ui-image-drop-target").css({display:'table'}).focus();
currentEditor = editor;
});
if (!initialized) {
initialized = true;
$("#red-ui-image-drop-target").on("dragover", function (ev) {
ev.preventDefault();
}).on("dragleave", function (ev) {
$("#red-ui-image-drop-target").hide();
}).on("drop", function (ev) {
ev.preventDefault();
if ($.inArray("Files",ev.originalEvent.dataTransfer.types) != -1) {
var files = ev.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var name = file.name.toLowerCase();
if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) {
file2base64Image(file, function (image) {
var session = currentEditor.getSession();
var img = `<img src="${image}"/>\n`;
var pos = session.getCursorPosition();
session.insert(pos, img);
$("#red-ui-image-drop-target").hide();
});
return;
}
}
}
$("#red-ui-image-drop-target").hide();
});
}
}
var toolbarTemplate = '<div style="margin-bottom: 5px">'+
'<span class="button-group">'+
@@ -114,6 +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);
RED.editor.mermaid.render()
},200);
})
if (options.header) {
@@ -122,6 +178,7 @@
if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
RED.editor.mermaid.render()
}
panels = RED.panels.create({
id:"red-ui-editor-type-markdown-panels",
@@ -148,10 +205,14 @@
});
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
if (options.cursor && !expressionEditor._initState) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
if(!expressionEditor._initState) {
if (options.cursor) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
else {
expressionEditor.gotoLine(0, 0, false);
}
}
dialogForm.i18n();
},
close: function() {
@@ -215,7 +276,11 @@
}
})
return toolbar;
}
},
postInit: function (editor, options) {
var elem = $("#"+options.id);
initImageDrag(elem, editor);
}
}
RED.editor.registerTypeEditor("_markdown", definition);
})();

View File

@@ -0,0 +1,67 @@
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
// Find the cache-buster:
let cacheBuster
$('script').each(function (i, el) {
if (!cacheBuster) {
const src = el.getAttribute('src')
const m = /\?v=(.+)$/.exec(src)
if (m) {
cacheBuster = m[1]
}
}
})
$.ajax({
url: `vendor/mermaid/mermaid.min.js?v=${cacheBuster}`,
dataType: "script",
cache: true,
success: 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

@@ -196,7 +196,7 @@
}
$('<div class="form-row">'+
'<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+
'<label for="node-input-show-label" data-i18n="editor.label"></label>'+
'<span style="margin-right: 2px;"/>'+
'<input type="checkbox" id="node-input-show-label"/>'+
'</div>').appendTo(dialogForm);

View File

@@ -52,8 +52,6 @@
node.info = info;
}
$("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.disabled);
}
}
});

View File

@@ -0,0 +1,185 @@
RED.envVar = (function() {
function saveEnvList(list) {
const items = list.editableList("items")
const new_env = [];
items.each(function (i,el) {
var data = el.data('data');
var item;
if (data.nameField && data.valueField) {
item = {
name: data.nameField.val(),
value: data.valueField.typedInput("value"),
type: data.valueField.typedInput("type")
};
new_env.push(item);
}
});
return new_env;
}
function getGlobalConf(create) {
var gconf = null;
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
gconf = conf;
}
});
if ((gconf === null) && create) {
var cred = {
_ : {},
map: {}
};
gconf = {
id: RED.nodes.id(),
type: "global-config",
env: [],
name: "global-config",
label: "",
hasUsers: false,
users: [],
credentials: cred,
_def: RED.nodes.getType("global-config"),
};
RED.nodes.add(gconf);
}
return gconf;
}
function applyChanges(list) {
var gconf = getGlobalConf(false);
var new_env = [];
var items = list.editableList('items');
var credentials = gconf ? gconf.credentials : null;
if (!gconf && list.editableList('length') === 0) {
// No existing global-config node and nothing in the list,
// so no need to do anything more
return
}
if (!credentials) {
credentials = {
_ : {},
map: {}
};
}
items.each(function (i,el) {
var data = el.data('data');
if (data.nameField && data.valueField) {
var item = {
name: data.nameField.val(),
value: data.valueField.typedInput("value"),
type: data.valueField.typedInput("type")
};
if (item.name.trim() !== "") {
new_env.push(item);
if (item.type === "cred") {
credentials.map[item.name] = item.value;
credentials.map["has_"+item.name] = (item.value !== "");
item.value = "__PWRD__";
}
}
}
});
if (gconf === null) {
gconf = getGlobalConf(true);
}
if (!gconf.credentials) {
gconf.credentials = {
_ : {},
map: {}
};
}
if ((JSON.stringify(new_env) !== JSON.stringify(gconf.env)) ||
(JSON.stringify(credentials) !== JSON.stringify(gconf.credentials))) {
gconf.env = new_env;
gconf.credentials = credentials;
RED.nodes.dirty(true);
}
}
function getSettingsPane() {
var gconf = getGlobalConf(false);
var env = gconf ? gconf.env : [];
var cred = gconf ? gconf.credentials : null;
if (!cred) {
cred = {
_ : {},
map: {}
};
}
var pane = $("<div/>", {
id: "red-ui-settings-tab-envvar",
class: "form-horizontal"
});
var content = $("<div/>", {
class: "form-row node-input-env-container-row"
}).css({
"margin": "10px"
}).appendTo(pane);
var label = $("<label></label>").css({
width: "100%"
}).appendTo(content);
$("<i/>", {
class: "fa fa-list"
}).appendTo(label);
$("<span/>").text(" "+RED._("env-var.header")).appendTo(label);
var list = $("<ol/>", {
id: "node-input-env-container"
}).appendTo(content);
var node = {
type: "",
env: env,
credentials: cred.map,
};
RED.editor.envVarList.create(list, node);
var buttons = $("<div/>").css({
"text-align": "right",
}).appendTo(content);
var revertButton = $("<button/>", {
class: "red-ui-button"
}).css({
}).text(RED._("env-var.revert")).appendTo(buttons);
var items = saveEnvList(list);
revertButton.on("click", function (ev) {
list.editableList("empty");
list.editableList("addItems", items);
});
return pane;
}
function init(done) {
RED.userSettings.add({
id:'envvar',
title: RED._("env-var.environment"),
get: getSettingsPane,
focus: function() {
var height = $("#red-ui-settings-tab-envvar").parent().height();
$("#node-input-env-container").editableList("height", (height -100));
},
close: function() {
var list = $("#node-input-env-container");
try {
applyChanges(list);
}
catch (e) {
console.log(e);
console.log(e.stack);
}
}
});
RED.actions.add("core:show-global-env", function() {
RED.userSettings.show('envvar');
});
}
return {
init: init,
};
})();

View File

@@ -188,6 +188,8 @@ RED.group = (function() {
var activateMerge = false;
var activateRemove = false;
var singleGroupSelected = false;
var locked = RED.workspaces.isLocked()
if (activateGroup) {
singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
selection.nodes.forEach(function (n) {
@@ -202,12 +204,12 @@ RED.group = (function() {
activateMerge = (selection.nodes.length > 1);
}
}
RED.menu.setDisabled("menu-item-group-group", !activateGroup);
RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
RED.menu.setDisabled("menu-item-group-group", locked || !activateGroup);
RED.menu.setDisabled("menu-item-group-ungroup", locked || !activateUngroup);
RED.menu.setDisabled("menu-item-group-merge", locked || !activateMerge);
RED.menu.setDisabled("menu-item-group-remove", locked || !activateRemove);
RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected);
RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup);
RED.menu.setDisabled("menu-item-edit-paste-group-style", locked || !activateUngroup);
});
RED.actions.add("core:group-selection", function() { groupSelection() })
@@ -264,6 +266,7 @@ RED.group = (function() {
}
}
function pasteGroupStyle() {
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
if (groupStyleClipboard) {
var selection = RED.view.selection();
@@ -298,6 +301,7 @@ RED.group = (function() {
}
function groupSelection() {
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -316,11 +320,12 @@ RED.group = (function() {
}
}
function ungroupSelection() {
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
var newSelection = [];
groups = selection.nodes.filter(function(n) { return n.type === "group" });
let groups = selection.nodes.filter(function(n) { return n.type === "group" });
var historyEvent = {
t:"ungroup",
@@ -339,6 +344,7 @@ RED.group = (function() {
}
function ungroup(g) {
if (RED.workspaces.isLocked()) { return }
var nodes = [];
var parentGroup = RED.nodes.group(g.g);
g.nodes.forEach(function(n) {
@@ -365,6 +371,7 @@ RED.group = (function() {
}
function mergeSelection() {
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -394,7 +401,7 @@ RED.group = (function() {
}
}
var existingGroup;
var mergedEnv = {}
// Second pass, ungroup any groups in the selection and add their contents
// to the selection
for (var i=0; i<selection.nodes.length; i++) {
@@ -403,6 +410,11 @@ RED.group = (function() {
if (!existingGroup) {
existingGroup = n;
}
if (n.env && n.env.length > 0) {
n.env.forEach(env => {
mergedEnv[env.name] = env
})
}
ungroupHistoryEvent.groups.push(n);
nodes = nodes.concat(ungroup(n));
} else {
@@ -420,6 +432,7 @@ RED.group = (function() {
group.style = existingGroup.style;
group.name = existingGroup.name;
}
group.env = Object.values(mergedEnv)
RED.view.select({nodes:[group]})
}
historyEvent.events.push({
@@ -434,6 +447,7 @@ RED.group = (function() {
}
function removeSelection() {
if (RED.workspaces.isLocked()) { return }
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
@@ -461,12 +475,21 @@ RED.group = (function() {
}
}
function createGroup(nodes) {
if (RED.workspaces.isLocked()) { return }
if (nodes.length === 0) {
return;
}
if (nodes.filter(function(n) { return n.type === "subflow" }).length > 0) {
RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error");
return;
const existingGroup = nodes[0].g
for (let i = 0; i < nodes.length; i++) {
const n = nodes[i]
if (n.type === 'subflow') {
RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error");
return;
}
if (n.g !== existingGroup) {
console.warn("Cannot add nooes with different z properties")
return
}
}
// nodes is an array
// each node must be on the same tab (z)
@@ -479,11 +502,16 @@ RED.group = (function() {
y: Number.POSITIVE_INFINITY,
w: 0,
h: 0,
_def: RED.group.def
_def: RED.group.def,
changed: true
}
group.z = nodes[0].z;
RED.nodes.addGroup(group);
group = RED.nodes.addGroup(group);
if (existingGroup) {
addToGroup(RED.nodes.group(existingGroup), group)
}
try {
addToGroup(group,nodes);
@@ -508,7 +536,7 @@ RED.group = (function() {
if (!z) {
z = n.z;
} else if (z !== n.z) {
throw new Error("Cannot add nooes with different z properties")
throw new Error("Cannot add nodes with different z properties")
}
if (n.g) {
// This is already in a group.
@@ -525,14 +553,10 @@ RED.group = (function() {
throw new Error(RED._("group.errors.cannotCreateDiffGroups"))
}
}
// The nodes are already in a group. The assumption is they should be
// wrapped in the newly provided group, and that group added to in their
// place to the existing containing group.
// The nodes are already in a group - so we need to remove them first
if (g) {
g = RED.nodes.group(g);
g.nodes.push(group);
g.dirty = true;
group.g = g.id;
}
// Second pass - add them to the group
for (i=0;i<nodes.length;i++) {
@@ -566,6 +590,7 @@ RED.group = (function() {
markDirty(group);
}
function removeFromGroup(group, nodes, reparent) {
if (RED.workspaces.isLocked()) { return }
if (!Array.isArray(nodes)) {
nodes = [nodes];
}
@@ -583,7 +608,7 @@ RED.group = (function() {
n.dirty = true;
var index = group.nodes.indexOf(n);
group.nodes.splice(index,1);
if (reparent && group.g) {
if (reparent && parentGroup) {
n.g = group.g
parentGroup.nodes.push(n);
} else {

View File

@@ -249,7 +249,10 @@ RED.keyboard = (function() {
// One exception is shortcuts that include both Cmd and Ctrl. We don't
// support them - but we need to make sure we don't block browser-specific
// shortcuts (such as Cmd-Ctrl-F for fullscreen).
if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) {
if (evt.ctrlKey && evt.metaKey) {
return null; // dont handle both cmd+ctrl - let browser handle this
}
if (evt.ctrlKey || evt.metaKey) {
slot = slot.ctrl;
}
if (slot && evt.shiftKey) {
@@ -491,7 +494,11 @@ RED.keyboard = (function() {
okButton.attr("disabled",!valid);
});
var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="red-ui-workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
var scopeSelect = $('<select>'+
'<option value="*" data-i18n="keyboard.global"></option>'+
'<option value="red-ui-workspace" data-i18n="keyboard.workspace"></option>'+
'<option value="red-ui-editor-stack" data-i18n="keyboard.editor"></option>'+
'</select>').appendTo(scope);
scopeSelect.i18n();
if (object.scope === "workspace") {
object.scope = "red-ui-workspace";

4
packages/node_modules/@node-red/editor-client/src/js/ui/library.js vendored Executable file → Normal file
View File

@@ -839,10 +839,10 @@ RED.library = (function() {
if (file && file.label && !file.children) {
$.get("library/"+file.library+"/"+file.type+"/"+file.path, function(data) {
//TODO: nls + sanitize
var propRow = $('<tr class="red-ui-help-info-row"><td>Type</td><td></td></tr>').appendTo(table);
var propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("library.type")+'</td><td></td></tr>').appendTo(table);
$(propRow.children()[1]).text(activeLibrary.type);
if (file.props.hasOwnProperty('name')) {
propRow = $('<tr class="red-ui-help-info-row"><td>Name</td><td>'+file.props.name+'</td></tr>').appendTo(table);
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("library.name")+'</td><td>'+file.props.name+'</td></tr>').appendTo(table);
$(propRow.children()[1]).text(file.props.name);
}
for (var p in file.props) {

View File

@@ -221,12 +221,12 @@ RED.notifications = (function() {
if (newType) {
n.className = "red-ui-notification red-ui-notification-"+newType;
}
newTimeout = newOptions.hasOwnProperty('timeout')?newOptions.timeout:timeout
if (!fixed || newOptions.fixed === false) {
newTimeout = (newOptions.hasOwnProperty('timeout')?newOptions.timeout:timeout)||5000;
newTimeout = newTimeout || 5000
}
if (newOptions.buttons) {
var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn)
var buttonSet = $('<div class="ui-dialog-buttonset"></div>').appendTo(nn)
newOptions.buttons.forEach(function(buttonDef) {
var b = $('<button>').text(buttonDef.text).on("click", buttonDef.click).appendTo(buttonSet);
if (buttonDef.id) {
@@ -272,6 +272,15 @@ RED.notifications = (function() {
};
})());
n.timeoutid = window.setTimeout(n.close,timeout||5000);
} else if (timeout) {
$(n).on("click.red-ui-notification-close", (function() {
var nn = n;
return function() {
nn.hideNotification();
window.clearTimeout(nn.timeoutid);
};
})());
n.timeoutid = window.setTimeout(n.hideNotification,timeout||5000);
}
currentNotifications.push(n);
if (options.id) {

View File

@@ -16,15 +16,17 @@
RED.palette.editor = (function() {
var disabled = false;
let catalogues = []
const loadedCatalogs = []
var editorTabs;
var filterInput;
var searchInput;
var nodeList;
var packageList;
var loadedList = [];
var filteredList = [];
var loadedIndex = {};
let filterInput;
let searchInput;
let nodeList;
let packageList;
let fullList = []
let loadedList = [];
let filteredList = [];
let loadedIndex = {};
var typesInUse = {};
var nodeEntries = {};
@@ -131,7 +133,7 @@ RED.palette.editor = (function() {
}).done(function(data,textStatus,xhr) {
callback();
}).fail(function(xhr,textStatus,err) {
callback(xhr);
callback(xhr,textStatus,err);
});
}
function removeNodeModule(id,callback) {
@@ -162,7 +164,6 @@ RED.palette.editor = (function() {
}
}
function getContrastingBorder(rgbColor){
var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor);
if (parts) {
@@ -247,86 +248,106 @@ RED.palette.editor = (function() {
var moduleInfo = nodeEntries[module].info;
var nodeEntry = nodeEntries[module].elements;
if (nodeEntry) {
var activeTypeCount = 0;
var typeCount = 0;
var errorCount = 0;
nodeEntry.errorList.empty();
nodeEntries[module].totalUseCount = 0;
nodeEntries[module].setUseCount = {};
if (moduleInfo.plugin) {
nodeEntry.enableButton.hide();
nodeEntry.removeButton.show();
for (var setName in moduleInfo.sets) {
if (moduleInfo.sets.hasOwnProperty(setName)) {
var inUseCount = 0;
var set = moduleInfo.sets[setName];
var setElements = nodeEntry.sets[setName];
if (set.err) {
errorCount++;
var errMessage = set.err;
if (set.err.message) {
errMessage = set.err.message;
} else if (set.err.code) {
errMessage = set.err.code;
let pluginCount = 0;
for (let setName in moduleInfo.sets) {
if (moduleInfo.sets.hasOwnProperty(setName)) {
let set = moduleInfo.sets[setName];
if (set.plugins) {
pluginCount += set.plugins.length;
}
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
}
if (set.enabled) {
activeTypeCount += set.types.length;
}
typeCount += set.types.length;
for (var i=0;i<moduleInfo.sets[setName].types.length;i++) {
var t = moduleInfo.sets[setName].types[i];
inUseCount += (typesInUse[t]||0);
var swatch = setElements.swatches[t];
}
nodeEntry.setCount.text(RED._('palette.editor.pluginCount',{count:pluginCount,label:pluginCount}));
} else {
var activeTypeCount = 0;
var typeCount = 0;
var errorCount = 0;
nodeEntry.errorList.empty();
nodeEntries[module].totalUseCount = 0;
nodeEntries[module].setUseCount = {};
for (var setName in moduleInfo.sets) {
if (moduleInfo.sets.hasOwnProperty(setName)) {
var inUseCount = 0;
const set = moduleInfo.sets[setName];
const setElements = nodeEntry.sets[setName]
if (set.err) {
errorCount++;
var errMessage = set.err;
if (set.err.message) {
errMessage = set.err.message;
} else if (set.err.code) {
errMessage = set.err.code;
}
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
}
if (set.enabled) {
var def = RED.nodes.getType(t);
if (def && def.color) {
swatch.css({background:RED.utils.getNodeColor(t,def)});
swatch.css({border: "1px solid "+getContrastingBorder(swatch.css('backgroundColor'))})
activeTypeCount += set.types.length;
}
typeCount += set.types.length;
for (var i=0;i<moduleInfo.sets[setName].types.length;i++) {
var t = moduleInfo.sets[setName].types[i];
inUseCount += (typesInUse[t]||0);
if (setElements && set.enabled) {
var def = RED.nodes.getType(t);
if (def && def.color) {
setElements.swatches[t].css({background:RED.utils.getNodeColor(t,def)});
setElements.swatches[t].css({border: "1px solid "+getContrastingBorder(setElements.swatches[t].css('backgroundColor'))})
}
}
}
}
nodeEntries[module].setUseCount[setName] = inUseCount;
nodeEntries[module].totalUseCount += inUseCount;
nodeEntries[module].setUseCount[setName] = inUseCount;
nodeEntries[module].totalUseCount += inUseCount;
if (inUseCount > 0) {
setElements.enableButton.text(RED._('palette.editor.inuse'));
setElements.enableButton.addClass('disabled');
} else {
setElements.enableButton.removeClass('disabled');
if (set.enabled) {
setElements.enableButton.text(RED._('palette.editor.disable'));
} else {
setElements.enableButton.text(RED._('palette.editor.enable'));
if (setElements) {
if (inUseCount > 0) {
setElements.enableButton.text(RED._('palette.editor.inuse'));
setElements.enableButton.addClass('disabled');
} else {
setElements.enableButton.removeClass('disabled');
if (set.enabled) {
setElements.enableButton.text(RED._('palette.editor.disable'));
} else {
setElements.enableButton.text(RED._('palette.editor.enable'));
}
}
setElements.setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
}
}
setElements.setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
}
}
if (errorCount === 0) {
nodeEntry.errorRow.hide()
} else {
nodeEntry.errorRow.show();
}
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
nodeEntry.setCount.text(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
if (nodeEntries[module].totalUseCount > 0) {
nodeEntry.enableButton.text(RED._('palette.editor.inuse'));
nodeEntry.enableButton.addClass('disabled');
nodeEntry.removeButton.hide();
} else {
nodeEntry.enableButton.removeClass('disabled');
if (moduleInfo.local) {
nodeEntry.removeButton.css('display', 'inline-block');
}
if (activeTypeCount === 0) {
nodeEntry.enableButton.text(RED._('palette.editor.enableall'));
if (errorCount === 0) {
nodeEntry.errorRow.hide()
} else {
nodeEntry.enableButton.text(RED._('palette.editor.disableall'));
nodeEntry.errorRow.show();
}
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
nodeEntry.setCount.text(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
if (nodeEntries[module].totalUseCount > 0) {
nodeEntry.enableButton.text(RED._('palette.editor.inuse'));
nodeEntry.enableButton.addClass('disabled');
nodeEntry.removeButton.hide();
} else {
nodeEntry.enableButton.removeClass('disabled');
if (moduleInfo.local) {
nodeEntry.removeButton.css('display', 'inline-block');
}
if (activeTypeCount === 0) {
nodeEntry.enableButton.text(RED._('palette.editor.enableall'));
} else {
nodeEntry.enableButton.text(RED._('palette.editor.disableall'));
}
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
}
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
}
}
if (moduleInfo.pending_version) {
@@ -369,10 +390,10 @@ RED.palette.editor = (function() {
var activeSort = sortModulesRelevance;
function handleCatalogResponse(err,catalog,index,v) {
const url = catalog.url
catalogueLoadStatus.push(err||v);
if (!err) {
if (v.modules) {
var a = false;
v.modules = v.modules.filter(function(m) {
if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
loadedIndex[m.id] = m;
@@ -389,13 +410,14 @@ RED.palette.editor = (function() {
m.timestamp = 0;
}
m.index = m.index.join(",").toLowerCase();
m.catalog = catalog;
m.catalogIndex = index;
return true;
}
return false;
})
loadedList = loadedList.concat(v.modules);
}
searchInput.searchBox('count',loadedList.length);
} else {
catalogueLoadErrors = true;
}
@@ -404,7 +426,7 @@ RED.palette.editor = (function() {
}
if (catalogueLoadStatus.length === catalogueCount) {
if (catalogueLoadErrors) {
RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: catalog}),"error",false,8000);
RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000);
}
var delta = 250-(Date.now() - catalogueLoadStart);
setTimeout(function() {
@@ -416,12 +438,13 @@ RED.palette.editor = (function() {
function initInstallTab() {
if (loadedList.length === 0) {
fullList = [];
loadedList = [];
loadedIndex = {};
packageList.editableList('empty');
$(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading'));
var catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json'];
catalogueLoadStatus = [];
catalogueLoadErrors = false;
catalogueCount = catalogues.length;
@@ -431,27 +454,97 @@ RED.palette.editor = (function() {
$("#red-ui-palette-module-install-shade").show();
catalogueLoadStart = Date.now();
var handled = 0;
catalogues.forEach(function(catalog,index) {
$.getJSON(catalog, {_: new Date().getTime()},function(v) {
handleCatalogResponse(null,catalog,index,v);
loadedCatalogs.length = 0; // clear the loadedCatalogs array
for (let index = 0; index < catalogues.length; index++) {
const url = catalogues[index];
$.getJSON(url, {_: new Date().getTime()},function(v) {
loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length })
handleCatalogResponse(null,{ url: url, name: v.name},index,v);
refreshNodeModuleList();
}).fail(function(jqxhr, textStatus, error) {
console.warn("Error loading catalog",catalog,":",error);
handleCatalogResponse(jqxhr,catalog,index);
console.warn("Error loading catalog",url,":",error);
handleCatalogResponse(jqxhr,url,index);
}).always(function() {
handled++;
if (handled === catalogueCount) {
searchInput.searchBox('change');
//sort loadedCatalogs by e.index ascending
loadedCatalogs.sort((a, b) => a.index - b.index)
updateCatalogFilter(loadedCatalogs)
}
})
});
}
}
}
/**
* Refreshes the catalog filter dropdown and updates local variables
* @param {[{url:String, name:String, updated_at:String, modules_count:Number}]} catalogEntries
*/
function updateCatalogFilter(catalogEntries, maxRetry = 3) {
// clean up existing filters
const catalogSelection = $('#red-catalogue-filter-select')
if (catalogSelection.length === 0) {
// sidebar not yet loaded (red-catalogue-filter-select is not in dom)
if (maxRetry > 0) {
// console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms")
// try again in 100ms
setTimeout(() => {
updateCatalogFilter(catalogEntries, maxRetry - 1)
}, 100);
return;
}
return; // give up
}
catalogSelection.off("change") // remove any existing event handlers
catalogSelection.attr('disabled', 'disabled')
catalogSelection.empty()
catalogSelection.append($('<option>', { value: "loading", text: RED._('palette.editor.loading'), disabled: true, selected: true }));
fullList = loadedList.slice()
catalogSelection.empty() // clear the select list
// loop through catalogTypes, and an option entry per catalog
for (let index = 0; index < catalogEntries.length; index++) {
const catalog = catalogEntries[index];
catalogSelection.append(`<option value="${catalog.name}">${catalog.name}</option>`)
}
// select the 1st option in the select list
catalogSelection.val(catalogSelection.find('option:first').val())
// if there is only 1 catalog, hide the select
if (catalogEntries.length > 1) {
catalogSelection.prepend(`<option value="all">${RED._('palette.editor.allCatalogs')}</option>`)
catalogSelection.val('all')
catalogSelection.removeAttr('disabled') // permit the user to select a catalog
}
// refresh the searchInput counter and trigger a change
filterByCatalog(catalogSelection.val())
searchInput.searchBox('change');
// hook up the change event handler
catalogSelection.on("change", function() {
const selectedCatalog = $(this).val();
filterByCatalog(selectedCatalog);
searchInput.searchBox('change');
})
}
function filterByCatalog(selectedCatalog) {
if (loadedCatalogs.length <= 1 || selectedCatalog === "all") {
loadedList = fullList.slice();
} else {
loadedList = fullList.filter(function(m) {
return (m.catalog.name === selectedCatalog);
})
}
refreshFilteredItems();
searchInput.searchBox('count',filteredList.length+" / "+loadedList.length);
}
function refreshFilteredItems() {
packageList.editableList('empty');
var currentFilter = searchInput.searchBox('value').trim();
if (currentFilter === ""){
if (currentFilter === "" && loadedList.length > 20){
packageList.editableList('addItem',{count:loadedList.length})
return;
}
@@ -462,7 +555,6 @@ RED.palette.editor = (function() {
if (filteredList.length === 0) {
packageList.editableList('addItem',{});
}
if (filteredList.length > 10) {
packageList.editableList('addItem',{start:10,more:filteredList.length-10})
}
@@ -492,6 +584,7 @@ RED.palette.editor = (function() {
var updateDenyList = [];
function init() {
catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json']
if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
return;
}
@@ -605,6 +698,33 @@ RED.palette.editor = (function() {
}
}
})
RED.events.on("registry:plugin-module-added", function(module) {
if (!nodeEntries.hasOwnProperty(module)) {
nodeEntries[module] = {info:RED.plugins.getModule(module)};
var index = [module];
for (var s in nodeEntries[module].info.sets) {
if (nodeEntries[module].info.sets.hasOwnProperty(s)) {
index.push(s);
index = index.concat(nodeEntries[module].info.sets[s].types)
}
}
nodeEntries[module].index = index.join(",").toLowerCase();
nodeList.editableList('addItem', nodeEntries[module]);
} else {
_refreshNodeModule(module);
}
for (var i=0;i<filteredList.length;i++) {
if (filteredList[i].info.id === module) {
var installButton = filteredList[i].elements.installButton;
installButton.addClass('disabled');
installButton.text(RED._('palette.editor.installed'));
break;
}
}
});
}
var settingsPane;
@@ -669,7 +789,8 @@ RED.palette.editor = (function() {
});
nodeList = $('<ol>',{id:"red-ui-palette-module-list", style:"position: absolute;top: 35px;bottom: 0;left: 0;right: 0px;"}).appendTo(modulesTab).editableList({
nodeList = $('<ol>',{id:"red-ui-palette-module-list"}).appendTo(modulesTab).editableList({
class: "scrollable",
addButton: false,
scrollOnAdd: false,
sort: function(A,B) {
@@ -730,6 +851,7 @@ RED.palette.editor = (function() {
errorRow: errorRow,
errorList: errorList,
setCount: setCount,
setButton: setButton,
container: container,
shade: shade,
versionSpan: versionSpan,
@@ -740,49 +862,88 @@ RED.palette.editor = (function() {
if (container.hasClass('expanded')) {
container.removeClass('expanded');
contentRow.slideUp();
setTimeout(() => {
contentRow.empty()
}, 200)
object.elements.sets = {}
} else {
container.addClass('expanded');
populateSetList()
contentRow.slideDown();
}
})
var setList = Object.keys(entry.sets)
setList.sort(function(A,B) {
return A.toLowerCase().localeCompare(B.toLowerCase());
});
setList.forEach(function(setName) {
var set = entry.sets[setName];
var setRow = $('<div>',{class:"red-ui-palette-module-set"}).appendTo(contentRow);
var buttonGroup = $('<div>',{class:"red-ui-palette-module-set-button-group"}).appendTo(setRow);
var typeSwatches = {};
set.types.forEach(function(t) {
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
typeSwatches[t] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
$('<span>',{class:"red-ui-palette-module-type-node"}).text(t).appendTo(typeDiv);
})
var enableButton = $('<a href="#" class="red-ui-button red-ui-button-small"></a>').appendTo(buttonGroup);
enableButton.on("click", function(evt) {
evt.preventDefault();
if (object.setUseCount[setName] === 0) {
var currentSet = RED.nodes.registry.getNodeSet(set.id);
shade.show();
var newState = !currentSet.enabled
changeNodeState(set.id,newState,shade,function(xhr){
if (xhr) {
if (xhr.responseJSON) {
RED.notify(RED._('palette.editor.errors.'+(newState?'enable':'disable')+'Failed',{module: id,message:xhr.responseJSON.message}));
const populateSetList = function () {
var setList = Object.keys(entry.sets)
setList.sort(function(A,B) {
return A.toLowerCase().localeCompare(B.toLowerCase());
});
setList.forEach(function(setName) {
var set = entry.sets[setName];
var setRow = $('<div>',{class:"red-ui-palette-module-set"}).appendTo(contentRow);
var buttonGroup = $('<div>',{class:"red-ui-palette-module-set-button-group"}).appendTo(setRow);
var typeSwatches = {};
let enableButton;
if (set.types) {
set.types.forEach(function(t) {
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
typeSwatches[t] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
if (set.enabled) {
var def = RED.nodes.getType(t);
if (def && def.color) {
typeSwatches[t].css({background:RED.utils.getNodeColor(t,def)});
typeSwatches[t].css({border: "1px solid "+getContrastingBorder(typeSwatches[t].css('backgroundColor'))})
}
}
});
}
})
$('<span>',{class:"red-ui-palette-module-type-node"}).text(t).appendTo(typeDiv);
})
enableButton = $('<a href="#" class="red-ui-button red-ui-button-small"></a>').appendTo(buttonGroup);
enableButton.on("click", function(evt) {
evt.preventDefault();
if (object.setUseCount[setName] === 0) {
var currentSet = RED.nodes.registry.getNodeSet(set.id);
shade.show();
var newState = !currentSet.enabled
changeNodeState(set.id,newState,shade,function(xhr){
if (xhr) {
if (xhr.responseJSON) {
RED.notify(RED._('palette.editor.errors.'+(newState?'enable':'disable')+'Failed',{module: id,message:xhr.responseJSON.message}));
}
}
});
}
})
object.elements.sets[set.name] = {
setRow: setRow,
enableButton: enableButton,
swatches: typeSwatches
};
});
if (object.setUseCount[setName] > 0) {
enableButton.text(RED._('palette.editor.inuse'));
enableButton.addClass('disabled');
} else {
enableButton.removeClass('disabled');
if (set.enabled) {
enableButton.text(RED._('palette.editor.disable'));
} else {
enableButton.text(RED._('palette.editor.enable'));
}
}
setRow.toggleClass("red-ui-palette-module-set-disabled",!set.enabled);
}
if (set.plugins) {
set.plugins.forEach(function(p) {
var typeDiv = $('<div>',{class:"red-ui-palette-module-type"}).appendTo(setRow);
// typeSwatches[p.id] = $('<span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
$('<span><i class="fa fa-puzzle-piece" aria-hidden="true"></i> </span>',{class:"red-ui-palette-module-type-swatch"}).appendTo(typeDiv);
$('<span>',{class:"red-ui-palette-module-type-node"}).text(p.id).appendTo(typeDiv);
})
}
object.elements.sets[set.name] = {
setRow: setRow,
enableButton: enableButton,
swatches: typeSwatches
};
});
}
enableButton.on("click", function(evt) {
evt.preventDefault();
if (object.totalUseCount === 0) {
@@ -800,28 +961,27 @@ RED.palette.editor = (function() {
$('<div>',{class:"red-ui-search-empty"}).text(RED._('search.empty')).appendTo(container);
}
}
});
})
}
function createInstallTab(content) {
var installTab = $('<div>',{class:"red-ui-palette-editor-tab hide"}).appendTo(content);
const installTab = $('<div>',{class:"red-ui-palette-editor-tab", style: "display: none;"}).appendTo(content);
editorTabs.addTab({
id: 'install',
label: RED._('palette.editor.tab-install'),
content: installTab
})
var toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
var searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
const toolBar = $('<div>',{class:"red-ui-palette-editor-toolbar"}).appendTo(installTab);
const searchDiv = $('<div>',{class:"red-ui-palette-search"}).appendTo(installTab);
searchInput = $('<input type="text" data-i18n="[placeholder]palette.search"></input>')
.appendTo(searchDiv)
.searchBox({
delay: 300,
change: function() {
var searchTerm = $(this).val().trim().toLowerCase();
if (searchTerm.length > 0) {
if (searchTerm.length > 0 || loadedList.length < 20) {
filteredList = loadedList.filter(function(m) {
return (m.index.indexOf(searchTerm) > -1);
}).map(function(f) { return {info:f}});
@@ -831,19 +991,26 @@ RED.palette.editor = (function() {
searchInput.searchBox('count',loadedList.length);
packageList.editableList('empty');
packageList.editableList('addItem',{count:loadedList.length});
}
}
});
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
var sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
var sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
var sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
const catalogSelection = $('<select id="red-catalogue-filter-select">').appendTo(toolBar);
catalogSelection.addClass('red-ui-palette-editor-catalogue-filter');
const toolBarActions = $('<div>',{class:"red-ui-palette-editor-toolbar-actions"}).appendTo(toolBar);
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBarActions);
const sortGroup = $('<span class="button-group"></span>').appendTo(toolBarActions);
const sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
const sortAZ = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-sort-alpha-asc"></i></a>').appendTo(sortGroup);
const sortRecent = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle"><i class="fa fa-calendar"></i></a>').appendTo(sortGroup);
RED.popover.tooltip(sortRelevance,RED._("palette.editor.sortRelevance"));
RED.popover.tooltip(sortAZ,RED._("palette.editor.sortAZ"));
RED.popover.tooltip(sortRecent,RED._("palette.editor.sortRecent"));
var sortOpts = [
const sortOpts = [
{button: sortRelevance, func: sortModulesRelevance},
{button: sortAZ, func: sortModulesAZ},
{button: sortRecent, func: sortModulesRecent}
@@ -861,7 +1028,7 @@ RED.palette.editor = (function() {
});
});
var refreshSpan = $('<span>').appendTo(toolBar);
var refreshSpan = $('<span>').appendTo(toolBarActions);
var refreshButton = $('<a href="#" class="red-ui-sidebar-header-button"><i class="fa fa-refresh"></i></a>').appendTo(refreshSpan);
refreshButton.on("click", function(e) {
e.preventDefault();
@@ -871,7 +1038,8 @@ RED.palette.editor = (function() {
})
RED.popover.tooltip(refreshButton,RED._("palette.editor.refresh"));
packageList = $('<ol>',{style:"position: absolute;top: 79px;bottom: 0;left: 0;right: 0px;"}).appendTo(installTab).editableList({
packageList = $('<ol>').appendTo(installTab).editableList({
class: "scrollable",
addButton: false,
scrollOnAdd: false,
addItem: function(container,i,object) {
@@ -906,6 +1074,9 @@ RED.palette.editor = (function() {
var metaRow = $('<div class="red-ui-palette-module-meta"></div>').appendTo(headerRow);
$('<span class="red-ui-palette-module-version"><i class="fa fa-tag"></i> '+entry.version+'</span>').appendTo(metaRow);
$('<span class="red-ui-palette-module-updated"><i class="fa fa-calendar"></i> '+formatUpdatedAt(entry.updated_at)+'</span>').appendTo(metaRow);
if (loadedCatalogs.length > 1) {
$('<span class="red-ui-palette-module-updated"><i class="fa fa-cubes"></i>' + (entry.catalog.name || entry.catalog.url) + '</span>').appendTo(metaRow);
}
var duplicateType = false;
if (entry.types && entry.types.length > 0) {
@@ -952,9 +1123,10 @@ RED.palette.editor = (function() {
}
}
});
if (RED.settings.get('externalModules.palette.allowUpload', true) !== false) {
var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
var uploadSpan = $('<span class="button-group">').prependTo(toolBarActions);
var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
var uploadInput = uploadButton.find('input[type="file"]');
@@ -1141,7 +1313,55 @@ RED.palette.editor = (function() {
}
}
]
}); }
});
}
} else {
// dedicated list management for plugins
if (entry.plugin) {
let e = nodeEntries[entry.name];
if (e) {
nodeList.editableList('removeItem', e);
delete nodeEntries[entry.name];
}
// We assume that a plugin that implements onremove
// cleans the editor accordingly of its left-overs.
let found_onremove = true;
let keys = Object.keys(entry.sets);
keys.forEach((key) => {
let set = entry.sets[key];
for (let i=0; i<set.plugins?.length; i++) {
let plgn = RED.plugins.getPlugin(set.plugins[i].id);
if (plgn && plgn.onremove && typeof plgn.onremove === 'function') {
plgn.onremove();
} else {
if (plgn && plgn.onadd && typeof plgn.onadd === 'function') {
// if there's no 'onadd', there shouldn't be any left-overs
found_onremove = false;
}
}
}
});
if (!found_onremove) {
let removeNotify = RED.notify(RED._("palette.editor.confirm.removePlugin.body",{module:entry.name}),{
modal: true,
fixed: true,
type: 'warning',
buttons: [
{
text: RED._("palette.editor.confirm.button.understood"),
class:"primary",
click: function(e) {
removeNotify.close();
}
}
]
});
}
}
}
})
notification.close();
@@ -1185,9 +1405,28 @@ RED.palette.editor = (function() {
RED.actions.invoke("core:show-event-log");
});
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.id+" "+entry.version);
installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr) {
installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr, textStatus,err) {
spinner.remove();
if (xhr) {
if (err && xhr.status === 504) {
var notification = RED.notify(RED._("palette.editor.errors.installTimeout"), {
modal: true,
fixed: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
},{
text: RED._("eventLog.view"),
click: function() {
notification.close();
RED.actions.invoke("core:show-event-log");
}
}
]
})
} else if (xhr) {
if (xhr.responseJSON) {
var notification = RED.notify(RED._('palette.editor.errors.installFailed',{module: entry.id,message:xhr.responseJSON.message}),{
type: 'error',

308
packages/node_modules/@node-red/editor-client/src/js/ui/palette.js vendored Executable file → Normal file
View File

@@ -35,6 +35,10 @@ RED.palette = (function() {
var categoryContainers = {};
var sidebarControls;
let paletteState = { filter: "", collapsed: [] };
let filterRefreshTimeout
function createCategory(originalCategory,rootCategory,category,ns) {
if ($("#red-ui-palette-base-category-"+rootCategory).length === 0) {
createCategoryContainer(originalCategory,rootCategory, ns+":palette.label."+rootCategory);
@@ -60,20 +64,57 @@ RED.palette = (function() {
catDiv.data('label',label);
categoryContainers[category] = {
container: catDiv,
close: function() {
hide: function (instant) {
if (instant) {
catDiv.hide()
} else {
catDiv.slideUp()
}
},
show: function () {
catDiv.show()
},
isOpen: function () {
return !!catDiv.hasClass("red-ui-palette-open")
},
getNodeCount: function (visibleOnly) {
const nodes = catDiv.find(".red-ui-palette-node")
if (visibleOnly) {
return nodes.filter(function() { return $(this).css('display') !== 'none'}).length
} else {
return nodes.length
}
},
close: function(instant, skipSaveState) {
catDiv.removeClass("red-ui-palette-open");
catDiv.addClass("red-ui-palette-closed");
$("#red-ui-palette-base-category-"+category).slideUp();
if (instant) {
$("#red-ui-palette-base-category-"+category).hide();
} else {
$("#red-ui-palette-base-category-"+category).slideUp();
}
$("#red-ui-palette-header-"+category+" i").removeClass("expanded");
if (!skipSaveState) {
if (!paletteState.collapsed.includes(category)) {
paletteState.collapsed.push(category);
savePaletteState();
}
}
},
open: function() {
open: function(skipSaveState) {
catDiv.addClass("red-ui-palette-open");
catDiv.removeClass("red-ui-palette-closed");
$("#red-ui-palette-base-category-"+category).slideDown();
$("#red-ui-palette-header-"+category+" i").addClass("expanded");
if (!skipSaveState) {
if (paletteState.collapsed.includes(category)) {
paletteState.collapsed.splice(paletteState.collapsed.indexOf(category), 1);
savePaletteState();
}
}
},
toggle: function() {
if (catDiv.hasClass("red-ui-palette-open")) {
if (categoryContainers[category].isOpen()) {
categoryContainers[category].close();
} else {
categoryContainers[category].open();
@@ -171,23 +212,15 @@ RED.palette = (function() {
}
metaData += type;
const safeType = type.replace(/'/g,"\\'");
const searchType = type.indexOf(' ') > -1 ? '&quot;' + type + '&quot;' : type
if (/^subflow:/.test(type)) {
$('<button type="button" onclick="RED.workspaces.show(\''+type.substring(8).replace(/'/g,"\\'")+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-pencil"></i></button>').appendTo(popOverContent)
}
const safeType = type.replace(/'/g,"\\'");
const wrapStr = function (str) {
if(str.indexOf(' ') >= 0) {
return '"' + str + '"'
}
return str
}
$('<button type="button" onclick="RED.search.show(\'type:'+searchType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>').appendTo(popOverContent)
$('<button type="button"; return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>')
.appendTo(popOverContent)
.on('click', function() {
RED.search.show('type:' + wrapStr(safeType))
})
$('<button type="button" onclick="RED.sidebar.help.show(\''+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
@@ -292,6 +325,7 @@ RED.palette = (function() {
var hoverGroup;
var paletteWidth;
var paletteTop;
var dropEnabled;
$(d).draggable({
helper: 'clone',
appendTo: '#red-ui-editor',
@@ -299,6 +333,7 @@ RED.palette = (function() {
revertDuration: 200,
containment:'#red-ui-main-container',
start: function() {
dropEnabled = !(RED.nodes.workspace(RED.workspaces.active())?.locked);
paletteWidth = $("#red-ui-palette").width();
paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top;
hoverGroup = null;
@@ -309,96 +344,100 @@ RED.palette = (function() {
RED.view.focus();
},
stop: function() {
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
if (dropEnabled) {
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
}
if (activeGroup) {
document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
}
if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
}
if (activeGroup) {
document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
}
if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
},
drag: function(e,ui) {
var paletteNode = getPaletteNode(nt);
ui.originalPosition.left = paletteNode.offset().left;
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
if (!groupTimer) {
groupTimer = setTimeout(function() {
var mx = mouseX / RED.view.scale();
var my = mouseY / RED.view.scale();
var group = RED.view.getGroupAtPoint(mx,my);
if (group !== hoverGroup) {
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
}
if (group) {
document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
}
hoverGroup = group;
if (hoverGroup) {
$(ui.helper).data('group',hoverGroup);
} else {
$(ui.helper).removeData('group');
}
}
groupTimer = null;
},200)
}
if (def.inputs > 0 && def.outputs > 0) {
if (!spliceTimer) {
spliceTimer = setTimeout(function() {
var nodes = [];
var bestDistance = Infinity;
var bestLink = null;
if (chartSVG.getIntersectionList) {
var svgRect = chartSVG.createSVGRect();
svgRect.x = mouseX;
svgRect.y = mouseY;
svgRect.width = 1;
svgRect.height = 1;
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
} else {
// Firefox doesn't do getIntersectionList and that
// makes us sad
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
}
if (dropEnabled) {
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
if (!groupTimer) {
groupTimer = setTimeout(function() {
var mx = mouseX / RED.view.scale();
var my = mouseY / RED.view.scale();
for (var i=0;i<nodes.length;i++) {
var node = d3.select(nodes[i]);
if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
var length = nodes[i].getTotalLength();
for (var j=0;j<length;j+=10) {
var p = nodes[i].getPointAtLength(j);
var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
if (d2 < 200 && d2 < bestDistance) {
bestDistance = d2;
bestLink = nodes[i];
var group = RED.view.getGroupAtPoint(mx,my);
if (group !== hoverGroup) {
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
}
if (group) {
document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
}
hoverGroup = group;
if (hoverGroup) {
$(ui.helper).data('group',hoverGroup);
} else {
$(ui.helper).removeData('group');
}
}
groupTimer = null;
},200)
}
if (def.inputs > 0 && def.outputs > 0) {
if (!spliceTimer) {
spliceTimer = setTimeout(function() {
var nodes = [];
var bestDistance = Infinity;
var bestLink = null;
if (chartSVG.getIntersectionList) {
var svgRect = chartSVG.createSVGRect();
svgRect.x = mouseX;
svgRect.y = mouseY;
svgRect.width = 1;
svgRect.height = 1;
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
} else {
// Firefox doesn't do getIntersectionList and that
// makes us sad
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
}
var mx = mouseX / RED.view.scale();
var my = mouseY / RED.view.scale();
for (var i=0;i<nodes.length;i++) {
var node = d3.select(nodes[i]);
if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
var length = nodes[i].getTotalLength();
for (var j=0;j<length;j+=10) {
var p = nodes[i].getPointAtLength(j);
var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
if (d2 < 200 && d2 < bestDistance) {
bestDistance = d2;
bestLink = nodes[i];
}
}
}
}
}
if (activeSpliceLink && activeSpliceLink !== bestLink) {
d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
}
if (bestLink) {
d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
} else {
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
}
if (activeSpliceLink !== bestLink) {
if (bestLink) {
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
} else {
$(ui.helper).removeData('splice');
if (activeSpliceLink && activeSpliceLink !== bestLink) {
d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
}
}
activeSpliceLink = bestLink;
spliceTimer = null;
},200);
if (bestLink) {
d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
} else {
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
}
if (activeSpliceLink !== bestLink) {
if (bestLink) {
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
} else {
$(ui.helper).removeData('splice');
}
}
activeSpliceLink = bestLink;
spliceTimer = null;
},200);
}
}
}
}
@@ -417,8 +456,16 @@ RED.palette = (function() {
var categoryNode = $("#red-ui-palette-container-"+rootCategory);
if (categoryNode.find(".red-ui-palette-node").length === 1) {
categoryContainers[rootCategory].open();
if (!paletteState?.collapsed?.includes(rootCategory)) {
categoryContainers[rootCategory].open();
} else {
categoryContainers[rootCategory].close(true);
}
}
clearTimeout(filterRefreshTimeout)
filterRefreshTimeout = setTimeout(() => {
refreshFilter()
}, 200)
}
}
@@ -432,6 +479,7 @@ RED.palette = (function() {
categoryNode.find(".red-ui-palette-content").slideToggle();
categoryNode.find("i").toggleClass("expanded");
}
categoryNode.hide();
}
}
@@ -485,7 +533,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||""));
}
@@ -510,13 +558,15 @@ RED.palette = (function() {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
currentCategoryNode.hide();
}
}
paletteNode.css("backgroundColor", sf.color);
}
function filterChange(val) {
function refreshFilter() {
const val = $("#red-ui-palette-search input").val()
var re = new RegExp(val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),'i');
$("#red-ui-palette-container .red-ui-palette-node").each(function(i,el) {
var currentLabel = $(el).attr("data-palette-label");
@@ -528,16 +578,26 @@ RED.palette = (function() {
}
});
for (var category in categoryContainers) {
for (let category in categoryContainers) {
if (categoryContainers.hasOwnProperty(category)) {
if (categoryContainers[category].container
.find(".red-ui-palette-node")
.filter(function() { return $(this).css('display') !== 'none'}).length === 0) {
categoryContainers[category].close();
categoryContainers[category].container.slideUp();
const categorySection = categoryContainers[category]
if (categorySection.getNodeCount(true) === 0) {
categorySection.hide()
} else {
categoryContainers[category].open();
categoryContainers[category].container.show();
categorySection.show()
if (val) {
// There is a filter being applied and it has matched
// something in this category - show the contents
categorySection.open(true)
} else {
// No filter. Only show the category if it isn't in lastState
if (!paletteState.collapsed.includes(category)) {
categorySection.open(true)
} else if (categorySection.isOpen()) {
// This section should be collapsed but isn't - so make it so
categorySection.close(true, true)
}
}
}
}
}
@@ -553,6 +613,9 @@ RED.palette = (function() {
$("#red-ui-palette > .red-ui-palette-spinner").show();
RED.events.on('logout', function () {
RED.settings.removeLocal('palette-state')
})
RED.events.on('registry:node-type-added', function(nodeType) {
var def = RED.nodes.getType(nodeType);
@@ -596,14 +659,14 @@ RED.palette = (function() {
RED.events.on("subflows:change",refreshSubflow);
$("#red-ui-palette-search input").searchBox({
delay: 100,
change: function() {
filterChange($(this).val());
refreshFilter();
paletteState.filter = $(this).val();
savePaletteState();
}
})
});
sidebarControls = $('<div class="red-ui-sidebar-control-left"><i class="fa fa-chevron-left"></i></div>').appendTo($("#red-ui-palette"));
RED.popover.tooltip(sidebarControls,RED._("keyboard.togglePalette"),"core:toggle-palette");
@@ -669,7 +732,23 @@ RED.palette = (function() {
togglePalette(state);
}
});
try {
paletteState = JSON.parse(RED.settings.getLocal("palette-state") || '{"filter":"", "collapsed": []}');
if (paletteState.filter) {
// Apply the category filter
$("#red-ui-palette-search input").searchBox("value", paletteState.filter);
}
} catch (error) {
console.error("Unexpected error loading palette state from localStorage: ", error);
}
setTimeout(() => {
// Lazily tidy up any categories that haven't been reloaded
paletteState.collapsed = paletteState.collapsed.filter(category => !!categoryContainers[category])
savePaletteState()
}, 10000)
}
function togglePalette(state) {
if (!state) {
$("#red-ui-main-container").addClass("red-ui-palette-closed");
@@ -689,6 +768,15 @@ RED.palette = (function() {
})
return categories;
}
function savePaletteState() {
try {
RED.settings.setLocal("palette-state", JSON.stringify(paletteState));
} catch (error) {
console.error("Unexpected error saving palette state to localStorage: ", error);
}
}
return {
init: init,
add:addNodeType,

View File

@@ -165,6 +165,9 @@ 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 () {
RED.editor.mermaid.render()
}, 200);
}
function editSummary(activeProject, summary, container, version, versionContainer) {
@@ -284,7 +287,7 @@ RED.projects.settings = (function() {
var notInstalledCount = 0;
for (var m in modulesInUse) {
if (modulesInUse.hasOwnProperty(m)) {
if (modulesInUse.hasOwnProperty(m) && !activeProject.dependencies.hasOwnProperty(m)) {
depsList.editableList('addItem',{
id: modulesInUse[m].module,
version: modulesInUse[m].version,
@@ -304,8 +307,8 @@ RED.projects.settings = (function() {
if (activeProject.dependencies) {
for (var m in activeProject.dependencies) {
if (activeProject.dependencies.hasOwnProperty(m) && !modulesInUse.hasOwnProperty(m)) {
var installed = !!RED.nodes.registry.getModule(m);
if (activeProject.dependencies.hasOwnProperty(m)) {
var installed = !!RED.nodes.registry.getModule(m) && activeProject.dependencies[m] === modulesInUse[m]?.version;
depsList.editableList('addItem',{
id: m,
version: activeProject.dependencies[m], //RED.nodes.registry.getModule(module).version,
@@ -1253,7 +1256,7 @@ RED.projects.settings = (function() {
notification.close();
}
},{
text: 'Delete branch',
text: RED._("sidebar.project.projectSettings.deleteBranch"),
click: function() {
notification.close();
var url = "projects/"+activeProject.name+"/branches/"+entry.name;

View File

@@ -747,14 +747,14 @@ RED.projects = (function() {
var row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.default-files.flow-file")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow)||"flow.json";
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow) || "flows.json";
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val(defaultFlowFile)
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
$('<label class="red-ui-projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials)||"flow_cred.json";
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials) || "flows_cred.json";
row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-credfile">'+RED._("projects.default-files.credentials-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
@@ -1257,7 +1257,7 @@ RED.projects = (function() {
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.create.flow-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flow.json")
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flows.json")
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);

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

@@ -46,7 +46,9 @@ RED.subflow = (function() {
'</script>';
function findAvailableSubflowIOPosition(subflow,isInput) {
var pos = {x:50,y:30};
const scrollPos = RED.view.scroll()
const scaleFactor = RED.view.scale()
var pos = { x: (scrollPos[0]/scaleFactor)+50, y: (scrollPos[1]/scaleFactor)+30 };
if (!isInput) {
pos.x += 110;
}
@@ -273,6 +275,11 @@ RED.subflow = (function() {
var subflowInstances = [];
if (activeSubflow) {
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
const parentFlow = RED.nodes.workspace(n.z)
const wasLocked = parentFlow && parentFlow.locked
if (wasLocked) {
parentFlow.locked = false
}
subflowInstances.push({
id: n.id,
changed: n.changed
@@ -285,6 +292,9 @@ RED.subflow = (function() {
n.resize = true;
n.dirty = true;
RED.editor.updateNodeProperties(n);
if (wasLocked) {
parentFlow.locked = true
}
});
RED.editor.validateNode(activeSubflow);
return {
@@ -431,44 +441,7 @@ RED.subflow = (function() {
$("#red-ui-subflow-delete").on("click", function(event) {
event.preventDefault();
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (subflow.instances.length > 0) {
var msg = $('<div>')
$('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
$('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
var confirmDeleteNotification = RED.notify(msg, {
modal: true,
fixed: true,
buttons: [
{
text: RED._('common.label.cancel'),
click: function() {
confirmDeleteNotification.close();
}
},
{
text: RED._('workspace.confirmDelete'),
class: "primary",
click: function() {
confirmDeleteNotification.close();
completeDelete();
}
}
]
});
return;
} else {
completeDelete();
}
function completeDelete() {
var startDirty = RED.nodes.dirty();
var historyEvent = removeSubflow(RED.workspaces.active());
historyEvent.t = 'delete';
historyEvent.dirty = startDirty;
RED.history.push(historyEvent);
}
RED.subflow.delete(RED.workspaces.active())
});
refreshToolbar(activeSubflow);
@@ -481,7 +454,51 @@ RED.subflow = (function() {
$("#red-ui-workspace-toolbar").hide().empty();
$("#red-ui-workspace-chart").css({"margin-top": "0"});
}
function deleteSubflow(id) {
const subflow = RED.nodes.subflow(id || RED.workspaces.active());
if (!subflow) {
return
}
if (subflow.instances.length > 0) {
if (subflow.instances.some(sf => { const ws = RED.nodes.workspace(sf.z); return ws?ws.locked:false })) {
return
}
const msg = $('<div>')
$('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
$('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
const confirmDeleteNotification = RED.notify(msg, {
modal: true,
fixed: true,
buttons: [
{
text: RED._('common.label.cancel'),
click: function() {
confirmDeleteNotification.close();
}
},
{
text: RED._('workspace.confirmDelete'),
class: "primary",
click: function() {
confirmDeleteNotification.close();
completeDelete();
}
}
]
});
return;
} else {
completeDelete();
}
function completeDelete() {
const startDirty = RED.nodes.dirty();
const historyEvent = removeSubflow(subflow.id);
historyEvent.t = 'delete';
historyEvent.dirty = startDirty;
RED.history.push(historyEvent);
}
}
function removeSubflow(id, keepInstanceNodes) {
// TODO: A lot of this logic is common with RED.nodes.removeWorkspace
var removedNodes = [];
@@ -558,7 +575,7 @@ RED.subflow = (function() {
}
});
RED.events.on("view:selection-changed",function(selection) {
if (!selection.nodes) {
if (!selection.nodes || RED.workspaces.isLocked()) {
RED.menu.setDisabled("menu-item-subflow-convert",true);
} else {
RED.menu.setDisabled("menu-item-subflow-convert",false);
@@ -621,6 +638,9 @@ RED.subflow = (function() {
}
function convertToSubflow() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (!selection.nodes) {
RED.notify(RED._("subflow.errors.noNodesSelected"),"error");
@@ -647,7 +667,7 @@ RED.subflow = (function() {
for (i=0; i<nodeList.length;i++) {
if (nodeList[i].g && !includedGroups.has(nodeList[i].g)) {
if (containingGroup !== nodeList[i].g) {
RED.notify("Cannot create subflow across multiple groups","error");
RED.notify(RED._("subflow.errors.acrossMultipleGroups"), "error");
return;
}
}
@@ -663,24 +683,23 @@ RED.subflow = (function() {
var candidateOutputs = [];
var candidateInputNodes = {};
var boundingBox = [nodeList[0].x,
nodeList[0].y,
nodeList[0].x,
nodeList[0].y];
var boundingBox = [nodeList[0].x-(nodeList[0].w/2),
nodeList[0].y-(nodeList[0].h/2),
nodeList[0].x+(nodeList[0].w/2),
nodeList[0].y+(nodeList[0].h/2)];
for (i=0;i<nodeList.length;i++) {
n = nodeList[i];
nodes[n.id] = {n:n,outputs:{}};
boundingBox = [
Math.min(boundingBox[0],n.x),
Math.min(boundingBox[1],n.y),
Math.max(boundingBox[2],n.x),
Math.max(boundingBox[3],n.y)
Math.min(boundingBox[0],n.x-(n.w/2)),
Math.min(boundingBox[1],n.y-(n.h/2)),
Math.max(boundingBox[2],n.x+(n.w/2)),
Math.max(boundingBox[3],n.y+(n.h/2))
]
}
var offsetX = snapToGrid(boundingBox[0] - 200);
var offsetY = snapToGrid(boundingBox[1] - 80);
var offsetX = snapToGrid(boundingBox[0] - 140);
var offsetY = snapToGrid(boundingBox[1] - 60);
var center = [
snapToGrid((boundingBox[2]+boundingBox[0]) / 2),
@@ -776,7 +795,7 @@ RED.subflow = (function() {
}
subflowInstance._def = RED.nodes.getType(subflowInstance.type);
RED.editor.validateNode(subflowInstance);
RED.nodes.add(subflowInstance);
subflowInstance = RED.nodes.add(subflowInstance);
if (containingGroup) {
RED.group.addToGroup(containingGroup, subflowInstance);
@@ -890,17 +909,19 @@ RED.subflow = (function() {
/**
* Create interface for controlling env var UI definition
* Build the edit dialog for a subflow template (creating/modifying a subflow template)
* @param {Object} uiContainer - the jQuery container for the environment variable list
* @param {Object} node - the subflow template node
*/
function buildEnvControl(envList,node) {
function buildEnvControl(uiContainer,node) {
var tabs = RED.tabs.create({
id: "subflow-env-tabs",
onchange: function(tab) {
if (tab.id === "subflow-env-tab-preview") {
var inputContainer = $("#subflow-input-ui");
var list = envList.editableList("items");
var list = uiContainer.editableList("items");
var exportedEnv = exportEnvList(list, true);
buildEnvUI(inputContainer, exportedEnv,node);
buildEnvUI(inputContainer, exportedEnv, node);
}
$("#subflow-env-tabs-content").children().hide();
$("#" + tab.id).show();
@@ -938,12 +959,33 @@ RED.subflow = (function() {
RED.editor.envVarList.setLocale(locale);
}
function buildEnvUIRow(row, tenv, ui, node) {
/**
* Build a UI row for a subflow instance environment variable
* Also used to build the UI row for subflow template preview
* @param {JQuery} row - A form row element
* @param {Object} tenv - A template environment variable
* @param {String} tenv.name - The name of the environment variable
* @param {String} tenv.type - The type of the environment variable
* @param {String} tenv.value - The value set for this environment variable
* @param {Object} tenv.parent - The parent environment variable
* @param {String} tenv.parent.value - The value set for the parent environment variable
* @param {String} tenv.parent.type - The type of the parent environment variable
* @param {Object} tenv.ui - The UI configuration for the environment variable
* @param {String} tenv.ui.icon - The icon for the environment variable
* @param {Object} tenv.ui.label - The label for the environment variable
* @param {String} tenv.ui.type - The type of the UI control for the environment variable
* @param {Object} node - The subflow instance node
*/
function buildEnvUIRow(row, tenv, node) {
if(RED.subflow.debug) { console.log("buildEnvUIRow", tenv) }
const ui = tenv.ui || {}
ui.label = ui.label||{};
if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
ui.type = "cred";
ui.opts = {};
} else if (tenv.type === "conf-types") {
ui.type = "conf-types"
ui.opts = { types: ['conf-types'] }
} else if (!ui.type) {
ui.type = "input";
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
@@ -987,9 +1029,10 @@ RED.subflow = (function() {
if (tenv.hasOwnProperty('type')) {
val.type = tenv.type;
}
const elId = getSubflowEnvPropertyName(tenv.name)
switch(ui.type) {
case "input":
input = $('<input type="text">').css('width','70%').appendTo(row);
input = $('<input type="text">').css('width','70%').attr('id', elId).appendTo(row);
if (ui.opts.types && ui.opts.types.length > 0) {
var inputType = val.type;
if (ui.opts.types.indexOf(inputType) === -1) {
@@ -1016,7 +1059,7 @@ RED.subflow = (function() {
}
break;
case "select":
input = $('<select>').css('width','70%').appendTo(row);
input = $('<select>').css('width','70%').attr('id', elId).appendTo(row);
if (ui.opts.opts) {
ui.opts.opts.forEach(function(o) {
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
@@ -1027,7 +1070,7 @@ RED.subflow = (function() {
case "checkbox":
label.css("cursor","default");
var cblabel = $('<label>').css('width','70%').appendTo(row);
input = $('<input type="checkbox">').css({
input = $('<input type="checkbox">').attr('id', elId).css({
marginTop: 0,
width: 'auto',
height: '34px'
@@ -1045,7 +1088,7 @@ RED.subflow = (function() {
input.prop("checked",boolVal);
break;
case "spinner":
input = $('<input>').css('width','70%').appendTo(row);
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
var spinnerOpts = {};
if (ui.opts.hasOwnProperty('min')) {
spinnerOpts.min = ui.opts.min;
@@ -1057,7 +1100,7 @@ RED.subflow = (function() {
input.val(val.value);
break;
case "cred":
input = $('<input type="password">').css('width','70%').appendTo(row);
input = $('<input type="password">').css('width','70%').attr('id', elId).appendTo(row);
if (node.credentials) {
if (node.credentials[tenv.name]) {
input.val(node.credentials[tenv.name]);
@@ -1074,18 +1117,25 @@ RED.subflow = (function() {
default: 'cred'
})
break;
}
if (input) {
input.attr('id',getSubflowEnvPropertyName(tenv.name))
case "conf-types":
// let clsId = 'config-node-input-' + val.type + '-' + val.value + '-' + Math.floor(Math.random() * 100000);
// clsId = clsId.replace(/\W/g, '-');
// input = $('<input>').css('width','70%').addClass(clsId).attr('id', elId).appendTo(row);
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
const _type = tenv.parent?.type || tenv.type;
RED.editor.prepareConfigNodeSelect(node, tenv.name, _type, 'node-input-subflow-env', null, tenv);
break;
}
}
/**
* Create environment variable input UI
* Build the edit form for a subflow instance
* Also used to build the preview form in the subflow template edit dialog
* @param uiContainer - container for UI
* @param envList - env var definitions of template
*/
function buildEnvUI(uiContainer, envList, node) {
if(RED.subflow.debug) { console.log("buildEnvUI",envList) }
uiContainer.empty();
for (var i = 0; i < envList.length; i++) {
var tenv = envList[i];
@@ -1093,7 +1143,7 @@ RED.subflow = (function() {
continue;
}
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
buildEnvUIRow(row,tenv, tenv.ui || {}, node);
buildEnvUIRow(row, tenv, node);
}
}
// buildEnvUI
@@ -1166,6 +1216,9 @@ RED.subflow = (function() {
delete ui.opts
}
break;
case "conf-types":
delete ui.opts;
break;
default:
delete ui.opts;
}
@@ -1188,8 +1241,9 @@ RED.subflow = (function() {
if (/^subflow:/.test(node.type)) {
var subflowDef = RED.nodes.subflow(node.type.substring(8));
if (subflowDef.env) {
subflowDef.env.forEach(function(env) {
subflowDef.env.forEach(function(env, i) {
var item = {
index: i,
name:env.name,
parent: {
type: env.type,
@@ -1226,14 +1280,20 @@ RED.subflow = (function() {
var nodePropValue = nodeProp;
if (prop.ui && prop.ui.type === "cred") {
nodePropType = "cred";
} else if (prop.ui && prop.ui.type === "conf-types") {
nodePropType = prop.value.type
} else {
switch(typeof nodeProp) {
case "string": nodePropType = "str"; break;
case "number": nodePropType = "num"; break;
case "boolean": nodePropType = "bool"; nodePropValue = nodeProp?"true":"false"; break;
default:
nodePropType = nodeProp.type;
nodePropValue = nodeProp.value;
if (nodeProp) {
nodePropType = nodeProp.type;
nodePropValue = nodeProp.value;
} else {
nodePropType = 'str'
}
}
}
var item = {
@@ -1254,6 +1314,7 @@ RED.subflow = (function() {
}
function exportSubflowInstanceEnv(node) {
if(RED.subflow.debug) { console.log("exportSubflowInstanceEnv",node) }
var env = [];
// First, get the values for the SubflowTemplate defined properties
// - these are the ones with custom UI elements
@@ -1285,7 +1346,7 @@ RED.subflow = (function() {
}
break;
case "cred":
item.value = input.val();
item.value = input.typedInput('value');
item.type = 'cred';
break;
case "spinner":
@@ -1300,6 +1361,9 @@ RED.subflow = (function() {
item.type = 'bool';
item.value = ""+input.prop("checked");
break;
case "conf-types":
item.value = input.val()
item.type = "conf-type"
}
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
env.push(item);
@@ -1313,8 +1377,15 @@ RED.subflow = (function() {
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
}
// Called by subflow.oneditprepare for both instances and templates
/**
* Build the subflow edit form
* Called by subflow.oneditprepare for both instances and templates
* @param {"subflow"|"subflow-template"} type - the type of subflow being edited
* @param {Object} node - the node being edited
*/
function buildEditForm(type,node) {
if(RED.subflow.debug) { console.log("buildEditForm",type,node) }
if (type === "subflow-template") {
// This is the tabbed UI that offers the env list - with UI options
// plus the preview tab
@@ -1330,7 +1401,10 @@ RED.subflow = (function() {
init: init,
createSubflow: createSubflow,
convertToSubflow: convertToSubflow,
// removeSubflow: Internal function to remove subflow
removeSubflow: removeSubflow,
// delete: Prompt user for confirmation
delete: deleteSubflow,
refresh: refresh,
removeInput: removeSubflowInput,
removeOutput: removeSubflowOutput,

View File

@@ -43,12 +43,15 @@ RED.sidebar.config = (function() {
var categories = {};
function getOrCreateCategory(name,parent,label) {
function getOrCreateCategory(name,parent,label,isLocked) {
name = name.replace(/\./i,"-");
if (!categories[name]) {
var container = $('<div class="red-ui-palette-category red-ui-sidebar-config-category" id="red-ui-sidebar-config-category-'+name+'"></div>').appendTo(parent);
var header = $('<div class="red-ui-sidebar-config-tray-header red-ui-palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
let lockIcon
if (label) {
lockIcon = $('<span style="margin-right: 5px"><i class="fa fa-lock"/></span>').appendTo(header)
lockIcon.toggle(!!isLocked)
$('<span class="red-ui-palette-node-config-label"/>').text(label).appendTo(header);
} else {
$('<span class="red-ui-palette-node-config-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
@@ -62,6 +65,7 @@ RED.sidebar.config = (function() {
var icon = header.find("i");
var result = {
label: label,
lockIcon,
list: category,
size: function() {
return result.list.find("li:not(.red-ui-palette-node-config-none)").length
@@ -100,6 +104,9 @@ RED.sidebar.config = (function() {
});
categories[name] = result;
} else {
if (isLocked !== undefined && categories[name].lockIcon) {
categories[name].lockIcon.toggle(!!isLocked)
}
if (categories[name].label !== label) {
categories[name].list.parent().find('.red-ui-palette-node-config-label').text(label);
categories[name].label = label;
@@ -138,17 +145,20 @@ RED.sidebar.config = (function() {
} else {
var currentType = "";
nodes.forEach(function(node) {
var label = RED.utils.getNodeLabel(node,node.id);
var labelText = RED.utils.getNodeLabel(node,node.id);
if (node.type != currentType) {
$('<li class="red-ui-palette-node-config-type">'+node.type+'</li>').appendTo(list);
currentType = node.type;
}
if (node.changed) {
labelText += "!!"
}
var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
entry.data('node',node.id);
nodeDiv.data('node',node.id);
var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv);
var label = $('<div class="red-ui-palette-label"></div>').text(labelText).appendTo(nodeDiv);
if (node.d) {
nodeDiv.addClass("red-ui-palette-node-config-disabled");
$('<i class="fa fa-ban"></i>').prependTo(label);
@@ -170,6 +180,20 @@ RED.sidebar.config = (function() {
nodeDiv.addClass("red-ui-palette-node-config-unused");
}
}
if (!node.valid) {
nodeDiv.addClass("red-ui-palette-node-config-invalid")
const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv)
const errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path");
errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z");
nodeDivAnnotations.append($(errorBadge))
RED.popover.tooltip(nodeDivAnnotations, function () {
if (node.validationErrors && node.validationErrors.length > 0) {
return RED._("editor.errors.invalidProperties")+"<br> - "+node.validationErrors.join("<br> - ")
}
})
}
nodeDiv.on('click',function(e) {
e.stopPropagation();
RED.view.select(false);
@@ -216,7 +240,7 @@ RED.sidebar.config = (function() {
RED.nodes.eachWorkspace(function(ws) {
validList[ws.id.replace(/\./g,"-")] = true;
getOrCreateCategory(ws.id,flowCategories,ws.label);
getOrCreateCategory(ws.id,flowCategories,ws.label, ws.locked);
})
RED.nodes.eachSubflow(function(sf) {
validList[sf.id.replace(/\./g,"-")] = true;
@@ -274,6 +298,15 @@ RED.sidebar.config = (function() {
changes: {},
dirty: RED.nodes.dirty()
}
for (let i = 0; i < selectedNodes.length; i++) {
let node = RED.nodes.node(selectedNodes[i])
if (node.z) {
let ws = RED.nodes.workspace(node.z)
if (ws && ws.locked) {
return
}
}
}
selectedNodes.forEach(function(id) {
var node = RED.nodes.node(id);
try {
@@ -349,9 +382,11 @@ RED.sidebar.config = (function() {
refreshConfigNodeList();
}
});
RED.popover.tooltip($('#red-ui-sidebar-config-filter-all'), RED._("sidebar.config.showAllConfigNodes"));
RED.popover.tooltip($('#red-ui-sidebar-config-filter-unused'), RED._("sidebar.config.showAllUnusedConfigNodes"));
RED.popover.tooltip($('#red-ui-sidebar-config-collapse-all'), RED._("palette.actions.collapse-all"));
RED.popover.tooltip($('#red-ui-sidebar-config-expand-all'), RED._("palette.actions.expand-all"));
}
function flashConfigNode(el) {

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