Compare commits

..

479 Commits

Author SHA1 Message Date
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
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
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
97470e94f1 Merge pull request #4262 from sammachin/patch-3
Handle 204 in httprequest JSON
2023-08-14 11:10:24 +01:00
Kazuhito Yokoi
0dd771351d Fix handling in file nodes when number is specified as file name 2023-08-06 20:35:33 +09: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
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
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
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
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
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
cliyr
ce9462e6aa Apply suggestions from code review
Co-authored-by: zoollcar <zoollcar@qq.com>
2023-02-20 21:42:53 +08:00
Hiroyasu Nishiyama
2e57d80959 add show-global-env action 2023-02-13 14:50:07 +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
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
Nick O'Leary
90d1bb0ae4 Merge branch 'master' into dev 2023-02-03 09:11:46 +00: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
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
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
Dave Conway-Jones
47bacaf58a Add count to join and batch node labels 2023-01-26 22:13:17 +00: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
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
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
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
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
Nick O'Leary
dd2bc44c2d Merge branch 'dev' into dev-jpn 2023-01-01 22:38:20 +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
Kazuhito Yokoi
a661bc1d23 Add Japanese translation for v3.1.0-beta.0 2022-12-31 00:41:35 +09: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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
4cc18c25fe Add drop mode to range node
and include tests
2022-10-29 17:34:29 +01:00
Nick O'Leary
fb499be979 Add context menu to tab bar 2022-10-25 23:44:59 +01:00
Kazuhito Yokoi
c4e277853c Add Japanese translation for button of node URL 2022-10-12 23:24:21 +09: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
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
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
333 changed files with 54612 additions and 14034 deletions

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16]
node-version: [16, 18]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
@@ -29,8 +29,8 @@ 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 }}
# - name: Publish to coveralls.io
# if: ${{ matrix.node-version == 16 }}
# uses: coverallsapp/github-action@v1.1.2
# with:
# github-token: ${{ github.token }}

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,3 +1,208 @@
#### 3.1.0-beta.4: Beta Release
Editor
- Add Japanese translation for 3.1.0 (#4252) @kazuhitoyokoi
- Improve Catalogue visibility (#4248) @Steve-Mcl
- Add support for wiring and moving junctions on touch device (#4244) @Steve-Mcl
- Show errors and statuses of config nodes in the sidebar when no catch node is available (#4231) @bvmensvoort
- Improve wiring for horizontally aligned nodes (#4232) @knolleary
- French translation of Welcome Tours (#4200) @GogoVega
- French translation of v3.1.0-beta.3 changes (#4199) @GogoVega
- add Japanese message for 3.1.0 beta 3 (#4209) @HiroyasuNishiyama
- Dont clone the group nodes `node` array when saving edits (#4208) @Steve-Mcl
Runtime
- Add NR_SUBFLOW_NAME/ID/PATH env vars (#4250) @knolleary
- Evaluate all env vars as part of async flow start (#4230) @knolleary
- Add support for httpStatic middleware (#4229) @knolleary
Nodes
- Fix JSONata in file nodes (#4246) @kazuhitoyokoi
- Fix timeout icon in function and link call nodes (#4253) @kazuhitoyokoi
- Fix connection keep-alive in http request node (#4228) @knolleary
- adding timeout attribute to function node (#4177) @k1ln
- Fix manual mode join when multiple sequences being handled (#4143) @BitCaesar
- Fix delay node flush issue (#4203) @dceejay
- Update status and catch node labels in group mode (#4207) @Steve-Mcl
#### 3.1.0-beta.3: Beta Release
Editor
- Select the item that is specified in a deep link URL (#4113) @Steve-Mcl
- Update to Monaco 0.38.0 (#4189) @Steve-Mcl
- Place subflow outputs/inputs relative to current view (#4183) @knolleary
- Enable RED.view.select to select group by id (#4184) @knolleary
- Combine existing env vars when merging groups (#4182) @knolleary
- Avoid creating empty global-config node if not needed (#4153) @knolleary
- Fix group selection when using lasso (#4108) @knolleary
- Use editor path in generating localStorage keys (#4151) @mw75
- Ensure no node credentials are included when exporting to clipboard (#4112) @knolleary
- Fix jsonata expression test ui (#4097) @knolleary
- Fix search button in palette popover (#4096) @knolleary
Runtime
- Allow options object on each httpStatic configuration (#4109) @kevinGodell
- Ensure non-zero exit codes for errors (#4181) @knolleary
- Ensure external modules are installed synchronously (#4180) @knolleary
- Update dependecies include got (#4155) @knolleary
- Add Japanese translations for v3.1 beta.2 (#4158) @kazuhitoyokoi
- Ensure express server options are applied consistently (#4178) @knolleary
- Remove version info from theme endpoint (#4179) @knolleary
- Add Japanese translations for welcome tour of 3.1.0 beta.2 (#4145) @kazuhitoyokoi
- Added SHA-256 and SHA-512-256 digest authentication (#4100) @sroebert
- Add "timers" types to known types (#4103) @Steve-Mcl
Nodes
- Allow Catch/Status nodes to be scoped to their group (#4185) @NetHans
- MQTT: Option to disable MQTT topic unsubscribe on disconnect (#4078) @flying7eleven
#### 3.1.0-beta.2: Beta Release
Editor
- NEW: Add change icon to tabs (#4068) @knolleary
- NEW: Complete overhaul of Group UX (#4079) @knolleary
- NEW: Add link to node help in node edit dialog footer (#4065) @knolleary
- NEW: Added editor feature for connecting multiple nodes to single node (#4051) @sonntam
- NEW: Increase workspace size to 8000x8000 (#4094) @knolleary
- Ensure node buttons are redrawn when flow lock state is changed (#4091) @knolleary
- Prevent loops being created with junction nodes (#4087) @knolleary
- Prevent opening locked node's edit dialog (#4069) @knolleary
- Reverse direction of tab scroll to expected direction (#4064) @knolleary
- Add cancel operation to editableList (#4077) @HiroyasuNishiyama
- Apply Mermaid diagram for project settings UI (#4054) @kazuhitoyokoi
- Add tooltip for show/hide button on info sidebar (#4050) @kazuhitoyokoi
- Fix align nodes on locked tab (#4072) @HiroyasuNishiyama
- Fix importing connected link nodes into a subflow (#4082) @knolleary
- Fix to add empty marker to empty group (#4060) @HiroyasuNishiyama
- Fix image URLs for v3.0 tour (#4053) @kazuhitoyokoi
- Show scrollbar in notification dialog only when needed (#4048) @kazuhitoyokoi
- Update-monaco-and-typings (#4089) @Steve-Mcl
- Update jquery UI (#4088) @knolleary
- Support i18n of lock/unlock buttons in flow property UI (#4049) @kazuhitoyokoi
- Translation kr (#3895) @hae-iotplatform
- Translation zhcn (请懂中文的帮忙review) (#3952) @cliyr
- Add French translation of nodes (#3964) @GogoVega
- Add French translation (#3962) @GogoVega
- Portuguese Brazilian (pt-BR) translation (#3804) @FabsMuller
Runtime
- NEW: Generate stable ids for subflow instance internal nodes (#4093) @knolleary
- NEW: Change default file name to flows.json in project feature (#4073) @kazuhitoyokoi
- NEW: Deprecate synchronous access to jsonata (#4090) @knolleary
- Add Node 18 to test matrix (#4084) @knolleary
- Bump minimum nodejs version supported to match documented value (#4086) @knolleary
- Update monaco docs link in settings.js (#4075) @Steve-Mcl
- Remove duplicated messages in the message catalog (#4066) @kazuhitoyokoi
- Ensure errors in preDeliver callback are handled (#3911) @knolleary
- Fix "EADDRINUSE" error (#4046) @bggbr
Nodes
- Link Call: Clear link-call timeouts when node is closed (#4085) @knolleary
- Join: ensure inflight status is cleared when in auto mode (#4083) @knolleary
- File Out: Fix extra newline append for multipart file write (#3915) @dceejay
- Add validators for complete and link call nodes (#4056) @kazuhitoyokoi
#### 3.1.0-beta.1: Beta Release
Editor
- NEW: Locking Flows (#3938) @knolleary
- NEW: Improve UX around hiding flows via context menu (#3930) @knolleary
- NEW: Add support for inline image in markdown editor by drag and drop of an image file (#4006) @HiroyasuNishiyama
- NEW: Add support for mermaid diagram to markdown editor (#4007) @HiroyasuNishiyama
- NEW: Support uri fragments for nodes and groups including edit support (#3870) @knolleary
- NEW: Add global environment variable feature (#3941) @HiroyasuNishiyama
- Remember compact/pretty flow export user choice (#3974) @Steve-Mcl
- fix .red-ui-notification class (#4035) @xiaobinqt
- Fix border radius on Modules list header (#4038) @bonanitech
- fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama
- Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama
- Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama
- fix hide subflow tooltip (#4033) @HiroyasuNishiyama
- Fix disabled menu items in project feature (#4027) @kazuhitoyokoi
- Let themes change radialMenu text colors (#3995) @bonanitech
- Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi
- Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi
- Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi
- Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi
- Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary
- Handle replacing unknown node inside group or subflow (#3921) @knolleary
- Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo
- i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama
- add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama
- Fix autocomplete entry for responseUrl (#3884) @knolleary
- Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama
- Fix search type with spaces (#3841) @Steve-Mcl
- Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama
- Add button type to the adding SSH key button (#3866) @kazuhitoyokoi
- Check radio button as default in project dialog (#3879) @kazuhitoyokoi
- Add $clone as supported function (#3874) @HiroyasuNishiyama
- Env var jsonata (#3807) @HiroyasuNishiyama
- Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi
Runtime
- Force IPv4 name resolution to have priority (#4019) @dceejay
- Fix async loading of modules containing both nodes and plugins (#3999) @knolleary
- Use main branch as default in project feature (#4036) @kazuhitoyokoi
- Rename package var to avoid strict mode error (#4020) @knolleary
- Fix typos in settings.js (#4013) @ypid
- Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary
- Ignore commit error in project feature (#3987) @kazuhitoyokoi
- Update dependencies (#3969) @knolleary
- Add check that node sends object rather than primitive type (#3909) @knolleary
- Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary
- Fix nodesDir scan when node package has js/html in sub dir to package.json (#3867) @Steve-Mcl
- Fix file permissions (#3917) @kazuhitoyokoi
- ci: add minimum GitHub token permissions for workflows (#3907) @boahc077
Nodes
- Catch: fix typo in catch.html (#3965) @we11adam
- Change: Fix change node overwriting msg with itself (#3899) @dceejay
- Comment node: Clarify where the text will appear (#4004) @dirkjanfaber
- CSV: change replace to replaceAll (#3990) @dceejay
- CSV node: check header properties for ' and " (#3920) @dceejay
- CSV: Fix for CSV undefined property (#3906) @dceejay
- Delay: let delay node handle both flush then reset (#3898) @dceejay
- Function: Limit number of ports in function node (#3886) @kazuhitoyokoi
- Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi
- Function: add function node monaco types util and promisify (#3868) @Steve-Mcl
- HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary
- HTTP Request: Support form-data arrays (#3991) @hardillb
- HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary
- HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb
- HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi
- HTTP Response: Ensure statusCode is a number (#3894) @hardillb
- Inject: Allow Inject node to work with async context stores (#4021) @knolleary
- Join/Batch: Add count to join and batch node labels (#4028) @dceejay
- MQTT: Fix birth topic handling in MQTT node (#3905) @Steve-Mcl
- MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi
- MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl
- MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl
- MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary
- MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl
- Range: Add drop mode to range node (#3935) @dceejay
- Remove done from describe (#3873) @HiroyasuNishiyama
- Split node: avoid duplicate done call for buffer split (#4000) @knolleary
- Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi
- TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay
- Template: Add information about environment variable to template node (#3882) @kazuhitoyokoi
- Trigger: Hide trigger node repeat send option if sending nothing (#4023) @dceejay
- Watch: fix watch node test on MacOS/ARM (#3942) @HiroyasuNishiyama
#### 3.0.2: Maintenance Release
Editor

View File

@@ -151,6 +151,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
@@ -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",
@@ -224,7 +226,7 @@ module.exports = function(grunt) {
"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/src/vendor/ace/ext-language_tools.js"
],
// "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
// // TODO: resolve relative resource paths in
@@ -233,6 +235,9 @@ module.exports = function(grunt) {
"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"
],
"packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js": [
"node_modules/mermaid/dist/mermaid.min.js"
]
}
}
@@ -403,7 +408,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

@@ -2,8 +2,7 @@
http://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.

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.0.2",
"version": "3.1.0-beta.4",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,30 +26,30 @@
}
],
"dependencies": {
"acorn": "8.7.1",
"acorn": "8.8.2",
"acorn-walk": "8.2.0",
"ajv": "8.11.0",
"async-mutex": "0.3.2",
"ajv": "8.12.0",
"async-mutex": "0.4.0",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.20.0",
"body-parser": "1.20.2",
"cheerio": "1.0.0-rc.10",
"clone": "2.1.2",
"content-type": "1.0.4",
"content-type": "1.0.5",
"cookie": "0.5.0",
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cronosjs": "1.7.1",
"denque": "2.1.0",
"express": "4.18.1",
"express": "4.18.2",
"express-session": "1.17.3",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"got": "11.8.5",
"fs-extra": "11.1.1",
"got": "12.6.0",
"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",
@@ -60,7 +60,7 @@
"memorystore": "1.6.7",
"mime": "3.0.0",
"moment": "2.29.4",
"moment-timezone": "0.5.34",
"moment-timezone": "0.5.43",
"mqtt": "4.3.7",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
@@ -72,21 +72,21 @@
"passport": "0.6.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",
"raw-body": "2.5.2",
"semver": "7.5.0",
"tar": "6.1.13",
"tough-cookie": "4.1.2",
"uglify-js": "3.17.4",
"uuid": "9.0.0",
"ws": "7.5.6",
"xml2js": "0.4.23"
"xml2js": "0.6.0"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
"bcrypt": "5.1.0"
},
"devDependencies": {
"dompurify": "2.3.10",
"grunt": "1.5.3",
"dompurify": "2.4.1",
"grunt": "1.6.1",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
"grunt-concurrent": "3.0.0",
@@ -108,17 +108,18 @@
"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": "^9.4.3",
"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.2",
"nodemon": "2.0.20",
"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"

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

View File

@@ -46,14 +46,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);
var editorApp = express();
const editorApp = apiUtil.createExpressApp(settings)
if (settings.requireHttps === true) {
editorApp.enable('trust proxy');
editorApp.use(function (req, res, next) {
@@ -86,7 +87,7 @@ module.exports = {
//Projects
var projects = require("./projects");
projects.init(runtimeAPI);
projects.init(settings, runtimeAPI);
editorApp.use("/projects",projects.app());
// Locales

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

@@ -17,13 +17,15 @@
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

@@ -19,6 +19,7 @@ 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 +28,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 +40,7 @@ var defaultContext = {
vendorMonaco: ""
}
};
var settings;
var theme = null;
var themeContext = clone(defaultContext);
@@ -92,7 +93,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 +115,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) {

View File

@@ -37,7 +37,6 @@ var adminApp;
var server;
var editor;
/**
* Initialise the module.
* @param {Object} settings The runtime settings
@@ -49,7 +48,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 +63,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 +127,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": "3.1.0-beta.4",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,14 +16,14 @@
}
],
"dependencies": {
"@node-red/util": "3.0.2",
"@node-red/editor-client": "3.0.2",
"@node-red/util": "3.1.0-beta.4",
"@node-red/editor-client": "3.1.0-beta.4",
"bcryptjs": "2.4.3",
"body-parser": "1.20.0",
"body-parser": "1.20.2",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.3",
"express": "4.18.1",
"express": "4.18.2",
"memorystore": "1.6.7",
"mime": "3.0.0",
"multer": "1.4.5-lts.1",
@@ -35,6 +35,6 @@
"ws": "7.5.6"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
"bcrypt": "5.1.0"
}
}

View File

@@ -1175,8 +1175,10 @@
"languages": {
"de": "Deutsch",
"en-US": "Englisch",
"fr": "Französisch",
"ja": "Japanisch",
"ko": "Koreanisch",
"pt-BR":"Portugiesisch",
"ru": "Russisch",
"zh-CN": "Chinesisch (Vereinfacht)",
"zh-TW": "Chinesisch (Traditionell)"

View File

@@ -23,7 +23,11 @@
"position": "Position",
"enable": "Enable",
"disable": "Disable",
"upload": "Upload"
"upload": "Upload",
"lock": "Lock",
"unlock": "Unlock",
"locked": "Locked",
"unlocked": "Unlocked"
},
"type": {
"string": "string",
@@ -53,22 +57,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 +113,7 @@
"displayStatus": "Show node status",
"displayConfig": "Configuration nodes",
"import": "Import",
"importExample": "Import Example Flow",
"export": "Export",
"search": "Search flows",
"searchInput": "search your flows",
@@ -403,6 +416,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 +505,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",
"deleteReconnect": "Delete and Reconnect",
"importNode": "Import nodes",
"exportNode": "Export nodes",
"nudgeNode": "Move selected nodes (1px)",
@@ -571,6 +587,7 @@
"editor": {
"title": "Manage palette",
"palette": "Palette",
"allCatalogs": "All Catalogs",
"times": {
"seconds": "seconds ago",
"minutes": "minutes ago",
@@ -683,9 +700,11 @@
"empty": "empty",
"globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action",
"find": "Find in workspace",
"copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard",
"showFlow": "Show",
"hideFlow": "Hide",
"find": "Find in workspace"
"hideFlow": "Hide"
},
"help": {
"name": "Help",
@@ -986,7 +1005,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",
@@ -1181,8 +1203,10 @@
"languages": {
"de": "German",
"en-US": "English",
"fr": "French",
"ja": "Japanese",
"ko": "Korean",
"pt-BR":"Portuguese",
"ru": "Russian",
"zh-CN": "Chinese(Simplified)",
"zh-TW": "Chinese(Traditional)"
@@ -1208,5 +1232,10 @@
"node": "Node",
"junction": "Junction",
"linkNodes": "Link Nodes"
},
"env-var": {
"environment": "Environment",
"header": "Global Environment Variables",
"revert": "Revert"
}
}

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,274 @@
{
"$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."
}
}

View File

@@ -23,7 +23,11 @@
"position": "配置",
"enable": "有効",
"disable": "無効",
"upload": "アップロード"
"upload": "アップロード",
"lock": "固定",
"unlock": "固定を解除",
"locked": "固定済み",
"unlocked": "固定なし"
},
"type": {
"string": "文字列",
@@ -53,8 +57,10 @@
"confirmDelete": "削除の確認",
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
"dropImageHere": "ここに画像ファイルをドロップしてください",
"addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加",
"closeFlow": "フローを閉じる",
"hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
@@ -68,7 +74,13 @@
"enabled": "有効",
"disabled": "無効",
"info": "詳細",
"selectNodes": "ノードをクリックして選択"
"selectNodes": "ノードをクリックして選択",
"enableFlow": "フローを有効化",
"disableFlow": "フローを無効化",
"lockFlow": "フローを固定",
"unlockFlow": "フローの固定を解除",
"moveToStart": "フローを先頭へ移動",
"moveToEnd": "フローを最後へ移動"
},
"menu": {
"label": {
@@ -101,6 +113,7 @@
"displayStatus": "ノードのステータスを表示",
"displayConfig": "設定ノード",
"import": "読み込み",
"importExample": "フロー例を読み込み",
"export": "書き出し",
"search": "ノードを検索",
"searchInput": "ノードを検索",
@@ -403,6 +416,7 @@
},
"errors": {
"noNodesSelected": "<strong>サブフローを作成できません</strong>: ノードが選択されていません",
"acrossMultipleGroups": "複数のグループをまたがるサブフローは作成できません",
"multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています"
}
},
@@ -491,12 +505,14 @@
"unassigned": "未割当",
"global": "グローバル",
"workspace": "ワークスペース",
"editor": "編集ダイアログ",
"selectAll": "全てのノードを選択",
"selectNone": "選択を外す",
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
"deleteReconnect": "削除と再接続",
"importNode": "フローの読み込み",
"exportNode": "フローの書き出し",
"nudgeNode": "選択したノードを移動(移動量小)",
@@ -571,6 +587,7 @@
"editor": {
"title": "パレットの管理",
"palette": "パレット",
"allCatalogs": "全カタログ",
"times": {
"seconds": "数秒前",
"minutes": "数分前",
@@ -683,9 +700,11 @@
"empty": "空",
"globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました",
"showFlow": "表示",
"hideFlow": "非表示",
"find": "ワークスペース内を検索"
"hideFlow": "非表示"
},
"help": {
"name": "ヘルプ",
@@ -986,7 +1005,10 @@
"quote": "引用",
"link": "リンク",
"horizontal-rule": "区切り線",
"toggle-preview": "プレビュー表示切替え"
"toggle-preview": "プレビュー表示切替え",
"mermaid": {
"summary": "Mermaid図"
}
},
"bufferEditor": {
"title": "バッファエディタ",
@@ -1181,8 +1203,10 @@
"languages": {
"de": "ドイツ語",
"en-US": "英語",
"fr": "フランス語",
"ja": "日本語",
"ko": "韓国語",
"pt-BR": "ポルトガル語",
"ru": "ロシア語",
"zh-CN": "中国語(簡体)",
"zh-TW": "中国語(繁体)"
@@ -1209,6 +1233,11 @@
"junction": "分岐点",
"linkNodes": "Linkード"
},
"env-var": {
"environment": "環境変数",
"header": "大域環境変数",
"revert": "破棄"
},
"action-list": {
"toggle-show-tips": "ヒント表示切替",
"show-about": "Node-REDの説明を表示",
@@ -1293,6 +1322,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": "ユーザ設定を表示",
@@ -1352,6 +1382,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

@@ -24,16 +24,29 @@
"delete": "정말로 '__label__' 을(를) 삭제하시겠습니까?",
"dropFlowHere": "플로우를 이곳에 가져오세요",
"addFlow": "플로우 추가",
"addFlowToRight": "오른쪽에 플로우 추가",
"hideFlow": "플로우 숨기기",
"hideOtherFlows": "다른 플로우 숨기기",
"showAllFlows": "모든 플로우 보기",
"hideAllFlows": "모든 플로우 숨기기",
"hiddenFlows": "__count__개의 숨겨진 플로우 보기",
"hiddenFlows_plural": "__count__개의 숨겨진 플로우 보기",
"showLastHiddenFlow": "마지막으로 숨겨진 플로우 보기",
"listFlows": "플로우 리스트",
"listSubflows": "서브 플로우 리스트",
"status": "상태",
"enabled": "사용가능",
"disabled": "사용불가능",
"info": "상세내역"
"info": "상세내역",
"selectNodes": "선택할 노드 클릭"
},
"menu": {
"label": {
"view": {
"view": "창",
"grid": "눈금선",
"storeZoom": "불러오기 시 확대/축소 복원",
"storePosition": "불러오기 시 스크롤 위치 복원",
"showGrid": "눈금선 보이기",
"snapGrid": "노드 배치 보조 켜기",
"gridSize": "눈금선 크기",
@@ -41,7 +54,9 @@
"defaultDir": "기본",
"ltr": "왼쪽 -> 오른쪽",
"rtl": "오른쪽 -> 왼쪽",
"auto": "자동배분"
"auto": "자동배분",
"language": "언어",
"browserDefault": "브라우저 기본값"
},
"sidebar": {
"show": "우측사이드바 보이기"
@@ -49,18 +64,19 @@
"palette": {
"show": "팔렛트 보이기"
},
"edit": "수정",
"settings": "설정",
"userSettings": "사용자 설정",
"nodes": "노드설정",
"displayStatus": "노드상태 보이기",
"displayConfig": "설정노드 보기",
"displayStatus": "노드 상태 보이기",
"displayConfig": "설정 노드 보기",
"import": "가져오기",
"export": "내보내기",
"search": "플로우 검색",
"searchInput": "플로우 검색",
"subflows": "보조 플로우",
"createSubflow": "보조 플로우 생성",
"selectionToSubflow": "보조 플로우 선택",
"subflows": "서브 플로우",
"createSubflow": "서브 플로우 생성",
"selectionToSubflow": "서브 플로우 선택",
"flows": "플로우",
"add": "추가",
"rename": "이름변경",
@@ -71,19 +87,43 @@
"editPalette": "팔렛트 관리",
"other": "기타",
"showTips": "Tip 보기",
"showWelcomeTours": "새 버전에 대한 가이드 보기 표시",
"help": "Node-RED 웹사이트",
"projects": "프로젝트",
"projects-new": "신규",
"projects-open": "열기",
"projects-settings": "프로젝트 설정",
"showNodeLabelDefault": "새로 추가된 노드의 라벨 보이기"
"showNodeLabelDefault": "새로 추가된 노드의 라벨 보이기",
"codeEditor": "Code Editor",
"groups": "그룹",
"groupSelection": "그룹 선택",
"ungroupSelection": "그룹 선택 해제",
"groupMergeSelection": "선택 항목 병합",
"groupRemoveSelection": "선택 그룹 제거",
"arrange": "배치",
"alignLeft": "왼쪽으로 정렬",
"alignCenter": "가운데 정렬",
"alignRight": "오른쪽으로 정렬",
"alignTop": "맨 위에 정렬",
"alignMiddle": "맨 위에 정렬",
"alignBottom": "맨 아래 정렬",
"distributeHorizontally": "수평으로 배치",
"distributeVertically": "수직으로 배치",
"moveToBack": "맨 뒤로 이동",
"moveToFront": "맨 앞으로 이동",
"moveBackwards": "뒤로 이동",
"moveForwards": "앞으로 이동"
}
},
"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__ 에 로그인됨",
@@ -99,12 +139,17 @@
}
},
"notification": {
"state": {
"flowsStopped": "플로우 중지됨",
"flowsStarted": "플로우 시작됨"
},
"warning": "<strong>경고</strong>: __message__",
"warnings": {
"undeployedChanges": "변경사항 배포가 취소되었습니다",
"nodeActionDisabled": "노드 실행이 비활성화 되었습니다",
"nodeActionDisabledSubflow": "보조 플로우에서 노드 실행이 비활성화 되었습니다",
"missing-types": "<p>타입이 없는 노드로인해 플로우가 중지되었습니다</p>",
"missing-modules": "<p>누락된 모듈로 인해 플로우가 중지되었습니다.</p>",
"safe-mode": "<p>[안전모드] 플로우가 정지되었습니다.</p><p>플로우의 수정과 배포가 가능합니다. 다시 배포버튼을 누르세요.</p>",
"restartRequired": "업그레이드한 모듈을 유효화하기 위해 Node-RED를 재시작 합니다 ",
"credentials_load_failed": "<p>인증정보 복호화에 실패하여 플로우가 멈췄습니다. </p><p>인증정보는 암호화 되어있습니다. 프로젝트의 암호화 키가 깨졌거나 정상적이지 않습니다.</p>",
@@ -132,7 +177,12 @@
"updated": "'__project__'가 변경 되었습니다",
"pull": "'__project__'를 다시 가져왔습니다",
"revert": "'__project__'를 취소했습니다",
"merge-complete": "Git 병합이 완료되었습니다"
"merge-complete": "Git 병합이 완료되었습니다",
"setupCredentials": "자격 증명 설정",
"setupProjectFiles": "프로젝트 파일 설정",
"no": "취소",
"createDefault": "기본 프로젝트 파일 만들기",
"mergeConflict": "병합 충돌 표시"
},
"label": {
"manage-project-dep": "프로젝트 의존성 관리",
@@ -152,10 +202,14 @@
"node_plural": "__count__ 개의 노드",
"configNode": "__count__ 개의 설정 노드",
"configNode_plural": "__count__ 개의 설정 노드",
"group": "__count__ 개의 그룹",
"group_plural": "__count__ 개의 그룹",
"flow": "__count__ 개의 플로우",
"flow_plural": "__count__ 개의 플로우",
"subflow": "__count__ 개의 서브 플로우",
"subflow_plural": "__count__ 개의 서브 플로우",
"replacedNodes": "__count__ 개의 교체된 노드",
"replacedNodes_plural": "__count__ 개의 교체된 노드",
"pasteNodes": "여기에 노드를 붙여넣기 하세요",
"selectFile": "불러올 파일을 선택하세요",
"importNodes": "노드 불러오기",
@@ -163,28 +217,46 @@
"download": "다운로드",
"importUnrecognised": "알 수 없는 형식 :",
"importUnrecognised_plural": "알 수 없는 형식 :",
"importDuplicate": "가져온 중복 노드:",
"importDuplicate_plural": "가져온 중복 노드:",
"nodesExported": "클립보드에 노드 내보내기",
"nodesImported": "불러오기 : ",
"nodeCopied": "__count__개의 노드가 복사 되었습니다",
"nodeCopied_plural": "__count__개의 노드가 복사 되었습니다",
"nodeCopied": "__count__개의 노드가 복사되었습니다",
"nodeCopied_plural": "__count__개의 노드가 복사되었습니다",
"groupCopied": "__count__ 개의 그룹이 복사되었습니다",
"groupCopied_plural": "__count__ 개의 그룹이 복사되었습니다",
"groupStyleCopied": "그룹 스타일이 복사되었습니다",
"invalidFlow": "정상적지 않은 플로우 : __message__",
"recoveredNodes": "복구된 노드",
"recoveredNodesInfo": "이 플로우의 노드를 가져올 때 유효한 플로우 ID가 누락되었습니다. 해당 플로우에 추가되었으므로 복원하거나 삭제할 수 있습니다.",
"recoveredNodesNotification": "<p>유효하지 않은 플로우 ID를 가진 노드입니다.</p><p>'__flowName__' 라는 플로우에 추가되었습니다.</p>",
"export": {
"selected": "선택된 노드",
"current": "현재 플로우",
"all": "모든 플로우",
"compact": "압축형식",
"formatted": "서식유지",
"copy": "클립보드로 내보내기"
"copy": "클립보드로 내보내기",
"export": "라이브러리로 내보내기",
"exportAs": "내보내기",
"overwrite": "확인",
"exists": "<p><b>\"__file__\"</b> 이미 존재합니다.</p><p>교체하시겠습니까?</p>"
},
"import": {
"import": "가져올 위치 : ",
"importSelected": "선택 항목 가져오기",
"importCopy": "사본 가져오기",
"viewNodes": "노드 보기...",
"newFlow": "새로운 플로우",
"replace": "교체",
"errors": {
"notArray": "입력이 JSON 배열이 아닙니다",
"itemNotObject": "입력이 올바른 플로우가 아닙니다 - __index__는 노드 오브젝트가 아닙니다",
"missingId": "입력이 올바른 플로우가 아닙니다 - __index__의 'id' 속성이 없습니다",
"missingType": "입력이 올바른 플로우가 아닙니다 - __index__의 'type' 속성이 없습니다"
}
},
"conflictNotification1": "가져오는 노드 중 일부가 이미 작업 공간에 있습니다..",
"conflictNotification2": "가져올 노드와 기존 노드를 바꿀지 아니면 복사본을 가져올지 선택합니다."
},
"copyMessagePath": "Path가 복사 되었습니다",
"copyMessageValue": "Value가 복사 되었습니다",
@@ -198,6 +270,10 @@
"modifiedFlowsDesc": "변경사항이 있는 플로우만 배포합니다",
"modifiedNodes": "변경된 노드",
"modifiedNodesDesc": "변경사항이 있는 노드만 배포합니다",
"startFlows": "시작",
"startFlowsDesc": "플로우를 시작합니다",
"stopFlows": "중지",
"stopFlowsDesc": "플로우를 중지합니다",
"restartFlows": "플로우 재시작",
"restartFlowsDesc": "현재 배포된 플로우를 재시작합니다",
"successfulDeploy": "배포가 성공했습니다",
@@ -266,6 +342,7 @@
"newVersionError": "New Version의 JSON 형식이 올바르지 않습니다 :"
},
"subflow": {
"editSubflowInstance": "서브 플로우 인스턴스 수정: __name__",
"editSubflow": "플로우 템플릿 수정 : __name__",
"edit": "플로우 템플릿 수정",
"subflowInstances": "서브 플로우 템플릿에 __count__개의 인스턴스가 있습니다",
@@ -273,14 +350,39 @@
"editSubflowProperties": "속성 수정",
"input": "입력:",
"output": "출력:",
"status": "상태 노드",
"deleteSubflow": "서브 플로우 삭제",
"confirmDelete": "서브 플로우를 삭제하시겠습니까?",
"info": "상세내역",
"category": "카테고리",
"module": "모듈",
"license": "라이선스",
"licenseNone": "없음",
"licenseOther": "Other",
"type": "노드",
"version": "버전",
"versionPlaceholder": "x.y.z",
"keys": "키워드",
"keysPlaceholder": "키워드(쉼표로 구분)를 입력해주세요",
"author": "이름",
"authorPlaceholder": "이름 또는 이메일을 입력해주세요",
"desc": "설명",
"env": {
"restore": "서브 플로우 기본값으로 복원",
"remove": "환경 변수 제거"
},
"errors": {
"noNodesSelected": "<strong>서브 플로우를 생성할 수 없습니다</strong> : 노드가 선택되지 않았습니다",
"multipleInputsToSelection": "<strong>서브 플로우를 생성할 수 없습니다</strong> : 복수의 입력이 선택되었습니다"
}
},
"group": {
"editGroup": "그룹 수정: __name__",
"errors": {
"cannotCreateDiffGroups": "다른 그룹의 노드를 사용하여 그룹을 생성할 수 없습니다",
"cannotAddSubflowPorts": "그룹에 서브 플로우 포트를 추가할 수 없습니다"
}
},
"editor": {
"configEdit": "수정",
"configAdd": "추가",
@@ -294,19 +396,20 @@
"addNewType": "__type__의 노드타입 추가 ...",
"nodeProperties": "노드 속성",
"label": "명칭",
"color": "Color",
"portLabels": "포트 설정",
"labelInputs": "입력",
"labelOutputs": "출력",
"settingIcon": "아이콘",
"default": "default",
"noDefaultLabel": "없음",
"defaultLabel": "기본 명칭",
"searchIcons": "아이콘 조회",
"useDefault": "기본설정 사용",
"description": "상세 내역",
"show": "보이기",
"hide": "숨기기",
"errors": {
"scopeChange": "범위를 변경하게 되면 다른 플로우의 노드가 사용이 불가능해 집니다."
"scopeChange": "범위를 변경하게 되면 다른 플로우의 노드가 사용이 불가능해 집니다.",
"invalidProperties": "유효하지 않은 속성:"
}
},
"keyboard": {
@@ -319,10 +422,11 @@
"global": "글로벌",
"workspace": "작업공간",
"selectAll": "모든 노드 선택",
"selectAllConnected": "모든 연결된 노드 선택",
"selectNone": "노드 선택 취소",
"selectAllConnected": "연결된 모든 노드 선택",
"addRemoveNode": "노드 추가/삭제",
"editSelected": "선택된 노드 수정",
"deleteSelected": "선택된 노드 링크 삭제",
"deleteSelected": "선택된 노드 또는 링크 삭제",
"importNode": "노드 불러오기",
"exportNode": "노드 내보내기",
"nudgeNode": "선택된 노드 이동 (1px)",
@@ -332,9 +436,14 @@
"copyNode": "선택된 노드 복사",
"cutNode": "선택된 노드 잘라내기",
"pasteNode": "노드 붙여넣기",
"copyGroupStyle": "그룹 스타일 복사하기",
"pasteGroupStyle": "그룹 스타일 붙여넣기",
"undoChange": "마지막 변경 되돌리기",
"redoChange": "다시 실행하기",
"searchBox": "검색창 열기",
"managePalette": "팔렛트 관리"
"managePalette": "팔렛트 관리",
"actionList": "액션 목록",
"splitWireWithLinks": "링크 노드로 선택 항목 분할"
},
"library": {
"library": "라이브러리",
@@ -342,13 +451,16 @@
"saveToLibrary": "라이브러리로 저장...",
"typeLibrary": "__type__ 라이브러리",
"unnamedType": "이름없는 __type__",
"exportedToLibrary": "노드를 라이브러리로 내보냈습니다.",
"dialogSaveOverwrite": "__libraryType__이 __libraryName__으로 이미 등록되어있습니다. 덮어쓸까요?",
"invalidFilename": "파일명이 올바르지 않습니다",
"savedNodes": "저장된 노드",
"savedType": "저장된 __type__",
"saveFailed": "저장 실패 : __message__",
"newFolder": "새로운 폴더",
"types": {
"examples": "예시"
"local": "로컬",
"examples": "예시"
}
},
"palette": {
@@ -358,9 +470,13 @@
"addCategory": "추가 ...",
"label": {
"subflows": "서브 플로우",
"network": "네트워크",
"common": "일반",
"input": "입력",
"output": "출력",
"function": "기능",
"sequence": "sequence",
"parser": "parser",
"social": "소셜",
"storage": "저장",
"analysis": "분석",
@@ -379,7 +495,8 @@
"nodeEnabled_plural": "노드가 활성화 되었습니다:",
"nodeDisabled": "노드가 비활성화 되었습니다:",
"nodeDisabled_plural": "노드가 비활성화 되었습니다:",
"nodeUpgraded": "__module__ 노드모듈이 __version__으로 업그레이드 되었습니다"
"nodeUpgraded": "__module__ 노드모듈이 __version__으로 업그레이드 되었습니다",
"unknownNodeRegistered": "Error loading node: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "팔렛트 관리",
@@ -426,6 +543,8 @@
"sortAZ": "a-z",
"sortRecent": "최근",
"more": "+ __count__ 개 더 보기",
"upload": "Upload module tgz file",
"refresh": "모듈 목록 새로 고침",
"errors": {
"catalogLoadFailed": "<p>노드 카탈로그를 설치하지 못했습니다.</p><p>브라우저 콘솔로그를 참고하세요.</p>",
"installFailed": "<p>설치 실패 : __module__</p><p>__message__</p><p>브라우저 콘솔로그를 참고하세요.</p>",
@@ -466,6 +585,7 @@
"label": "정보",
"node": "노드",
"type": "타입",
"group": "Group",
"module": "모듈",
"id": "ID",
"status": "상태",
@@ -488,7 +608,23 @@
"nodeHelp": "노드 도움말",
"none": "없음",
"arrayItems": "__count__ 개의 항목",
"showTips": "설정에서 도움말을 열 수 있습니다. "
"showTips": "설정에서 도움말을 열 수 있습니다. ",
"outline": "개요",
"empty": "비우기",
"globalConfig": "전역 설정 노드",
"triggerAction": "트리거 작업",
"find": "작업 공간에서 찾기"
},
"help": {
"name": "도움말",
"label": "도움말",
"search": "도움말 검색",
"nodeHelp": "노드 도움말 보기",
"showHelp": "도움말 보기",
"showInOutline": "요약 보기",
"showTopics": "토픽 보기",
"noHelp": "선택한 도움말 항목이 없습니다",
"changeLog": "릴리즈 정보"
},
"config": {
"name": "노드 설정",
@@ -498,7 +634,9 @@
"subflows": "보조 플로우",
"flows": "플로우",
"filterAll": "전체",
"showAllConfigNodes": "모든 설정 노드 보기",
"filterUnused": "미사용",
"showAllUnusedConfigNodes": "사용하지 않는 모든 설정 노드 보기",
"filtered": "__count__ 개 숨김"
},
"context": {
@@ -509,8 +647,11 @@
"empty": "공백",
"node": "노드",
"flow": "플로우",
"global": "Global",
"deleteConfirm": "정말로 이 아이템을 지우시겠습니까?"
"global": "글로벌",
"deleteConfirm": "정말로 이 아이템을 지우시겠습니까?",
"autoRefresh": "선택 변경 시 새로 고침",
"refrsh": "새로고침",
"delete": "삭제"
},
"palette": {
"name": "팔레트 관리",
@@ -525,6 +666,7 @@
"noSummaryAvailable": "요약 없음",
"editDescription": "프로젝트 상세내역 수정",
"editDependencies": "프로젝트 의존성 수정",
"noDescriptionAvailable": "설명 없음",
"editReadme": "README.md 수정",
"showProjectSettings": "프로젝트 설정 보이기",
"projectSettings": {
@@ -537,6 +679,10 @@
"files": "파일",
"flow": "플로우",
"credentials": "인증정보",
"package": "Package",
"packageCreate": "변경 내용이 저장될 때 파일이 생성됩니다",
"fileNotExist": "파일이 존재하지 않습니다",
"selectFile": "파일 선택",
"invalidEncryptionKey": "잘못된 암호화 키",
"encryptionEnabled": "암호화 활성화",
"encryptionDisabled": "암호화 비활성화",
@@ -674,15 +820,29 @@
"bin": "buffer",
"date": "timestamp",
"jsonata": "expression",
"env": "env variable"
"env": "env variable",
"cred": "credential"
}
},
"editableList": {
"add": "추가"
"add": "추가",
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "결과 없음",
"addNode": "노드 추가 ..."
"addNode": "노드 추가 ...",
"options": {
"configNodes": "설정 노드",
"unusedConfigNodes": "사용되지 않는 설정 노드",
"invalidNodes": "잘못된 노드",
"uknownNodes": "알 수 없는 노드",
"unusedSubflows": "사용되지 않는 서브 플로우",
"hiddenFlows": "숨겨진 플로우",
"modifiedNodes": "수정된 노드 및 플로우",
"thisFlow": "현재 플로우"
}
},
"expressionEditor": {
"functions": "기능",
@@ -703,15 +863,36 @@
"eval": "형식 오류 :\n __message__"
}
},
"monaco": {
"setTheme": "테마 설정"
},
"jsEditor": {
"title": "자바스크립트 에디터"
},
"textEditor": {
"title": "텍스트 에디터"
},
"jsonEditor": {
"title": "JSON 에디터",
"format": "JSON 형식"
"format": "JSON 형식",
"rawMode": "JSON 수정",
"uiMode": "비주얼 편집기",
"rawMode-readonly": "JSON",
"uiMode-readonly": "비주얼",
"insertAbove": "위로 삽입",
"insertBelow": "아래로 삽입",
"addItem": "속성 추가",
"copyPath": "속성의 키값 복사",
"expandItems": "속성 펼치기",
"collapseItems": "속성 접기",
"duplicate": "복사",
"error": {
"invalidJSON": "비유효한 JSON: "
}
},
"markdownEditor": {
"title": "Markdown 에디터",
"expand": "Expand",
"format": "Markdown 형식",
"heading1": "제목 레벨1",
"heading2": "제목 레벨2",
@@ -741,6 +922,7 @@
"desc2": "이 기능을 건너뛰어도 상관없습니다. 언제든지 프로젝트 메뉴에서 첫번째 프로젝트를 만들 수 있습니다.",
"create": "프로젝트 생성",
"clone": "프로젝트 복제",
"openExistingProject": "기존 프로젝트 열기",
"not-right-now": "나중에"
},
"git-config": {
@@ -858,7 +1040,8 @@
"not-git": "git 저장소가 아닙니다",
"no-resource": "저장소아 없습니다",
"cant-get-ssh-key-path": "에러! 선택한 SSH키 경로를 가져올 수 없습니다.",
"unexpected_error": "예기치 않은 에러"
"unexpected_error": "예기치 않은 에러",
"clearContext": "프로젝트 전환 시 context 삭제"
},
"delete": {
"confirm": "프로젝트를 정말 지우시겠습니까?"
@@ -897,7 +1080,26 @@
},
"editor-tab": {
"properties": "속성",
"envProperties": "환경 변수",
"module": "모듈 속성",
"description": "상세 내역",
"appearance": "모양"
"appearance": "모양",
"preview": "UI 프리뷰",
"defaultValue": "기본값"
},
"tourGuide": {
"takeATour": "둘러보기",
"start": "시작",
"next": "다음",
"welcomeTours": "버전 별 릴리즈 정보"
},
"diagnostics": {
"title": "시스템 정보"
},
"contextMenu": {
"insert": "삽입",
"node": "노드",
"junction": "접합",
"linkNodes": "링크 노드"
}
}

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'."
}
}

View File

@@ -1133,8 +1133,10 @@
"languages" : {
"de": "Немецкий",
"en-US": "Английский",
"fr": "Французский",
"ja": "Японский",
"ko": "Корейский",
"pt-BR":"португальский",
"ru": "Русский",
"zh-CN": "Китайский (упрощенный)",
"zh-TW": "Китайский (традиционный)"

File diff suppressed because it is too large Load Diff

View File

@@ -1088,8 +1088,11 @@
"languages": {
"de": "德語",
"en-US": "英語",
"fr": "法語",
"ja": "日語",
"ko": "韓語",
"pt-BR":"葡萄牙语",
"ru":"俄語",
"zh-CN": "簡體中文",
"zh-TW": "繁體中文"
}

View File

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

View File

@@ -14,7 +14,7 @@
* limitations under the License.
**/
/**
/**
* An API for undo / redo history buffer
* @namespace RED.history
*/
@@ -378,7 +378,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 +422,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 +438,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 +515,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 +650,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

@@ -19,7 +19,6 @@
* @namespace RED.nodes
*/
RED.nodes = (function() {
var PORT_TYPE_INPUT = 1;
var PORT_TYPE_OUTPUT = 0;
@@ -47,6 +46,9 @@ RED.nodes = (function() {
function setDirty(d) {
dirty = d;
if (!d) {
allNodes.clearState()
}
RED.events.emit("workspace:dirty",{dirty:dirty});
}
@@ -63,12 +65,12 @@ RED.nodes = (function() {
defaults: {
label: {value:""},
disabled: {value: false},
locked: {value: false},
info: {value: ""},
env: {value: []}
}
};
var exports = {
setModulePendingUpdated: function(module,version) {
moduleList[module].pending_version = version;
@@ -238,22 +240,72 @@ RED.nodes = (function() {
// allNodes holds information about the Flow nodes.
var allNodes = (function() {
// Map node.id -> node
var nodes = {};
// Map tab.id -> Array of nodes on that tab
var tabMap = {};
// Map tab.id -> Set of dirty object ids on that tab
var tabDirtyMap = {};
// Map tab.id -> Set of object ids of things deleted from the tab that weren't otherwise dirty
var tabDeletedNodesMap = {};
// Set of object ids of things added to a tab after initial import
var addedDirtyObjects = new Set()
function changeCollectionDepth(tabNodes, toMove, direction, singleStep) {
const result = []
const moved = new Set();
const startIndex = direction ? tabNodes.length - 1 : 0
const endIndex = direction ? -1 : tabNodes.length
const step = direction ? -1 : 1
let target = startIndex // Only used for all-the-way moves
for (let i = startIndex; i != endIndex; i += step) {
if (toMove.size === 0) {
break;
}
const n = tabNodes[i]
if (toMove.has(n)) {
if (singleStep) {
if (i !== startIndex && !moved.has(tabNodes[i - step])) {
tabNodes.splice(i, 1)
tabNodes.splice(i - step, 0, n)
n._reordered = true
result.push(n)
}
} else {
if (i !== target) {
tabNodes.splice(i, 1)
tabNodes.splice(target, 0, n)
n._reordered = true
result.push(n)
}
target += step
}
toMove.delete(n);
moved.add(n);
}
}
return result
}
var api = {
addTab: function(id) {
tabMap[id] = [];
tabDirtyMap[id] = new Set();
tabDeletedNodesMap[id] = new Set();
},
hasTab: function(z) {
return tabMap.hasOwnProperty(z)
},
removeTab: function(id) {
delete tabMap[id];
delete tabDirtyMap[id];
delete tabDeletedNodesMap[id];
},
addNode: function(n) {
nodes[n.id] = n;
if (tabMap.hasOwnProperty(n.z)) {
tabMap[n.z].push(n);
api.addObjectToWorkspace(n.z, n.id, n.changed || n.moved)
} else {
console.warn("Node added to unknown tab/subflow:",n);
tabMap["_"] = tabMap["_"] || [];
@@ -267,8 +319,37 @@ RED.nodes = (function() {
if (i > -1) {
tabMap[n.z].splice(i,1);
}
api.removeObjectFromWorkspace(n.z, n.id)
}
},
/**
* Add an object to our dirty/clean tracking state
* @param {String} z
* @param {String} id
* @param {Boolean} isDirty
*/
addObjectToWorkspace: function (z, id, isDirty) {
if (isDirty) {
addedDirtyObjects.add(id)
}
if (tabDeletedNodesMap[z].has(id)) {
tabDeletedNodesMap[z].delete(id)
}
api.markNodeDirty(z, id, isDirty)
},
/**
* Remove an object from our dirty/clean tracking state
* @param {String} z
* @param {String} id
*/
removeObjectFromWorkspace: function (z, id) {
if (!addedDirtyObjects.has(id)) {
tabDeletedNodesMap[z].add(id)
} else {
addedDirtyObjects.delete(id)
}
api.markNodeDirty(z, id, false)
},
hasNode: function(id) {
return nodes.hasOwnProperty(id);
},
@@ -280,152 +361,54 @@ RED.nodes = (function() {
n.z = newZ;
api.addNode(n)
},
moveNodesForwards: function(nodes) {
var result = [];
/**
* @param {array} nodes
* @param {boolean} direction true:forwards false:back
* @param {boolean} singleStep true:single-step false:all-the-way
*/
changeDepth: function(nodes, direction, singleStep) {
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var moved = new Set();
for (var i = tabNodes.length-1; i >= 0; i--) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i < tabNodes.length-1 && !moved.has(tabNodes[i+1])) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position higher
tabNodes.splice(i+1,0,n);
n._reordered = true;
result.push(n);
}
toMove.delete(n);
moved.add(n);
let result = []
const tabNodes = tabMap[nodes[0].z];
const toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
if (toMove.size > 0) {
result = result.concat(changeCollectionDepth(tabNodes, toMove, direction, singleStep))
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
const groupNodes = groupsByZ[nodes[0].z] || []
const groupsToMove = new Set(nodes.filter(function(n) { return n.type === 'group'}))
if (groupsToMove.size > 0) {
const groupResult = changeCollectionDepth(groupNodes, groupsToMove, direction, singleStep)
if (groupResult.length > 0) {
result = result.concat(groupResult)
RED.events.emit('groups:reorder',{
z: nodes[0].z,
nodes: groupResult
});
}
}
return result;
RED.view.redraw(true)
return result
},
moveNodesForwards: function(nodes) {
return api.changeDepth(nodes, true, true)
},
moveNodesBackwards: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var moved = new Set();
for (var i = 0; i < tabNodes.length; i++) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i > 0 && !moved.has(tabNodes[i-1])) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position lower
tabNodes.splice(i-1,0,n);
n._reordered = true;
result.push(n);
}
toMove.delete(n);
moved.add(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
return api.changeDepth(nodes, false, true)
},
moveNodesToFront: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var target = tabNodes.length-1;
for (var i = tabNodes.length-1; i >= 0; i--) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i < target) {
// Remove from current position
tabNodes.splice(i,1);
tabNodes.splice(target,0,n);
n._reordered = true;
result.push(n);
}
target--;
toMove.delete(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
return api.changeDepth(nodes, true, false)
},
moveNodesToBack: function(nodes) {
var result = [];
if (!Array.isArray(nodes)) {
nodes = [nodes]
}
// Can only do this for nodes on the same tab.
// Use nodes[0] to get the z
var tabNodes = tabMap[nodes[0].z];
var toMove = new Set(nodes.filter(function(n) { return n.type !== "group" && n.type !== "subflow" }));
var target = 0;
for (var i = 0; i < tabNodes.length; i++) {
if (toMove.size === 0) {
break;
}
var n = tabNodes[i];
if (toMove.has(n)) {
// This is a node to move.
if (i > target) {
// Remove from current position
tabNodes.splice(i,1);
// Add it back one position lower
tabNodes.splice(target,0,n);
n._reordered = true;
result.push(n);
}
target++;
toMove.delete(n);
}
}
if (result.length > 0) {
RED.events.emit('nodes:reorder',{
z: nodes[0].z,
nodes: result
});
}
return result;
return api.changeDepth(nodes, false, false)
},
getNodes: function(z) {
return tabMap[z];
@@ -433,6 +416,33 @@ RED.nodes = (function() {
clear: function() {
nodes = {};
tabMap = {};
tabDirtyMap = {};
tabDeletedNodesMap = {};
addedDirtyObjects = new Set();
},
/**
* Clear all internal state on what is dirty.
*/
clearState: function () {
// Called when a deploy happens, we can forget about added/remove
// items as they have now been deployed.
addedDirtyObjects = new Set()
const flowsToCheck = new Set()
for (const [z, set] of Object.entries(tabDeletedNodesMap)) {
if (set.size > 0) {
set.clear()
flowsToCheck.add(z)
}
}
for (const [z, set] of Object.entries(tabDirtyMap)) {
if (set.size > 0) {
set.clear()
flowsToCheck.add(z)
}
}
for (const z of flowsToCheck) {
api.checkTabState(z)
}
},
eachNode: function(cb) {
var nodeList,i,j;
@@ -498,7 +508,7 @@ RED.nodes = (function() {
return result;
},
getNodeOrder: function(z) {
return tabMap[z].map(function(n) { return n.id })
return (groupsByZ[z] || []).concat(tabMap[z]).map(n => n.id)
},
setNodeOrder: function(z, order) {
var orderMap = {};
@@ -510,6 +520,41 @@ RED.nodes = (function() {
B._reordered = true;
return orderMap[A.id] - orderMap[B.id];
})
if (groupsByZ[z]) {
groupsByZ[z].sort(function(A,B) {
return orderMap[A.id] - orderMap[B.id];
})
}
},
/**
* Update our records if an object is dirty or not
* @param {String} z tab id
* @param {String} id object id
* @param {Boolean} dirty whether the object is dirty or not
*/
markNodeDirty: function(z, id, dirty) {
if (tabDirtyMap[z]) {
if (dirty) {
tabDirtyMap[z].add(id)
} else {
tabDirtyMap[z].delete(id)
}
api.checkTabState(z)
}
},
/**
* Check if a tab should update its contentsChange flag
* @param {String} z tab id
*/
checkTabState: function (z) {
const ws = workspaces[z]
if (ws) {
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
if (Boolean(ws.contentsChanged) !== contentsChanged) {
ws.contentsChanged = contentsChanged
RED.events.emit("flows:change", ws);
}
}
}
}
return api;
@@ -575,15 +620,53 @@ RED.nodes = (function() {
}
}
const nodeProxyHandler = {
get(node, prop) {
if (prop === '__isProxy__') {
return true
} else if (prop == '__node__') {
return node
}
return node[prop]
},
set(node, prop, value) {
if (node.z && (RED.nodes.workspace(node.z)?.locked || RED.nodes.subflow(node.z)?.locked)) {
if (
node._def.defaults[prop] ||
prop === 'z' ||
prop === 'l' ||
prop === 'd' ||
(prop === 'changed' && (!!node.changed) !== (!!value)) || // jshint ignore:line
((prop === 'x' || prop === 'y') && !node.resize && node.type !== 'group')
) {
throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`)
}
}
if (node.z && (prop === 'changed' || prop === 'moved')) {
setTimeout(() => {
allNodes.markNodeDirty(node.z, node.id, node.changed || node.moved)
}, 0)
}
node[prop] = value;
return true
}
}
function addNode(n) {
let newNode
if (!n.__isProxy__) {
newNode = new Proxy(n, nodeProxyHandler)
} else {
newNode = n
}
if (n.type.indexOf("subflow") !== 0) {
n["_"] = n._def._;
} else {
var subflowId = n.type.substring(8);
var sf = RED.nodes.subflow(subflowId);
if (sf) {
sf.instances.push(sf);
sf.instances.push(newNode);
}
n["_"] = RED._;
}
@@ -600,12 +683,13 @@ RED.nodes = (function() {
});
n.i = nextId+1;
}
allNodes.addNode(n);
allNodes.addNode(newNode);
if (!nodeLinks[n.id]) {
nodeLinks[n.id] = {in:[],out:[]};
}
}
RED.events.emit('nodes:add',n);
RED.events.emit('nodes:add',newNode);
return newNode
}
function addLink(l) {
if (nodeLinks[l.source.id]) {
@@ -632,10 +716,16 @@ RED.nodes = (function() {
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
linkTabMap[l.source.z].push(l);
allNodes.addObjectToWorkspace(l.source.z, getLinkId(l), true)
}
RED.events.emit("links:add",l);
}
function getLinkId(link) {
return link.source.id + ':' + link.sourcePort + ':' + link.target.id
}
function getNode(id) {
if (id in configNodes) {
return configNodes[id];
@@ -830,6 +920,7 @@ RED.nodes = (function() {
if (index !== -1) {
linkTabMap[l.source.z].splice(index,1)
}
allNodes.removeObjectFromWorkspace(l.source.z, getLinkId(l))
}
}
RED.events.emit("links:remove",l);
@@ -999,6 +1090,11 @@ RED.nodes = (function() {
return false;
}
function getDownstreamNodes(node) {
const downstreamLinks = nodeLinks[node.id].out
const downstreamNodes = new Set(downstreamLinks.map(l => l.target))
return Array.from(downstreamNodes)
}
function getAllDownstreamNodes(node) {
return getAllFlowNodes(node,'down').filter(function(n) { return n !== node });
}
@@ -1046,6 +1142,9 @@ RED.nodes = (function() {
node.type = n.type;
for (var d in n._def.defaults) {
if (n._def.defaults.hasOwnProperty(d)) {
if (d === 'locked' && !n.locked) {
continue
}
node[d] = n[d];
}
}
@@ -1369,7 +1468,7 @@ RED.nodes = (function() {
}
}
if (node.type !== "subflow") {
var convertedNode = RED.nodes.convertNode(node);
var convertedNode = RED.nodes.convertNode(node, { credentials: false });
for (var d in node._def.defaults) {
if (node._def.defaults[d].type) {
var nodeList = node[d];
@@ -1402,7 +1501,7 @@ RED.nodes = (function() {
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
}
} else {
var convertedSubflow = convertSubflow(node);
var convertedSubflow = convertSubflow(node, { credentials: false });
nns.push(convertedSubflow);
}
}
@@ -1651,6 +1750,7 @@ RED.nodes = (function() {
* Options:
* - generateIds - whether to replace all node ids
* - addFlow - whether to import nodes to a new tab
* - markChanged - whether to set changed=true on all newly imported objects
* - reimport - if node has a .z property, dont overwrite it
* Only applicible when `generateIds` is false
* - importMap - how to resolve any conflicts.
@@ -1659,7 +1759,7 @@ RED.nodes = (function() {
* - id:replace - import over the top of existing
*/
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} }
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
options = Object.assign({}, defOpts, options)
options.importMap = options.importMap || {}
const createNewIds = options.generateIds;
@@ -1685,7 +1785,7 @@ RED.nodes = (function() {
newNodes = newNodesObj;
}
if (!$.isArray(newNodes)) {
if (!Array.isArray(newNodes)) {
newNodes = [newNodes];
}
@@ -1983,6 +2083,9 @@ RED.nodes = (function() {
if (!n.z) {
delete configNode.z;
}
if (options.markChanged) {
configNode.changed = true
}
if (n.hasOwnProperty('d')) {
configNode.d = n.d;
}
@@ -2045,6 +2148,9 @@ RED.nodes = (function() {
if (n.hasOwnProperty('g')) {
node.g = n.g;
}
if (options.markChanged) {
node.changed = true
}
if (createNewIds || options.importMap[n.id] === "copy") {
if (subflow_denylist[n.z]) {
continue;
@@ -2095,16 +2201,27 @@ RED.nodes = (function() {
} else if (n.type.substring(0,7) === "subflow") {
var parentId = n.type.split(":")[1];
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
if (createNewIds || options.importMap[n.id] === "copy") {
parentId = subflow.id;
node.type = "subflow:"+parentId;
node._def = registry.getNodeType(node.type);
delete node.i;
if (!subflow){
node._def = {
color:"#fee",
defaults: {},
label: "unknown: "+n.type,
labelStyle: "red-ui-flow-node-label-italic",
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
set: registry.getNodeSet("node-red/unknown")
}
} else {
if (createNewIds || options.importMap[n.id] === "copy") {
parentId = subflow.id;
node.type = "subflow:"+parentId;
node._def = registry.getNodeType(node.type);
delete node.i;
}
node.name = n.name;
node.outputs = subflow.out.length;
node.inputs = subflow.in.length;
node.env = n.env;
}
node.name = n.name;
node.outputs = subflow.out.length;
node.inputs = subflow.in.length;
node.env = n.env;
} else if (n.type === 'junction') {
node._def = {defaults:{}}
node._config.x = node.x
@@ -2265,7 +2382,7 @@ RED.nodes = (function() {
// get added
if (activeSubflow && /^link /.test(n.type) && n.links) {
n.links = n.links.filter(function(id) {
var otherNode = RED.nodes.node(id);
const otherNode = node_map[id] || RED.nodes.node(id);
return (otherNode && otherNode.z === activeWorkspace)
});
}
@@ -2315,19 +2432,6 @@ RED.nodes = (function() {
if (n.g && !new_group_set.has(n.g)) {
delete n.g;
}
n.nodes = n.nodes.map(function(id) {
return node_map[id];
})
// Just in case the group references a node that doesn't exist for some reason
n.nodes = n.nodes.filter(function(v) {
if (v) {
// Repair any nodes that have forgotten they are in this group
if (v.g !== n.id) {
v.g = n.id;
}
}
return !!v
});
if (!n.g) {
groupDepthMap[n.id] = 0;
}
@@ -2350,21 +2454,22 @@ RED.nodes = (function() {
return groupDepthMap[A.id] - groupDepthMap[B.id];
});
for (i=0;i<new_groups.length;i++) {
n = new_groups[i];
addGroup(n);
new_groups[i] = addGroup(new_groups[i]);
node_map[new_groups[i].id] = new_groups[i]
}
for (i=0;i<new_junctions.length;i++) {
var junction = new_junctions[i];
addJunction(junction);
new_junctions[i] = addJunction(new_junctions[i]);
node_map[new_junctions[i].id] = new_junctions[i]
}
// Now the nodes have been fully updated, add them.
for (i=0;i<new_nodes.length;i++) {
var node = new_nodes[i];
addNode(node);
new_nodes[i] = addNode(new_nodes[i])
node_map[new_nodes[i].id] = new_nodes[i]
}
// Finally validate them all.
// This has to be done after everything is added so that any checks for
// dependent config nodes will pass
@@ -2372,6 +2477,39 @@ RED.nodes = (function() {
var node = new_nodes[i];
RED.editor.validateNode(node);
}
const lookupNode = (id) => {
const mappedNode = node_map[id]
if (!mappedNode) {
return null
}
if (mappedNode.__isProxy__) {
return mappedNode
} else {
return node_map[mappedNode.id]
}
}
// Update groups to reference proxy node objects
for (i=0;i<new_groups.length;i++) {
n = new_groups[i];
// bypass the proxy in case the flow is locked
n.__node__.nodes = n.nodes.map(lookupNode)
// Just in case the group references a node that doesn't exist for some reason
n.__node__.nodes = n.nodes.filter(function(v) {
if (v) {
// Repair any nodes that have forgotten they are in this group
if (v.g !== n.id) {
v.g = n.id;
}
}
return !!v
});
}
// Update links to use proxy node objects
for (i=0;i<new_links.length;i++) {
new_links[i].source = lookupNode(new_links[i].source.id) || new_links[i].source
new_links[i].target = lookupNode(new_links[i].target.id) || new_links[i].target
}
RED.workspaces.refresh();
@@ -2500,11 +2638,17 @@ RED.nodes = (function() {
junctions = {};
junctionsByZ = {};
var workspaceIds = Object.keys(workspaces);
// Ensure all workspaces are unlocked so we don't get any edit-protection
// preventing removal
workspaceIds.forEach(function(id) {
workspaces[id].locked = false
});
var subflowIds = Object.keys(subflows);
subflowIds.forEach(function(id) {
RED.subflow.removeSubflow(id)
});
var workspaceIds = Object.keys(workspaces);
workspaceIds.forEach(function(id) {
RED.workspaces.remove(workspaces[id]);
});
@@ -2525,10 +2669,15 @@ RED.nodes = (function() {
}
function addGroup(group) {
if (!group.__isProxy__) {
group = new Proxy(group, nodeProxyHandler)
}
groupsByZ[group.z] = groupsByZ[group.z] || [];
groupsByZ[group.z].push(group);
groups[group.id] = group;
allNodes.addObjectToWorkspace(group.z, group.id, group.changed || group.moved)
RED.events.emit("groups:add",group);
return group
}
function removeGroup(group) {
var i = groupsByZ[group.z].indexOf(group);
@@ -2543,19 +2692,28 @@ RED.nodes = (function() {
}
}
RED.group.markDirty(group);
allNodes.removeObjectFromWorkspace(group.z, group.id)
delete groups[group.id];
RED.events.emit("groups:remove",group);
}
function getGroupOrder(z) {
const groups = groupsByZ[z]
return groups.map(g => g.id)
}
function addJunction(junction) {
if (!junction.__isProxy__) {
junction = new Proxy(junction, nodeProxyHandler)
}
junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
junctionsByZ[junction.z].push(junction)
junctions[junction.id] = junction;
if (!nodeLinks[junction.id]) {
nodeLinks[junction.id] = {in:[],out:[]};
}
allNodes.addObjectToWorkspace(junction.z, junction.id, junction.changed || junction.moved)
RED.events.emit("junctions:add", junction)
return junction
}
function removeJunction(junction) {
var i = junctionsByZ[junction.z].indexOf(junction)
@@ -2565,6 +2723,7 @@ RED.nodes = (function() {
}
delete junctions[junction.id]
delete nodeLinks[junction.id];
allNodes.removeObjectFromWorkspace(junction.z, junction.id)
RED.events.emit("junctions:remove", junction)
var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
@@ -2802,6 +2961,9 @@ RED.nodes = (function() {
RED.view.redraw(true);
}
});
RED.events.on('deploy', function () {
allNodes.clearState()
})
},
registry:registry,
setNodeList: registry.setNodeList,
@@ -2850,7 +3012,7 @@ RED.nodes = (function() {
},
addWorkspace: addWorkspace,
removeWorkspace: removeWorkspace,
getWorkspaceOrder: function() { return workspacesOrder },
getWorkspaceOrder: function() { return [...workspacesOrder] },
setWorkspaceOrder: function(order) { workspacesOrder = order; },
workspace: getWorkspace,
@@ -2904,6 +3066,20 @@ RED.nodes = (function() {
}
}
},
eachGroup: function(cb) {
for (var group of Object.values(groups)) {
if (cb(group) === false) {
break
}
}
},
eachJunction: function(cb) {
for (var junction of Object.values(junctions)) {
if (cb(junction) === false) {
break
}
}
},
node: getNode,
@@ -2926,6 +3102,7 @@ RED.nodes = (function() {
getAllFlowNodes: getAllFlowNodes,
getAllUpstreamNodes: getAllUpstreamNodes,
getAllDownstreamNodes: getAllDownstreamNodes,
getDownstreamNodes: getDownstreamNodes,
getNodeIslands: getNodeIslands,
createExportableNodeSet: createExportableNodeSet,
createCompleteNodeSet: createCompleteNodeSet,

View File

@@ -249,8 +249,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")||"{}");
@@ -643,11 +672,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"},
@@ -657,7 +681,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);
@@ -750,6 +779,7 @@ var RED = (function() {
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.init(buildMainMenu);
RED.envVar.init();
RED.nodes.init();
RED.runtime.init()

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

View File

@@ -37,13 +37,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);
@@ -423,11 +423,10 @@ RED.clipboard = (function() {
}
}
function showImportNodes(mode) {
function showImportNodes(library = 'clipboard') {
if (disabled) {
return;
}
mode = mode || "clipboard";
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
@@ -504,7 +503,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 +532,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,13 +557,16 @@ 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));
@@ -654,7 +656,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 +677,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 +687,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 +731,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 +775,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();
@@ -1266,15 +1278,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 +1296,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

@@ -417,6 +417,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

@@ -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);
}
})
@@ -807,23 +829,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 +959,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

@@ -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,172 @@ RED.contextMenu = (function () {
if (menu) {
menu.remove()
}
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
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
const offset = $("#red-ui-workspace-chart").offset()
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')
}
},
(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
}
]
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)
}
menuItems.push(
{ onselect: 'core:show-action-list', 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: true,
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,
{ 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' },
null
)
}
nodeOptions.push(
{ onselect: 'core:enable-selected-nodes' },
{ onselect: 'core:disable-selected-nodes' },
null,
{ onselect: 'core:show-selected-node-labels' },
{ onselect: 'core:hide-selected-node-labels' }
)
menuItems.push({
label: RED._('sidebar.info.node'),
options: nodeOptions
})
menuItems.push({
label: RED._('sidebar.info.group'),
options: [
{ onselect: 'core:group-selection' },
{ onselect: 'core:ungroup-selection', 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', disabled: !hasGroup },
{ onselect: 'core:paste-group-style', 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', 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: !canEdit || !hasSelection },
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
{ onselect: 'core:select-all-nodes' },
)
if (canRemoveFromGroup) {
menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
}
}
var direction = "right";

View File

@@ -557,7 +557,17 @@ RED.deploy = (function() {
} else {
RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
}
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)
}
}
RED.nodes.eachNode(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
@@ -570,7 +580,32 @@ 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;
@@ -580,8 +615,16 @@ RED.deploy = (function() {
subflow.changed = false;
});
RED.nodes.eachWorkspace(function (ws) {
ws.changed = false;
if (ws.changed || ws.added) {
ensureUnlocked(ws.z)
ws.changed = false;
delete ws.added
RED.events.emit("flows:change", ws)
}
});
flowsToLock.forEach(flow => {
flow.locked = true
})
// Once deployed, cannot undo back to a clean state
RED.history.markAllDirty();
RED.view.redraw();

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;
@@ -718,7 +720,10 @@ 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;
}
}
}
}
@@ -860,6 +865,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;
@@ -1045,6 +1051,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",
@@ -1148,6 +1161,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) {
@@ -1190,6 +1205,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",
@@ -1694,6 +1716,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);
@@ -1853,11 +1876,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",
@@ -1898,6 +1925,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',
@@ -1912,6 +1940,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();

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 = {};
@@ -1160,19 +1168,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

@@ -2,7 +2,7 @@ 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'];
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
/**
* Create env var edit interface

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);
mermaid.init();
},200);
})
if (options.header) {
@@ -122,6 +178,7 @@
if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
mermaid.init();
}
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

@@ -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,189 @@
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") && (item.value !== "__PWRD__")) {
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) {
if (!RED.user.hasPermission("settings.write")) {
RED.notify(RED._("user.errors.settings"),"error");
return;
}
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";

View File

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

View File

@@ -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 = {};
@@ -162,7 +164,6 @@ RED.palette.editor = (function() {
}
}
function getContrastingBorder(rgbColor){
var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor);
if (parts) {
@@ -369,10 +370,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 +390,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 +406,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 +418,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,23 +434,92 @@ 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.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();
@@ -462,7 +534,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 +563,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;
}
@@ -669,7 +741,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) {
@@ -800,21 +873,20 @@ 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({
@@ -831,19 +903,25 @@ 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(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 +939,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 +949,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 +985,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 +1034,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"]');

View File

@@ -171,23 +171,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 +284,7 @@ RED.palette = (function() {
var hoverGroup;
var paletteWidth;
var paletteTop;
var dropEnabled;
$(d).draggable({
helper: 'clone',
appendTo: '#red-ui-editor',
@@ -299,6 +292,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 +303,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);
}
}
}
}

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 () {
mermaid.init();
}, 200);
}
function editSummary(activeProject, summary, container, version, versionContainer) {

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

@@ -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);
@@ -1330,7 +1349,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,19 @@ 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);
@@ -216,7 +225,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 +283,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 {

View File

@@ -141,7 +141,8 @@ RED.sidebar.help = (function() {
RED.events.on('registry:node-type-removed', queueRefresh);
RED.events.on('subflows:change', refreshSubflow);
RED.actions.add("core:show-help-tab",show);
RED.actions.add("core:show-help-tab", show);
RED.actions.add("core:show-node-help", showNodeHelp)
}
@@ -338,6 +339,19 @@ RED.sidebar.help = (function() {
resizeStack();
}
function showNodeHelp(node) {
if (!node) {
const selection = RED.view.selection()
if (selection.nodes && selection.nodes.length > 0) {
node = selection.nodes.find(n => n.type !== 'group' && n.type !== 'junction')
}
}
if (node) {
show(node.type, true)
}
}
// TODO: DRY - projects.js
function addTargetToExternalLinks(el) {
$(el).find("a").each(function(el) {

View File

@@ -225,6 +225,22 @@ RED.sidebar.info.outliner = (function() {
} else {
$('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
}
if (n.type === 'tab') {
var lockToggleButton = $('<button type="button" class="red-ui-info-outline-item-control-lock red-ui-button red-ui-button-small"><i class="fa fa-unlock-alt"></i><i class="fa fa-lock"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (n.locked) {
RED.workspaces.unlock(n.id)
} else {
RED.workspaces.lock(n.id)
}
})
RED.popover.tooltip(lockToggleButton,function() {
return RED._("common.label."+(n.locked?"unlock":"lock"));
});
} else {
$('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
}
controls.find("button").on("dblclick", function(evt) {
evt.preventDefault();
evt.stopPropagation();
@@ -368,6 +384,8 @@ RED.sidebar.info.outliner = (function() {
flowList.treeList.addChild(objects[ws.id])
objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
objects[ws.id].element.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
updateSearch();
}
@@ -382,6 +400,8 @@ RED.sidebar.info.outliner = (function() {
existingObject.element.find(".red-ui-info-outline-item-label").text(label);
existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
existingObject.element.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
existingObject.treeList.container.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
updateSearch();
}
function onFlowsReorder(order) {

View File

@@ -25,6 +25,7 @@ RED.sidebar.info = (function() {
var propertiesPanelHeaderLabel;
var propertiesPanelHeaderReveal;
var propertiesPanelHeaderHelp;
var propertiesPanelHeaderCopyLink;
var selectedObject;
@@ -67,10 +68,20 @@ RED.sidebar.info = (function() {
propertiesPanelHeaderIcon = $("<span>").appendTo(propertiesPanelHeader);
propertiesPanelHeaderLabel = $("<span>").appendTo(propertiesPanelHeader);
propertiesPanelHeaderHelp = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
propertiesPanelHeaderCopyLink = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-link"></button>').css({
position: 'absolute',
top: '12px',
right: '32px'
}).on("click", function(evt) {
RED.actions.invoke('core:copy-item-url',selectedObject)
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderCopyLink,RED._("sidebar.info.copyItemUrl"));
propertiesPanelHeaderHelp = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
position: 'absolute',
top: '12px',
right: '56px'
}).on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
@@ -80,8 +91,7 @@ RED.sidebar.info = (function() {
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp"));
propertiesPanelHeaderReveal = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
propertiesPanelHeaderReveal = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
position: 'absolute',
top: '12px',
right: '8px'
@@ -185,6 +195,7 @@ RED.sidebar.info = (function() {
propertiesPanelHeaderLabel.text("");
propertiesPanelHeaderReveal.hide();
propertiesPanelHeaderHelp.hide();
propertiesPanelHeaderCopyLink.hide();
return;
} else if (Array.isArray(node)) {
// Multiple things selected
@@ -196,6 +207,7 @@ RED.sidebar.info = (function() {
propertiesPanelHeaderLabel.text("Selection");
propertiesPanelHeaderReveal.hide();
propertiesPanelHeaderHelp.hide();
propertiesPanelHeaderCopyLink.hide();
selectedObject = null;
var types = {
@@ -277,9 +289,11 @@ RED.sidebar.info = (function() {
if (node.type === "tab" || node.type === "subflow") {
// If nothing is selected, but we're on a flow or subflow tab.
propertiesPanelHeaderHelp.hide();
propertiesPanelHeaderCopyLink.show();
} else if (node.type === "group") {
propertiesPanelHeaderHelp.hide();
propertiesPanelHeaderCopyLink.show();
propRow = $('<tr class="red-ui-help-info-row"><td>&nbsp;</td><td></td></tr>').appendTo(tableBody);
@@ -304,8 +318,10 @@ RED.sidebar.info = (function() {
}
} else if (node.type === 'junction') {
propertiesPanelHeaderHelp.hide();
propertiesPanelHeaderCopyLink.hide();
} else {
propertiesPanelHeaderHelp.show();
propertiesPanelHeaderCopyLink.show();
if (!subflowRegex) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+'</td><td></td></tr>').appendTo(tableBody);
@@ -447,7 +463,8 @@ RED.sidebar.info = (function() {
el = el.next();
}
$(this).toggleClass('expanded',!isExpanded);
})
});
mermaid.init();
}
var tips = (function() {

View File

@@ -435,10 +435,15 @@ RED.tourGuide = (function() {
function listTour() {
return [
{
id: "3_1",
label: "3.1",
path: "./tours/welcome.js"
},
{
id: "3_0",
label: "3.0",
path: "./tours/welcome.js"
path: "./tours/3.0/welcome.js"
},
{
id: "2_2",

View File

@@ -96,6 +96,37 @@ RED.utils = (function() {
}
}
var mermaidIsInitialized = false;
var mermaidIsEnabled /* = undefined */;
renderer.code = function (code, lang) {
if(lang === "mermaid") {
// mermaid diagram rendering
if (mermaidIsEnabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
mermaidIsEnabled = true;
}
}
if (mermaidIsEnabled) {
if (!mermaidIsInitialized) {
mermaidIsInitialized = true;
mermaid.initialize({startOnLoad:false});
}
return `<pre class='mermaid'>${code}</pre>`;
}
else {
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
}
}
else {
return "<pre><code>" +code +"</code></pre>";
}
};
window._marked.setOptions({
renderer: renderer,
gfm: true,

View File

@@ -17,9 +17,9 @@
RED.view.navigator = (function() {
var nav_scale = 25;
var nav_width = 5000/nav_scale;
var nav_height = 5000/nav_scale;
var nav_scale = 50;
var nav_width = 8000/nav_scale;
var nav_height = 8000/nav_scale;
var navContainer;
var navBox;

View File

@@ -15,7 +15,7 @@
**/
RED.view.tools = (function() {
'use strict';
function selectConnected(type) {
var selection = RED.view.selection();
var visited = new Set();
@@ -39,6 +39,9 @@ RED.view.tools = (function() {
}
function alignToGrid() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
var changedNodes = [];
@@ -87,6 +90,9 @@ RED.view.tools = (function() {
}
function moveSelection(dx,dy) {
if (RED.workspaces.isLocked()) {
return
}
if (moving_set === null) {
moving_set = [];
var selection = RED.view.selection();
@@ -153,6 +159,9 @@ RED.view.tools = (function() {
}
function setSelectedNodeLabelState(labelShown) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
var historyEvents = [];
var nodes = [];
@@ -439,6 +448,9 @@ RED.view.tools = (function() {
}
function alignSelectionToEdge(direction) {
if (RED.workspaces.isLocked()) {
return;
}
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length > 1) {
@@ -539,8 +551,10 @@ RED.view.tools = (function() {
}
}
function distributeSelection(direction) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length > 2) {
@@ -699,14 +713,16 @@ RED.view.tools = (function() {
}
function reorderSelection(dir) {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
var nodesToMove = [];
selection.nodes.forEach(function(n) {
if (n.type === "group") {
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true).filter(function(n) {
return n.type !== "group";
}))
nodesToMove.push(n)
nodesToMove = nodesToMove.concat(RED.group.getNodes(n, true))
} else if (n.type !== "subflow"){
nodesToMove.push(n);
}
@@ -734,8 +750,10 @@ RED.view.tools = (function() {
}
}
function wireSeriesOfNodes() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
@@ -776,6 +794,9 @@ RED.view.tools = (function() {
}
function wireNodeToMultiple() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
@@ -818,11 +839,72 @@ RED.view.tools = (function() {
}
}
function wireMultipleToNode() {
if (RED.workspaces.isLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
var targetNode = selection.nodes[selection.nodes.length - 1];
if (targetNode.inputs === 0) {
return;
}
var i = 0;
var newLinks = [];
for (i = 0; i < selection.nodes.length - 1; i++) {
var sourceNode = selection.nodes[i];
if (sourceNode.outputs > 0) {
// Wire the first output to the target that has no link to the target yet.
// This allows for connecting all combinations of inputs/outputs.
// The user may then delete links quickly that aren't needed.
var sourceConnectedOutports = RED.nodes.filterLinks({
source: sourceNode,
target: targetNode
});
// Get outport indices that have no link yet
var sourceOutportIndices = Array.from({ length: sourceNode.outputs }, (_, i) => i);
var sourceConnectedOutportIndices = sourceConnectedOutports.map( x => x.sourcePort );
var sourceFreeOutportIndices = sourceOutportIndices.filter(x => !sourceConnectedOutportIndices.includes(x));
// Does an unconnected source port exist?
if (sourceFreeOutportIndices.length == 0) {
continue;
}
// Connect the first free outport to the target
var newLink = {
source: sourceNode,
target: targetNode,
sourcePort: sourceFreeOutportIndices[0]
}
RED.nodes.addLink(newLink);
newLinks.push(newLink);
}
}
if (newLinks.length > 0) {
RED.history.push({
t: 'add',
links: newLinks,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
/**
* Splits selected wires and re-joins them with link-out+link-in
* @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
*/
function splitWiresWithLinkNodes(wires) {
if (RED.workspaces.isLocked()) {
return
}
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
if (!wiresToSplit) {
return
@@ -877,7 +959,6 @@ RED.view.tools = (function() {
if(!nnLinkOut) {
const nLinkOut = RED.view.createNode("link out"); //create link node
nnLinkOut = nLinkOut.node;
nodeSrcMap[linkOutMapId] = nnLinkOut;
let yOffset = 0;
if(nSrc.outputs > 1) {
@@ -892,7 +973,8 @@ RED.view.tools = (function() {
updateNewNodePosXY(nSrc, nnLinkOut, false, RED.view.snapGrid, yOffset);
}
//add created node
RED.nodes.add(nnLinkOut);
nnLinkOut = RED.nodes.add(nnLinkOut);
nodeSrcMap[linkOutMapId] = nnLinkOut;
RED.editor.validateNode(nnLinkOut);
history.events.push(nLinkOut.historyEvent);
//connect node to link node
@@ -913,10 +995,10 @@ RED.view.tools = (function() {
if(!nnLinkIn) {
const nLinkIn = RED.view.createNode("link in"); //create link node
nnLinkIn = nLinkIn.node;
nodeTrgMap[nTrg.id] = nnLinkIn;
updateNewNodePosXY(nTrg, nnLinkIn, true, RED.view.snapGrid, 0);
//add created node
RED.nodes.add(nnLinkIn);
nnLinkIn = RED.nodes.add(nnLinkIn);
nodeTrgMap[nTrg.id] = nnLinkIn;
RED.editor.validateNode(nnLinkIn);
history.events.push(nLinkIn.historyEvent);
//connect node to link node
@@ -991,6 +1073,9 @@ RED.view.tools = (function() {
* @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`
*/
function generateNodeNames(node, options) {
if (RED.workspaces.isLocked()) {
return
}
options = Object.assign({
renameBlank: true,
renameClash: true,
@@ -1061,6 +1146,9 @@ RED.view.tools = (function() {
}
function addJunctionsToWires(wires) {
if (RED.workspaces.isLocked()) {
return
}
let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
if (!wiresToSplit) {
return
@@ -1102,7 +1190,8 @@ RED.view.tools = (function() {
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
dirty: true,
moved: true
}
links = links.filter(function(l) { return !removedLinks.has(l) })
if (links.length === 0) {
@@ -1131,7 +1220,7 @@ RED.view.tools = (function() {
var nodeGroups = new Set()
RED.nodes.addJunction(junction)
junction = RED.nodes.addJunction(junction)
addedJunctions.push(junction)
let newLink
if (gid === links[0].source.id+":"+links[0].sourcePort) {
@@ -1192,6 +1281,63 @@ RED.view.tools = (function() {
RED.view.redraw(true);
}
function copyItemUrl(node, isEdit) {
if (!node) {
const selection = RED.view.selection();
if (selection.nodes && selection.nodes.length > 0) {
node = selection.nodes[0]
}
}
if (node) {
let thingType = 'node'
if (node.type === 'group') {
thingType = 'group'
} else if (node.type === 'tab' || node.type === 'subflow') {
thingType = 'flow'
}
let url = `${window.location.origin}${window.location.pathname}#${thingType}/${node.id}`
if (isEdit) {
url += '/edit'
}
if (RED.clipboard.copyText(url)) {
RED.notify(RED._("sidebar.info.copyURL2Clipboard"), { timeout: 2000 })
}
}
}
/**
* Determine if a point is within a node
* @param {*} node - A Node or Junction node
* @param {[Number,Number]} mouse_position The x,y position of the mouse
* @param {Number} [marginX=0] - A margin to add or deduct from the x position (to increase the hit area)
* @param {Number} [marginY=0] - A margin to add or deduct from the y position (to increase the hit area)
* @returns
*/
function isPointInNode (node, [x, y], marginX, marginY) {
marginX = marginX || 0
marginY = marginY || 0
let w = node.w || 10 // junctions dont have any w or h value
let h = node.h || 10
let x1, x2, y1, y2
if (node.type === "junction" || node.type === "group") {
// x/y is the top left of the node
x1 = node.x
y1 = node.y
x2 = node.x + w
y2 = node.y + h
} else {
// x/y is the center of the node
const [xMid, yMid] = [w/2, h/2]
x1 = node.x - xMid
y1 = node.y - yMid
x2 = node.x + xMid
y2 = node.y + yMid
}
return (x >= (x1 - marginX) && x <= (x2 + marginX) && y >= (y1 - marginY) && y <= (y2 + marginY))
}
return {
init: function() {
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -1252,12 +1398,16 @@ RED.view.tools = (function() {
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() })
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
RED.actions.add("core:generate-node-names", generateNodeNames )
RED.actions.add("core:copy-item-url", function (node) { copyItemUrl(node) })
RED.actions.add("core:copy-item-edit-url", function (node) { copyItemUrl(node, true) })
// RED.actions.add("core:add-node", function() { addNode() })
},
/**
@@ -1270,7 +1420,8 @@ RED.view.tools = (function() {
* @param {Number} dy
*/
moveSelection: moveSelection,
calculateGridSnapOffsets: calculateGridSnapOffsets
calculateGridSnapOffsets: calculateGridSnapOffsets,
isPointInNode: isPointInNode
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -58,6 +58,9 @@ RED.workspaces = (function() {
if (!ws.closeable) {
ws.hideable = true;
}
if (!ws.hasOwnProperty('locked')) {
ws.locked = false
}
workspace_tabs.addTab(ws,targetIndex);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
@@ -75,11 +78,15 @@ RED.workspaces = (function() {
type: "tab",
id: tabId,
disabled: false,
locked: false,
info: "",
label: RED._('workspace.defaultName',{number:workspaceIndex}),
env: [],
hideable: true
hideable: true,
};
if (!skipHistoryEntry) {
ws.added = true
}
RED.nodes.addWorkspace(ws,targetIndex);
workspace_tabs.addTab(ws,targetIndex);
@@ -89,8 +96,7 @@ RED.workspaces = (function() {
RED.nodes.dirty(true);
}
}
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
RED.view.focus();
return ws;
}
@@ -99,6 +105,9 @@ RED.workspaces = (function() {
if (workspaceTabCount === 1) {
return;
}
if (ws.locked) {
return
}
var workspaceOrder = RED.nodes.getWorkspaceOrder();
ws._index = workspaceOrder.indexOf(ws.id);
removeWorkspace(ws);
@@ -119,13 +128,206 @@ RED.workspaces = (function() {
RED.editor.editSubflow(subflow);
}
} else {
RED.editor.editFlow(workspace);
if (!workspace.locked) {
RED.editor.editFlow(workspace);
}
}
}
var workspace_tabs;
var workspaceTabCount = 0;
function getMenuItems(isMenuButton, tab) {
let hiddenFlows = new Set()
for (let i = 0; i < hideStack.length; i++) {
let ids = hideStack[i]
if (!Array.isArray(ids)) {
ids = [ids]
}
ids.forEach(id => {
if (RED.nodes.workspace(id)) {
hiddenFlows.add(id)
}
})
}
const hiddenflowCount = hiddenFlows.size;
let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active())
let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false
const currentTabs = workspace_tabs.listTabs();
let flowCount = 0;
currentTabs.forEach(tab => {
if (RED.nodes.workspace(tab)) {
flowCount++;
}
});
let isCurrentLocked = RED.workspaces.isLocked()
if (tab) {
isCurrentLocked = tab.locked
}
var menuItems = []
if (isMenuButton) {
menuItems.push({
id:"red-ui-tabs-menu-option-search-flows",
label: RED._("workspace.listFlows"),
onselect: "core:list-flows"
},
{
id:"red-ui-tabs-menu-option-search-subflows",
label: RED._("workspace.listSubflows"),
onselect: "core:list-subflows"
},
null)
}
menuItems.push(
{
id:"red-ui-tabs-menu-option-add-flow",
label: RED._("workspace.addFlow"),
onselect: "core:add-flow"
}
)
if (isMenuButton || !!tab) {
menuItems.push(
{
id:"red-ui-tabs-menu-option-add-flow-right",
label: RED._("workspace.addFlowToRight"),
shortcut: RED.keyboard.getShortcut("core:add-flow-to-right"),
onselect: function() {
RED.actions.invoke("core:add-flow-to-right", tab)
}
},
null
)
if (activeWorkspace && activeWorkspace.type === 'tab') {
menuItems.push(
isFlowDisabled ? {
label: RED._("workspace.enableFlow"),
shortcut: RED.keyboard.getShortcut("core:enable-flow"),
onselect: function() {
RED.actions.invoke("core:enable-flow", tab?tab.id:undefined)
},
disabled: isCurrentLocked
} : {
label: RED._("workspace.disableFlow"),
shortcut: RED.keyboard.getShortcut("core:disable-flow"),
onselect: function() {
RED.actions.invoke("core:disable-flow", tab?tab.id:undefined)
},
disabled: isCurrentLocked
},
isCurrentLocked? {
label: RED._("workspace.unlockFlow"),
shortcut: RED.keyboard.getShortcut("core:unlock-flow"),
onselect: function() {
RED.actions.invoke('core:unlock-flow', tab?tab.id:undefined)
}
} : {
label: RED._("workspace.lockFlow"),
shortcut: RED.keyboard.getShortcut("core:lock-flow"),
onselect: function() {
RED.actions.invoke('core:lock-flow', tab?tab.id:undefined)
}
},
null
)
}
const activeIndex = currentTabs.findIndex(id => (activeWorkspace && (id === activeWorkspace.id)));
menuItems.push(
{
label: RED._("workspace.moveToStart"),
shortcut: RED.keyboard.getShortcut("core:move-flow-to-start"),
onselect: function() {
RED.actions.invoke("core:move-flow-to-start", tab?tab.id:undefined)
},
disabled: activeIndex === 0
},
{
label: RED._("workspace.moveToEnd"),
shortcut: RED.keyboard.getShortcut("core:move-flow-to-end"),
onselect: function() {
RED.actions.invoke("core:move-flow-to-end", tab?tab.id:undefined)
},
disabled: activeIndex === currentTabs.length - 1
}
)
}
menuItems.push(null)
if (isMenuButton || !!tab) {
menuItems.push(
{
id:"red-ui-tabs-menu-option-add-hide-flows",
label: RED._("workspace.hideFlow"),
shortcut: RED.keyboard.getShortcut("core:hide-flow"),
onselect: function() {
RED.actions.invoke("core:hide-flow", tab)
}
},
{
id:"red-ui-tabs-menu-option-add-hide-other-flows",
label: RED._("workspace.hideOtherFlows"),
shortcut: RED.keyboard.getShortcut("core:hide-other-flows"),
onselect: function() {
RED.actions.invoke("core:hide-other-flows", tab)
}
}
)
}
menuItems.push(
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows",
disabled: (hiddenflowCount === flowCount)
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
disabled: hiddenflowCount === 0,
label: RED._("workspace.showAllFlows", { count: hiddenflowCount }),
onselect: "core:show-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-last-flow",
disabled: hideStack.length === 0,
label: RED._("workspace.showLastHiddenFlow"),
onselect: "core:show-last-hidden-flow"
}
)
if (tab) {
menuItems.push(
null,
{
label: RED._("common.label.delete"),
onselect: function() {
if (tab.type === 'tab') {
RED.workspaces.delete(tab)
} else if (tab.type === 'subflow') {
RED.subflow.delete(tab.id)
}
},
disabled: isCurrentLocked || (workspaceTabCount === 1)
},
{
label: RED._("menu.label.export"),
shortcut: RED.keyboard.getShortcut("core:show-export-dialog"),
onselect: function() {
RED.workspaces.show(tab.id)
RED.actions.invoke('core:show-export-dialog', null, 'flow')
}
}
)
}
// if (isMenuButton && hiddenflowCount > 0) {
// menuItems.unshift({
// label: RED._("workspace.hiddenFlows",{count: hiddenflowCount}),
// onselect: "core:list-hidden-flows"
// })
// }
return menuItems;
}
function createWorkspaceTabs() {
workspace_tabs = RED.tabs.create({
id: "red-ui-workspace-tabs",
@@ -137,8 +339,9 @@ RED.workspaces = (function() {
$("#red-ui-workspace-chart").show();
activeWorkspace = tab.id;
window.location.hash = 'flow/'+tab.id;
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled);
} else {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
} else {
$("#red-ui-workspace-chart").hide();
activeWorkspace = 0;
window.location.hash = '';
@@ -169,6 +372,18 @@ RED.workspaces = (function() {
if (tab.disabled) {
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-disabled');
}
$('<span class="red-ui-workspace-locked-icon"><i class="fa fa-lock"></i> </span>').prependTo("#red-ui-tab-"+(tab.id.replace(".","-"))+" .red-ui-tab-label");
if (tab.locked) {
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-locked');
}
const changeBadgeContainer = $('<svg class="red-ui-flow-tab-changed red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo("#red-ui-tab-"+(tab.id.replace(".","-")))
const changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle");
changeBadge.setAttribute("cx",5);
changeBadge.setAttribute("cy",5);
changeBadge.setAttribute("r",5);
changeBadgeContainer.append(changeBadge)
RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
if (workspaceTabCount === 1) {
showWorkspace();
@@ -189,13 +404,19 @@ RED.workspaces = (function() {
RED.history.push({
t:'reorder',
workspaces: {
from:oldOrder,
to:newOrder
from: oldOrder,
to: newOrder
},
dirty:RED.nodes.dirty()
});
RED.nodes.dirty(true);
setWorkspaceOrder(newOrder);
// Only mark flows dirty if flow-order has changed (excluding subflows)
const filteredOldOrder = oldOrder.filter(id => !!RED.nodes.workspace(id))
const filteredNewOrder = newOrder.filter(id => !!RED.nodes.workspace(id))
if (JSON.stringify(filteredOldOrder) !== JSON.stringify(filteredNewOrder)) {
RED.nodes.dirty(true);
setWorkspaceOrder(newOrder);
}
},
onselect: function(selectedTabs) {
RED.view.select(false)
@@ -214,12 +435,12 @@ RED.workspaces = (function() {
},
onhide: function(tab) {
hideStack.push(tab.id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
hiddenTabs[tab.id] = true;
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:hide",{workspace: tab.id})
if (tab.type === "tab") {
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
hiddenTabs[tab.id] = true;
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:hide",{workspace: tab.id})
}
},
onshow: function(tab) {
removeFromHideStack(tab.id);
@@ -234,77 +455,8 @@ RED.workspaces = (function() {
scrollable: true,
addButton: "core:add-flow",
addButtonCaption: RED._("workspace.addFlow"),
menu: function() {
var menuItems = [
{
id:"red-ui-tabs-menu-option-search-flows",
label: RED._("workspace.listFlows"),
onselect: "core:list-flows"
},
{
id:"red-ui-tabs-menu-option-search-subflows",
label: RED._("workspace.listSubflows"),
onselect: "core:list-subflows"
},
null,
{
id:"red-ui-tabs-menu-option-add-flow",
label: RED._("workspace.addFlow"),
onselect: "core:add-flow"
},
{
id:"red-ui-tabs-menu-option-add-flow-right",
label: RED._("workspace.addFlowToRight"),
onselect: "core:add-flow-to-right"
},
null,
{
id:"red-ui-tabs-menu-option-add-hide-flows",
label: RED._("workspace.hideFlow"),
onselect: "core:hide-flow"
},
{
id:"red-ui-tabs-menu-option-add-hide-other-flows",
label: RED._("workspace.hideOtherFlows"),
onselect: "core:hide-other-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
label: RED._("workspace.showAllFlows"),
onselect: "core:show-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-last-flow",
label: RED._("workspace.showLastHiddenFlow"),
onselect: "core:show-last-hidden-flow"
}
]
let hiddenFlows = new Set()
for (let i = 0; i < hideStack.length; i++) {
let ids = hideStack[i]
if (!Array.isArray(ids)) {
ids = [ids]
}
ids.forEach(id => {
if (RED.nodes.workspace(id)) {
hiddenFlows.add(id)
}
})
}
const flowCount = hiddenFlows.size;
if (flowCount > 0) {
menuItems.unshift({
label: RED._("workspace.hiddenFlows",{count: flowCount}),
onselect: "core:list-hidden-flows"
})
}
return menuItems;
}
menu: function() { return getMenuItems(true) },
contextmenu: function(tab) { return getMenuItems(false, tab) }
});
workspaceTabCount = 0;
}
@@ -355,16 +507,33 @@ RED.workspaces = (function() {
});
RED.actions.add("core:add-flow",function(opts) { addWorkspace(undefined,undefined,opts?opts.index:undefined)});
RED.actions.add("core:add-flow-to-right",function(opts) { addWorkspace(undefined,undefined,workspace_tabs.activeIndex()+1)});
RED.actions.add("core:add-flow-to-right",function(workspace) {
let index
if (workspace) {
index = workspace_tabs.getTabIndex(workspace.id)+1
} else {
index = workspace_tabs.activeIndex()+1
}
addWorkspace(undefined,undefined,index)
});
RED.actions.add("core:edit-flow",editWorkspace);
RED.actions.add("core:remove-flow",removeWorkspace);
RED.actions.add("core:enable-flow",enableWorkspace);
RED.actions.add("core:disable-flow",disableWorkspace);
RED.actions.add("core:lock-flow",lockWorkspace);
RED.actions.add("core:unlock-flow",unlockWorkspace);
RED.actions.add("core:move-flow-to-start", function(id) { moveWorkspace(id, 'start') });
RED.actions.add("core:move-flow-to-end", function(id) { moveWorkspace(id, 'end') });
RED.actions.add("core:hide-flow", function() {
var selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
RED.actions.add("core:hide-flow", function(workspace) {
let selection
if (workspace) {
selection = [workspace]
} else {
selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
}
}
var hiddenTabs = [];
selection.forEach(function(ws) {
@@ -378,10 +547,15 @@ RED.workspaces = (function() {
workspace_tabs.clearSelection();
})
RED.actions.add("core:hide-other-flows", function() {
var selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
RED.actions.add("core:hide-other-flows", function(workspace) {
let selection
if (workspace) {
selection = [workspace]
} else {
selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
}
}
var selected = new Set(selection.map(function(ws) { return ws.id }))
@@ -471,6 +645,11 @@ RED.workspaces = (function() {
RED.workspaces.show(viewStack[++viewStackPos],true);
}
})
RED.events.on("flows:change", (ws) => {
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
})
hideWorkspace();
}
@@ -486,7 +665,7 @@ RED.workspaces = (function() {
}
function setWorkspaceState(id,disabled) {
var workspace = RED.nodes.workspace(id||activeWorkspace);
if (!workspace) {
if (!workspace || workspace.locked) {
return;
}
if (workspace.disabled !== disabled) {
@@ -521,11 +700,47 @@ RED.workspaces = (function() {
}
}
}
function lockWorkspace(id) {
setWorkspaceLockState(id,true);
}
function unlockWorkspace(id) {
setWorkspaceLockState(id,false);
}
function setWorkspaceLockState(id,locked) {
var workspace = RED.nodes.workspace(id||activeWorkspace);
if (!workspace) {
return;
}
if (workspace.locked !== locked) {
var changes = { locked: workspace.locked };
workspace.locked = locked;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
if (!id || (id === activeWorkspace)) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked);
}
var historyEvent = {
t: "edit",
changes:changes,
node: workspace,
dirty: RED.nodes.dirty()
}
workspace.changed = true;
RED.history.push(historyEvent);
RED.events.emit("flows:change",workspace);
RED.nodes.dirty(true);
RED.nodes.filterNodes({z:workspace.id}).forEach(n => n.dirty = true)
RED.view.redraw(true);
}
}
function removeWorkspace(ws) {
if (!ws) {
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
ws = RED.nodes.workspace(activeWorkspace)
if (ws && !ws.locked) {
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
}
} else {
if (ws.locked) { return }
if (workspace_tabs.contains(ws.id)) {
workspace_tabs.removeTab(ws.id);
}
@@ -535,16 +750,46 @@ RED.workspaces = (function() {
}
}
function moveWorkspace(id, direction) {
const workspace = RED.nodes.workspace(id||activeWorkspace) || RED.nodes.subflow(id||activeWorkspace);
if (!workspace) {
return;
}
const currentOrder = workspace_tabs.listTabs()
const oldOrder = [...currentOrder]
const currentIndex = currentOrder.findIndex(id => id === workspace.id)
currentOrder.splice(currentIndex, 1)
if (direction === 'start') {
currentOrder.unshift(workspace.id)
} else if (direction === 'end') {
currentOrder.push(workspace.id)
}
const newOrder = setWorkspaceOrder(currentOrder)
if (JSON.stringify(newOrder) !== JSON.stringify(oldOrder)) {
RED.history.push({
t:'reorder',
workspaces: {
from:oldOrder,
to:newOrder
},
dirty:RED.nodes.dirty()
});
const filteredOldOrder = oldOrder.filter(id => !!RED.nodes.workspace(id))
const filteredNewOrder = newOrder.filter(id => !!RED.nodes.workspace(id))
if (JSON.stringify(filteredOldOrder) !== JSON.stringify(filteredNewOrder)) {
RED.nodes.dirty(true);
}
}
}
function setWorkspaceOrder(order) {
var newOrder = order.filter(function(id) {
return RED.nodes.workspace(id) !== undefined;
})
var newOrder = order.filter(id => !!RED.nodes.workspace(id))
var currentOrder = RED.nodes.getWorkspaceOrder();
if (JSON.stringify(newOrder) !== JSON.stringify(currentOrder)) {
RED.nodes.setWorkspaceOrder(newOrder);
RED.events.emit("flows:reorder",newOrder);
}
workspace_tabs.order(order);
return newOrder
}
function flashTab(tabId) {
@@ -590,6 +835,11 @@ RED.workspaces = (function() {
active: function() {
return activeWorkspace
},
isLocked: function(id) {
id = id || activeWorkspace
var ws = RED.nodes.workspace(id) || RED.nodes.subflow(id)
return ws && ws.locked
},
selection: function() {
return workspace_tabs.selection();
},
@@ -646,6 +896,8 @@ RED.workspaces = (function() {
workspace_tabs.resize();
},
enable: enableWorkspace,
disable: disableWorkspace
disable: disableWorkspace,
lock: lockWorkspace,
unlock: unlockWorkspace
}
})();

View File

@@ -37,3 +37,27 @@
}
}
}
#red-ui-image-drop-target {
position: absolute;
top: 0; bottom: 0;
left: 0; right: 0;
background: var(--red-ui-dnd-background);
display:table;
width: 100%;
height: 100%;
display: none;
z-index:100;
div {
pointer-events: none;
display: table-cell;
vertical-align: middle;
text-align: center;
font-size: 40px;
color: var(--red-ui-dnd-color);
i {
pointer-events: none;
font-size: 80px;
}
}
}

View File

@@ -87,16 +87,18 @@
padding: 0px 8px;
height: 26px;
line-height: 26px;
&.toggle:not(.selected) {
&.toggle.selected {
color: var(--red-ui-workspace-button-color-selected) !important;
background: var(--red-ui-workspace-button-background-active);
background: var(--red-ui-workspace-button-background) !important;
}
}
.red-ui-tray-footer-left {
display:inline-block;
margin-right: 20px;
float:left;
& :not(:first-child) {
margin-left: 5px
}
}
.red-ui-tray-footer-right {
float: right;
@@ -124,7 +126,7 @@
list-style-type: none;
margin: 0;
padding:0;
overflow-wrap: anywhere;
li {
display: inline-block;
padding:0;

View File

@@ -68,6 +68,9 @@
stroke: var(--red-ui-node-border);
cursor: move;
stroke-width: 1;
.red-ui-workspace-locked & {
cursor: pointer;
}
}
.red-ui-workspace-select-mode {
g.red-ui-flow-node.red-ui-flow-node-hovered * {
@@ -88,10 +91,13 @@
.red-ui-flow-group {
&.red-ui-flow-group-hovered {
.red-ui-flow-group-outline-select {
.red-ui-flow-group-outline-select-line {
stroke-opacity: 0.8 !important;
stroke-dasharray: 10 4 !important;
}
.red-ui-flow-group-outline-select-outline {
stroke-opacity: 0.8 !important;
}
}
&.red-ui-flow-group-active-hovered:not(.red-ui-flow-group-hovered) {
.red-ui-flow-group-outline-select {
@@ -110,15 +116,35 @@
.red-ui-flow-group-outline-select {
fill: none;
stroke: var(--red-ui-node-selected-color);
pointer-events: stroke;
pointer-events: none;
stroke-opacity: 0;
stroke-width: 3;
stroke-width: 2;
&.red-ui-flow-group-outline-select-background {
&.red-ui-flow-group-outline-select-outline {
stroke: var(--red-ui-view-background);
stroke-width: 6;
stroke-width: 4;
}
&.red-ui-flow-group-outline-select-background {
fill: white;
fill-opacity: 0;
pointer-events: stroke;
stroke-width: 16;
}
}
svg:not(.red-ui-workspace-lasso-active) {
.red-ui-flow-group:not(.red-ui-flow-group-selected) {
.red-ui-flow-group-outline-select.red-ui-flow-group-outline-select-background:hover {
~ .red-ui-flow-group-outline-select {
stroke-opacity: 0.4 !important;
}
~ .red-ui-flow-group-outline-select-line {
stroke-dasharray: 10 4 !important;
}
}
}
}
.red-ui-flow-group-body {
pointer-events: none;
fill: var(--red-ui-group-default-fill);
@@ -278,7 +304,11 @@ g.red-ui-flow-node-selected {
stroke: var(--red-ui-node-status-colors-#{"" + $current-color});
}
}
.red-ui-flow-node-status-background {
stroke: none;
fill: var(--red-ui-view-background);
fill-opacity: 0.9;
}
.red-ui-flow-node-status-label {
@include disable-selection;
stroke-width: 0;
@@ -287,9 +317,11 @@ g.red-ui-flow-node-selected {
text-anchor:start;
}
.red-ui-flow-port-hovered {
stroke: var(--red-ui-port-selected-color);
fill: var(--red-ui-port-selected-color);
#red-ui-workspace:not(.red-ui-workspace-locked) {
.red-ui-flow-port-hovered {
stroke: var(--red-ui-port-selected-color);
fill: var(--red-ui-port-selected-color);
}
}
.red-ui-flow-subflow-port {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
**/
#red-ui-settings-tab-palette {
#red-ui-settings-tab-palette {
height: 100%;
}
@@ -28,7 +28,17 @@
padding: 0;
box-sizing:border-box;
background: var(--red-ui-secondary-background);
display: flex;
flex-direction: column;
.red-ui-tabs {
flex-shrink: 0;
margin-bottom: 0;
}
.red-ui-editableList.scrollable {
overflow-y: auto;
}
.red-ui-editableList-container {
border: none;
border-radius: 0;
@@ -72,11 +82,9 @@
}
.red-ui-palette-editor-tab {
position:absolute;
top:35px;
left:0;
right:0;
bottom:0
display: flex;
flex-direction: column;
min-height: 0;
}
.red-ui-palette-editor-toolbar {
background: var(--red-ui-primary-background);
@@ -84,6 +92,24 @@
padding: 8px 10px;
border-bottom: 1px solid var(--red-ui-primary-border-color);
text-align: right;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 3px 12px;
.red-ui-palette-editor-toolbar-actions {
flex-shrink: 0;
flex-grow: 1;
}
.red-ui-palette-editor-catalogue-filter {
width: unset;
margin: 0;
flex-shrink: 1;
flex-grow: 1;
font-size: 12px;
height: 26px;
padding: 1px;
}
}
.red-ui-palette-module-shade-status {
color: var(--red-ui-secondary-text-color);

View File

@@ -54,8 +54,8 @@
}
.red-ui-palette-search {
position: relative;
overflow: hidden;
background: var(--red-ui-secondary-background);
// overflow: hidden;
background: var(--red-ui-form-input-background);
text-align: center;
height: 35px;
padding: 3px;

View File

@@ -35,6 +35,7 @@
padding: 8px;
border-radius: 2px;
background: var(--red-ui-popover-background);
overflow-wrap: anywhere;
}
.red-ui-popover:after, .red-ui-popover:before {
border: solid transparent;

View File

@@ -108,6 +108,8 @@
}
.red-ui-search-result-node-label {
color: var(--red-ui-secondary-text-color);
width: 240px;
overflow-wrap: anywhere;
}
}

View File

@@ -31,6 +31,7 @@
> span {
display: inline-block;
margin-left: 5px;
overflow-wrap: anywhere;
}
border-bottom: 1px solid var(--red-ui-secondary-border-color);
}
@@ -467,6 +468,9 @@ div.red-ui-info-table {
.fa-eye {
display: none;
}
.fa-unlock-alt {
display: none;
}
}
.red-ui-info-outline-item-control-reveal,
.red-ui-info-outline-item-control-action {
@@ -500,6 +504,25 @@ div.red-ui-info-table {
display: none;
}
}
.fa-lock {
display: none;
}
.red-ui-info-outline-item.red-ui-info-outline-item-locked & {
.fa-lock {
display: inline-block;
}
.fa-unlock-alt {
display: none;
}
}
// If the parent is locked, do not show the display/action buttons when
// hovering in the outline
.red-ui-info-outline-item-locked .red-ui-info-outline-item & {
.red-ui-info-outline-item-control-disable,
.red-ui-info-outline-item-control-action {
display: none;
}
}
button {
margin-right: 3px
}
@@ -517,8 +540,6 @@ div.red-ui-info-table {
}
}
.red-ui-icons {
display: inline-block;
width: 18px;

View File

@@ -105,7 +105,38 @@
}
}
}
.red-ui-tab:not(.red-ui-workspace-changed) .red-ui-flow-tab-changed {
display: none;
}
.red-ui-tab.red-ui-workspace-changed .red-ui-flow-tab-changed {
display: inline-block;
position: absolute;
top: 1px;
right: 1px;
}
.red-ui-workspace-locked-icon {
display: none;
}
.red-ui-workspace-locked {
&.red-ui-tab {
// border-top-style: dashed;
// border-left-style: dashed;
// border-right-style: dashed;
// a {
// font-style: italic;
// color: var(--red-ui-tab-text-color-disabled-inactive) !important;
// }
// &.active a {
// font-weight: normal;
// color: var(--red-ui-tab-text-color-disabled-active) !important;
// }
.red-ui-workspace-locked-icon {
display: inline;
}
}
}
#red-ui-navigator-canvas {
position: absolute;

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -0,0 +1,197 @@
export default {
version: "3.0.0",
steps: [
{
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 3.0!",
"ja": "Node-RED 3.0へようこそ!",
"fr": "Bienvenue dans Node-RED 3.0 !"
},
description: {
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>",
"fr": "<p>Prenons un moment pour découvrir les nouvelles fonctionnalités de cette version.</p>"
}
},
{
title: {
"en-US": "Context Menu",
"ja": "コンテキストメニュー",
"fr": "Menu contextuel"
},
image: '3.0/images/context-menu.png',
description: {
"en-US": `<p>The editor now has its own context menu when you
right-click in the workspace.</p>
<p>This makes many of the built-in actions much easier
to access.</p>`,
"ja": `<p>ワークスペースで右クリックすると、エディタに独自のコンテキストメニューが表示されるようになりました。</p>
<p>これによって多くの組み込み動作を、より簡単に利用できます。</p>`,
"fr": `<p>L'éditeur a maintenant son propre menu contextuel lorsque vous
faites un clic droit dans l'espace de travail.</p>
<p>Cela facilite l'accès à de nombreuses actions intégrées.</p>`
}
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加",
"fr": "Jonctions de fils"
},
image: '3.0/images/junction-slice.gif',
description: {
"en-US": `<p>To make it easier to route wires around your flows,
it is now possible to add junction nodes that give
you more control.</p>
<p>Junctions can be added to wires by holding both the Alt key and the Shift key
then click and drag the mouse across the wires.</p>`,
"ja": `<p>フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。</p>
<p>Altキーとシフトキーを押しながらマウスをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。</p>`,
"fr": `<p>Pour faciliter le routage des câbles autour de vos flux, il est désormais possible d'ajouter des noeuds
de jonction qui vous donnent plus de contrôle.</p>
<p>Les jonctions peuvent être ajoutées aux fils en maintenant les touches Alt et Maj enfoncées, puis en cliquant
et en faisant glisser la souris sur les fils.</p>`
},
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加",
"fr": "Jonctions de fils"
},
image: '3.0/images/junction-quick-add.png',
description: {
"en-US": `<p>Junctions can also be added using the quick-add dialog.</p>
<p>The dialog is opened by holding the Ctrl (or Cmd) key when
clicking in the workspace.</p>`,
"ja": `<p>クイック追加ダイアログを用いて、分岐点を追加することもできます。</p>
<p>本ダイアログを開くには、Ctrl(またはCmd)キーを押しながら、ワークスペース上でクリックします。</p>`,
"fr": `<p>Les jonctions peuvent également être ajoutées à l'aide de la boîte de dialogue d'ajout rapide.</p>
<p>La boîte de dialogue s'ouvre en maintenant la touche Ctrl (ou Cmd) enfoncée lors d'un clic dans l'espace de travail.</p>`
},
},
{
title: {
"en-US": "Debug Path Tooltip",
"ja": "デバッグパスのツールチップ",
"fr": "Info-bulle du chemin de débogage"
},
image: '3.0/images/debug-path-tooltip.png',
description: {
"en-US": `<p>When hovering over a node name in the Debug sidebar, a
new tooltip shows the full location of the node.</p>
<p>This is useful when working with subflows, making it
much easier to identify exactly which node generated
the message.</p>
<p>Clicking on any item in the list will reveal it in
the workspace.</p>`,
"ja": `<p>デバックサイドバー内のノード名の上にマウスカーソルを乗せると、新たにツールチップが表示され、ノードの場所が分かるようになっています。</p>
<p>これは、サブフローを用いる時に役立つ機能であり、メッセージがどのノードから出力されたかを正確に特定することが遥かに簡単になります。</p>
<p>本リスト内の要素をクリックすると、ワークスペース内にその要素が表示されます。</p>`,
"fr": `<p>Lorsque vous passez la souris sur un nom de noeud dans la barre latérale de débogage, une nouvelle info-bulle affiche l'emplacement complet du noeud.</p>
<p>C'est utile lorsque vous travaillez avec des sous-flux, ce qui facilite l'identification exacte du noeud qui a généré le message.</p>
<p>Cliquer sur n'importe quel élément de la liste le révélera dans l'espace de travail.</p>`
},
},
{
title: {
"en-US": "Continuous Search",
"ja": "連続した検索",
"fr": "Recherche continue"
},
image: '3.0/images/continuous-search.png',
description: {
"en-US": `<p>When searching for things in the editor, a new toolbar in
the workspace provides options to quickly jump between
the search results.</p>`,
"ja": `<p>ワークスペース内の新しいツールバーにあるオプションによって、エディタ内を検索する際に、検索結果の間を素早く移動できます。</p>`,
"fr": `<p>Lorsque vous recherchez des éléments dans l'éditeur, une nouvelle barre d'outils dans l'espace de travail fournit des options pour passer
rapidement d'un résultat de recherche à l'autre.</p>`
},
},
{
title: {
"en-US": "New wiring actions",
"ja": "新しいワイヤー操作",
"fr": "Nouvelles actions de câblage"
},
image: "3.0/images/split-wire-with-links.gif",
description: {
"en-US": `<p>A new action has been added that will replace a wire with a pair of connected Link nodes:</p>
<ul>
<li><b><code>Split Wire With Link Nodes</code></b></li>
</ul>
<p>Actions can be accessed from the Action List in the main menu.</p>`,
"ja": `<p>ワイヤーを、接続されたLinkードのペアに置き換える動作が新たに追加されました:</p>
<ul>
<li><b><code>ワイヤーをlinkードで分割</code></b></li>
</ul>
<p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>`,
"fr": `<p>Une nouvelle action a été ajoutée pour remplacer un fil par une paire de noeuds de lien connectés :</p>
<ul>
<li><b><code>Diviser le fil avec les noeuds de liaison</code></b></li>
</ul>
<p>Les actions sont accessibles à partir de la liste d'actions dans le menu principal.</p>`
},
},
{
title: {
"en-US": "Default node names",
"ja": "標準ノードの名前",
"fr": "Noms de noeud par défaut"
},
// image: "images/",
description: {
"en-US": `<p>Some nodes have been updated to generate a unique name when
new instances are added to the workspace. This applies to
<code>Debug</code>, <code>Function</code> and <code>Link</code> nodes.</p>
<p>A new action has also been added to generate default names for the selected
nodes:</p>
<ul>
<li><b><code>Generate Node Names</code></b></li>
</ul><p>Actions can be accessed from the Action List in the main menu.</p>
`,
"ja": `<p>一部のノードは、ワークスペース上に新インスタンスとして追加した際に、一意の名前を付けるよう変更されました。この変更は、<code>Debug</code>、<code>Function</code>、<code>Link</code>ノードに適用されています。</p>
<p>選択したノードに対して、標準の名前を生成する動作も新たに追加されました:</p>
<ul>
<li><b><code>ノード名を生成</code></b></li>
</ul><p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>
`,
"fr": `<p>Certains noeuds ont été mis à jour pour générer un nom unique lorsque
de nouvelles instances sont ajoutées à l'espace de travail. Ceci s'applique aux
noeuds <code>Debug</code>, <code>Function</code> et <code>Link</code>.</p>
<p>Une nouvelle action a également été ajoutée pour générer des noms par défaut pour les noeuds sélectionnés :</p>
<ul>
<li><b><code>Générer des noms de noeud</code></b></li>
</ul>
<p>Les actions sont accessibles à partir de la liste d'actions dans le menu principal.</p>`
}
},
{
title: {
"en-US": "Node Updates",
"ja": "ノードの更新",
"fr": "Mises à jour des noeuds"
},
// image: "images/",
description: {
"en-US": `<ul>
<li>The Debug node can be configured to count messages it receives</li>
<li>The Link Call node can use a message property to dynamically target the link it should call</li>
<li>The HTTP Request node can be preconfigured with HTTP headers</li>
</ul>`,
"ja": `<ul>
<li>Debugードは、受信したメッセージの数をカウントするよう設定できるようになりました。</li>
<li>Link Callードは、メッセージのプロパティによって、呼び出し対象のlinkを動的に指定できるようになりました。</li>
<li>HTTP Requestードは、HTTPヘッダを事前設定できるようになりました。</li>
</ul>`,
"fr": `<ul>
<li>Le noeud de débogage peut être configuré pour compter les messages qu'il reçoit</li>
<li>Le noeud Link Call peut utiliser une propriété de message pour cibler dynamiquement le lien qu'il doit appeler</li>
<li>Le noeud de requête HTTP peut être préconfiguré avec des en-têtes HTTP</li>
</ul>`
}
}
]
}

View File

@@ -3,12 +3,14 @@ export default {
{
title: {
'en-US': 'Create your first flow',
'ja': 'はじめてのフローを作成'
'ja': 'はじめてのフローを作成',
'fr': "Créer votre premier flux"
},
width: 400,
description: {
'en-US': 'This tutorial will guide you through creating your first flow',
'ja': '本チュートリアルでは、はじめてのフローを作成する方法について説明します。'
'ja': '本チュートリアルでは、はじめてのフローを作成する方法について説明します。',
'fr': "Ce didacticiel vous guidera dans la création de votre premier flux"
},
nextButton: 'start'
},
@@ -16,7 +18,8 @@ export default {
element: "#red-ui-workspace .red-ui-tab-button.red-ui-tabs-add",
description: {
'en-US': 'To add a new tab, click the <i class="fa fa-plus"></i> button',
'ja': '新しいタブを追加するため、 <i class="fa fa-plus"></i> ボタンをクリックします。'
'ja': '新しいタブを追加するため、 <i class="fa fa-plus"></i> ボタンをクリックします。',
'fr': 'Pour ajouter un nouvel onglet, cliquez sur le bouton <i class="fa fa-plus"></i>'
},
wait: {
type: "dom-event",
@@ -29,7 +32,8 @@ export default {
direction: 'right',
description: {
'en-US': 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.',
'ja': 'パレットには、利用できる全てのードが一覧表示されます。injectードをワークスペースにドラッグします。'
'ja': 'パレットには、利用できる全てのードが一覧表示されます。injectードをワークスペースにドラッグします。',
'fr': "La palette répertorie tous les noeuds disponibles à utiliser. Faites glisser un nouveau noeud Inject dans l'espace de travail."
},
fallback: 'inset-bottom-right',
wait: {
@@ -52,7 +56,8 @@ export default {
direction: 'right',
description: {
'en-US': 'Next, drag a new Debug node into the workspace.',
'ja': '次に、debugードをワークスペースにドラッグします。'
'ja': '次に、debugードをワークスペースにドラッグします。',
'fr': "Ensuite, faites glisser un nouveau noeud Debug dans l'espace de travail."
},
fallback: 'inset-bottom-right',
wait: {
@@ -74,7 +79,8 @@ export default {
element: function() { return $("#"+this.injectNode.id+" .red-ui-flow-port") },
description: {
'en-US': 'Add a wire from the output of the Inject node to the input of the Debug node',
'ja': 'injectードの出力から、debugードの入力へワイヤーで接続します。'
'ja': 'injectードの出力から、debugードの入力へワイヤーで接続します。',
'fr': "Ajoutez un fil de la sortie du noeud Inject à l'entrée du noeud Debug"
},
fallback: 'inset-bottom-right',
wait: {
@@ -89,7 +95,8 @@ export default {
element: "#red-ui-header-button-deploy",
description: {
'en-US': 'Deploy your changes so the flow is active in the runtime',
'ja': 'フローをランタイムで実行させるため、変更をデプロイします。'
'ja': 'フローをランタイムで実行させるため、変更をデプロイします。',
'fr': "Déployez vos modifications afin que le flux soit actif dans le runtime"
},
width: 200,
wait: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,154 +1,230 @@
export default {
version: "3.0.0",
version: "3.1.0",
steps: [
{
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 3.0!",
"ja": "Node-RED 3.0へようこそ!"
"en-US": "Welcome to Node-RED 3.1!",
"ja": "Node-RED 3.1へようこそ!",
"fr": "Bienvenue dans Node-RED 3.1!"
},
description: {
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>"
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>",
"fr": "<p>Prenons un moment pour découvrir les nouvelles fonctionnalités de cette version.</p>"
}
},
{
title: {
"en-US": "Context Menu",
"ja": "コンテキストメニュー"
"en-US": "New ways to work with groups",
"ja": "グループの新たな操作方法",
"fr": "De nouvelles façons de travailler avec les groupes"
},
description: {
"en-US": `<p>We have changed how you interact with groups in the editor.</p>
<ul>
<li>They don't get in the way when clicking on a node</li>
<li>They can be reordered using the Moving Forwards and Move Backwards actions</li>
<li>Multiple nodes can be dragged into a group in one go</li>
<li>Holding <code>Alt</code> when dragging a node will *remove* it from its group</li>
</ul>`,
"ja": `<p>エディタ上のグループの操作が変更されました。</p>
<ul>
<li>グループ内のノードをクリックする時に、グループが邪魔をすることが無くなりました。</li>
<li>「前面へ移動」と「背面へ移動」の動作を用いて、複数のグループの表示順序を変えることができます。</li>
<li>グループ内へ一度に複数のノードをドラッグできるようになりました。</li>
<li><code>Alt</code> を押したまま、グループ内のノードをドラッグすると、そのグループから *除く* ことができます。</li>
</ul>`,
"fr": `<p>Nous avons modifié la façon dont vous interagissez avec les groupes dans l'éditeur.</p>
<ul>
<li>Ils ne gênent plus lorsque vous cliquez sur un noeud</li>
<li>Ils peuvent être réorganisés à l'aide des actions Avancer et Reculer</li>
<li>Plusieurs noeuds peuvent être glissés dans un groupe en une seule fois</li>
<li>Maintenir <code>Alt</code> lors du déplacement d'un noeud le *supprimera* de son groupe</li>
</ul>`
}
},
{
title: {
"en-US": "Change notification on tabs",
"ja": "タブ上の変更通知",
"fr": "Notification de changement sur les onglets"
},
image: 'images/tab-changes.png',
description: {
"en-US": `<p>When a tab contains undeployed changes it now shows the
same style of change icon used by nodes.</p>
<p>This will make it much easier to track down changes when you're
working across multiple flows.</p>`,
"ja": `<p>タブ内にデプロイされていない変更が存在する時は、ノードと同じスタイルで変更の印が表示されるようになりました。</p>
<p>これによって複数のフローを編集している時に、変更を見つけるのが簡単になりました。</p>`,
"fr": `<p>Lorsqu'un onglet contient des modifications non déployées, il affiche désormais le
même style d'icône de changement utilisé par les noeuds.</p>
<p>Cela facilitera grandement le suivi des modifications lorsque vous
travaillez sur plusieurs flux.</p>`
}
},
{
title: {
"en-US": "A bigger canvas to work with",
"ja": "より広くなった作業キャンバス",
"fr": "Un canevas plus grand pour travailler"
},
description: {
"en-US": `<p>The default canvas size has been increased so you can fit more
into one flow.</p>
<p>We still recommend using tools such as subflows and Link Nodes to help
keep things organised, but now you have more room to work in.</p>`,
"ja": `<p>標準のキャンバスが広くなったため、1つのフローに沢山のものを含めることができるようになりました。</p>
<p>引き続き、サブフローやリンクノードなどの方法を用いて整理することをお勧めしますが、作業できる場所が増えました。</p>`,
"fr": `<p>La taille par défaut du canevas a été augmentée pour que vous puissiez en mettre plus
sur un seul flux.</p>
<p>Nous recommandons toujours d'utiliser des outils tels que les sous-flux et les noeuds de lien pour vous aider
à garder les choses organisées, mais vous avez maintenant plus d'espace pour travailler.</p>`
}
},
{
title: {
"en-US": "Finding help",
"ja": "ヘルプを見つける",
"fr": "Trouver de l'aide"
},
image: 'images/node-help.png',
description: {
"en-US": `<p>All node edit dialogs now include a link to that node's help
in the footer.</p>
<p>Clicking it will open up the Help sidebar showing the help for that node.</p>`,
"ja": `<p>全てのノードの編集ダイアログの下に、ノードのヘルプへのリンクが追加されました。</p>
<p>これをクリックすると、ノードのヘルプサイドバーが表示されます。</p>`,
"fr": `<p>Toutes les boîtes de dialogue d'édition de noeud incluent désormais un lien vers l'aide de ce noeud
dans le pied de page.</p>
<p>Cliquer dessus ouvrira la barre latérale d'aide affichant l'aide pour ce noeud.</p>`
}
},
{
title: {
"en-US": "Improved Context Menu",
"ja": "コンテキストメニューの改善",
"fr": "Menu contextuel amélioré"
},
image: 'images/context-menu.png',
description: {
"en-US": `<p>The editor now has its own context menu when you
right-click in the workspace.</p>
<p>This makes many of the built-in actions much easier
to access.</p>`,
"ja": `<p>ワークスペースで右クリックすると、エディタに独自のコンテキストメニューが表示されるようになりました。</p>
<p>これによって多くの組み込み動作を、より簡単に利用できます。</p>`
"en-US": `<p>The editor's context menu has been expanded to make lots more of
the built-in actions available.</p>
<p>Adding nodes, working with groups and plenty
of other useful tools are now just a click away.</p>
<p>The flow tab bar also has its own context menu to make working
with your flows much easier.</p>`,
"ja": `<p>より多くの組み込み動作を利用できるように、エディタのコンテキストメニューが拡張されました。</p>
<p>ノードの追加、グループの操作、その他の便利なツールをクリックするだけで実行できるようになりました。</p>
<p>フローのタブバーには、フローの操作をより簡単にする独自のコンテキストメニューもあります。</p>`,
"fr": `<p>Le menu contextuel de l'éditeur a été étendu pour faire beaucoup plus d'actions intégrées disponibles.</p>
<p>Ajouter des noeuds, travailler avec des groupes et beaucoup d'autres outils utiles sont désormais à portée de clic.</p>
<p>La barre d'onglets de flux possède également son propre menu contextuel pour faciliter l'utilisation de vos flux.</p>`
}
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
"en-US": "Hiding Flows",
"ja": "フローを非表示",
"fr": "Masquage de flux"
},
image: 'images/junction-slice.gif',
image: 'images/hiding-flows.png',
description: {
"en-US": `<p>To make it easier to route wires around your flows,
it is now possible to add junction nodes that give
you more control.</p>
<p>Junctions can be added to wires by holding both the Alt key and the Shift key
then click and drag the mouse across the wires.</p>`,
"ja": `<p>フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。</p>
<p>Altキーとシフトキーを押しながらマウスをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。</p>`
"en-US": `<p>Hiding flows is now done through the flow context menu.</p>
<p>The 'hide' button in previous releases has been removed from the tabs
as they were being clicked accidentally too often.</p>`,
"ja": `<p>フローを非表示にする機能は、フローのコンテキストメニューから実行するようになりました。</p>
<p>これまでのリリースでタブに存在していた「非表示」ボタンは、よく誤ってクリックされていたため、削除されました。</p>`,
"fr": `<p>Le masquage des flux s'effectue désormais via le menu contextuel du flux.</p>
<p>Le bouton "Masquer" des versions précédentes a été supprimé des onglets
car il était cliqué accidentellement trop souvent.</p>`
},
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
"en-US": "Locking Flows",
"ja": "フローを固定",
"fr": "Verrouillage de flux"
},
image: 'images/junction-quick-add.png',
image: 'images/locking-flows.png',
description: {
"en-US": `<p>Junctions can also be added using the quick-add dialog.</p>
<p>The dialog is opened by holding the Ctrl (or Cmd) key when
clicking in the workspace.</p>`,
"ja": `<p>クイック追加ダイアログを用いて、分岐点を追加することもできます。</p>
<p>本ダイアログを開くには、Ctrl(またはCmd)キーを押しながら、ワークスペース上でクリックします。</p>`
"en-US": `<p>Flows can now be locked to prevent accidental changes being made.</p>
<p>When locked you cannot modify the nodes in any way.</p>
<p>The flow context menu provides the options to lock and unlock flows,
as well as in the Info sidebar explorer.</p>`,
"ja": `<p>誤ってフローに変更が加えられてしまうのを防ぐために、フローを固定できるようになりました。</p>
<p>固定されている時は、ノードを修正することはできません。</p>
<p>フローのコンテキストメニューと、情報サイドバーのエクスプローラには、フローの固定や解除をするためのオプションが用意されています。</p>`,
"fr": `<p>Les flux peuvent désormais être verrouillés pour éviter toute modification accidentelle.</p>
<p>Lorsqu'il est verrouillé, vous ne pouvez en aucun cas modifier les noeuds.</p>
<p>Le menu contextuel du flux fournit les options pour verrouiller et déverrouiller les flux,
ainsi que dans l'explorateur de la barre latérale d'informations.</p>`
},
},
{
title: {
"en-US": "Debug Path Tooltip",
"ja": "デバッグパスのツールチップ"
"en-US": "Adding Images to node/flow descriptions",
"ja": "ノードやフローの説明へ画像を追加",
"fr": "Ajout d'images aux descriptions de noeud/flux"
},
image: 'images/debug-path-tooltip.png',
// image: 'images/debug-path-tooltip.png',
description: {
"en-US": `<p>When hovering over a node name in the Debug sidebar, a
new tooltip shows the full location of the node.</p>
<p>This is useful when working with subflows, making it
much easier to identify exactly which node generated
the message.</p>
<p>Clicking on any item in the list will reveal it in
the workspace.</p>`,
"ja": `<p>デバックサイドバー内のノード名の上にマウスカーソルを乗せると、新たにツールチップが表示され、ノードの場所が分かるようになっています。</p>
<p>これは、サブフローを用いる時に役立つ機能であり、メッセージがどのノードから出力されたかを正確に特定することが遥かに簡単になります。</p>
<p>本リスト内の要素をクリックすると、ワークスペース内にその要素が表示されます。</p>`
"en-US": `<p>You can now add images to a node's or flows's description.</p>
<p>Simply drag the image into the text editor and it will get added inline.</p>
<p>When the description is shown in the Info sidebar, the image will be displayed.</p>`,
"ja": `<p>ノードまたはフローの説明に、画像を追加できるようになりました。</p>
<p>画像をテキストエディタにドラッグするだけで、行内に埋め込まれます。</p>
<p>情報サイドバーの説明を開くと、その画像が表示されます。</p>`,
"fr": `<p>Vous pouvez désormais ajouter des images à la description d'un noeud ou d'un flux.</p>
<p>Faites simplement glisser l'image dans l'éditeur de texte et elle sera ajoutée en ligne.</p>
<p>Lorsque la description s'affiche dans la barre latérale d'informations, l'image s'affiche.</p>`
},
},
{
title: {
"en-US": "Continuous Search",
"ja": "連続した検索"
"en-US": "Adding Mermaid Diagrams",
"ja": "Mermaid図を追加",
"fr": "Ajout de diagrammes Mermaid"
},
image: 'images/continuous-search.png',
image: 'images/mermaid.png',
description: {
"en-US": `<p>When searching for things in the editor, a new toolbar in
the workspace provides options to quickly jump between
the search results.</p>`,
"ja": `<p>ワークスペース内の新しいツールバーにあるオプションによって、エディタ内を検索する際に、検索結果の間を素早く移動できます。</p>`
"en-US": `<p>You can also add <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> diagrams directly into your node or flow descriptions.</p>
<p>This gives you much richer options for documenting your flows.</p>`,
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>図を直接追加することもできます。</p>
<p>これによって、フローを説明する文書作成の選択肢がより多くなります。</p>`,
"fr": `<p>Vous pouvez également ajouter des diagrammes <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> directement dans vos descriptions de noeud ou de flux.</p>
<p>Cela vous offre des options beaucoup plus riches pour documenter vos flux.</p>`
},
},
{
title: {
"en-US": "New wiring actions",
"ja": "新しいワイヤー操作"
"en-US": "Managing Global Environment Variables",
"ja": "グローバル環境変数の管理",
"fr": "Gestion des variables d'environnement globales"
},
image: "images/split-wire-with-links.gif",
image: 'images/global-env-vars.png',
description: {
"en-US": `<p>A new action has been added that will replace a wire with a pair of connected Link nodes:</p>
<ul>
<li><b><code>Split Wire With Link Nodes</code></b></li>
</ul>
<p>Actions can be accessed from the Action List in the main menu.</p>`,
"ja": `<p>ワイヤーを、接続されたLinkードのペアに置き換える動作が新たに追加されました:</p>
<ul>
<li><b><code>ワイヤーをlinkードで分割</code></b></li>
</ul>
<p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>`,
"en-US": `<p>You can set environment variables that apply to all nodes and flows in the new
'Global Environment Variables' section of User Settings.</p>`,
"ja": `<p>ユーザ設定に新しく追加された「大域環境変数」のセクションで、全てのノードとフローに適用される環境変数を登録できます。</p>`,
"fr": `<p>Vous pouvez définir des variables d'environnement qui s'appliquent à tous les noeuds et flux dans la nouvelle
section "Global Environment Variables" des paramètres utilisateur.</p>`
},
},
{
title: {
"en-US": "Default node names",
"ja": "標準ノードの名前"
},
// image: "images/",
description: {
"en-US": `<p>Some nodes have been updated to generate a unique name when
new instances are added to the workspace. This applies to
<code>Debug</code>, <code>Function</code> and <code>Link</code> nodes.</p>
<p>A new action has also been added to generate default names for the selected
nodes:</p>
<ul>
<li><b><code>Generate Node Names</code></b></li>
</ul><p>Actions can be accessed from the Action List in the main menu.</p>
`,
"ja": `<p>一部のノードは、ワークスペース上に新インスタンスとして追加した際に、一意の名前を付けるよう変更されました。この変更は、<code>Debug</code>、<code>Function</code>、<code>Link</code>ノードに適用されています。</p>
<p>選択したノードに対して、標準の名前を生成する動作も新たに追加されました:</p>
<ul>
<li><b><code>ノード名を生成</code></b></li>
</ul><p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>
`
}
},
{
title: {
"en-US": "Node Updates",
"ja": "ノードの更新"
"ja": "ノードの更新",
"fr": "Mises à jour des noeuds"
},
// image: "images/",
description: {
"en-US": `<ul>
<li>The Debug node can be configured to count messages it receives</li>
<li>The Link Call node can use a message property to dynamically target the link it should call</li>
<li>The HTTP Request node can be preconfigured with HTTP headers</li>
</ul>`,
"ja": `<ul>
<li>Debugードは、受信したメッセージの数をカウントするよう設定できるようになりました。</li>
<li>Link Callードは、メッセージのプロパティによって、呼び出し対象のlinkを動的に指定できるようになりました。</li>
<li>HTTP Requestードは、HTTPヘッダを事前設定できるようになりました。</li>
</ul>`
"en-US": `<p>The core nodes have received lots of minor fixes, documentation updates and
small enhancements. Check the full changelog in the Help sidebar for a full list.</p>`,
"ja": `<p>コアノードにマイナーな修正、ドキュメント更新、小規模な拡張が数多く追加されています。全ての一覧は、ヘルプサイドバーの全ての更新履歴を確認してください。</p>`,
"fr": `<p>Les noeuds principaux ont reçu de nombreux correctifs mineurs, mises à jour de la documentation et
petites améliorations. Consulter le journal des modifications complet dans la barre latérale d'aide.</p>`
}
}
]

View File

@@ -1,6 +1,5 @@
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/node-red/nr-monaco-build */
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/Steve-Mcl/monaco-editor-esm-i18n */
interface NodeMessage {
topic?: string;
@@ -281,5 +280,5 @@ declare class env {
* @example
* ```const flowName = env.get("NR_FLOW_NAME");```
*/
static get(name:string) :string;
static get(name:string) :any;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/Steve-Mcl/monaco-editor-esm-i18n */
declare module 'assert/strict' {
import { strict } from 'node:assert';
export = strict;
}
declare module 'node:assert/strict' {
import { strict } from 'node:assert';
export = strict;
}

View File

@@ -2,18 +2,49 @@
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/Steve-Mcl/monaco-editor-esm-i18n */
/**
* Async Hooks module: https://nodejs.org/api/async_hooks.html
* The `async_hooks` module provides an API to track asynchronous resources. It
* can be accessed using:
*
* ```js
* import async_hooks from 'async_hooks';
* ```
* @experimental
* @see [source](https://github.com/nodejs/node/blob/v16.9.0/lib/async_hooks.js)
*/
declare module 'async_hooks' {
/**
* Returns the asyncId of the current execution context.
* ```js
* import { executionAsyncId } from 'async_hooks';
*
* console.log(executionAsyncId()); // 1 - bootstrap
* fs.open(path, 'r', (err, fd) => {
* console.log(executionAsyncId()); // 6 - open()
* });
* ```
*
* The ID returned from `executionAsyncId()` is related to execution timing, not
* causality (which is covered by `triggerAsyncId()`):
*
* ```js
* const server = net.createServer((conn) => {
* // Returns the ID of the server, not of the new connection, because the
* // callback runs in the execution scope of the server's MakeCallback().
* async_hooks.executionAsyncId();
*
* }).listen(port, () => {
* // Returns the ID of a TickObject (process.nextTick()) because all
* // callbacks passed to .listen() are wrapped in a nextTick().
* async_hooks.executionAsyncId();
* });
* ```
*
* Promise contexts may not get precise `executionAsyncIds` by default.
* See the section on `promise execution tracking`.
* @since v8.1.0
* @return The `asyncId` of the current execution context. Useful to track when something calls.
*/
function executionAsyncId(): number;
/**
* The resource representing the current execution.
* Useful to store data within the resource.
*
* Resource objects returned by `executionAsyncResource()` are most often internal
* Node.js handle objects with undocumented APIs. Using any functions or properties
* on the object is likely to crash your application and should be avoided.
@@ -21,14 +52,70 @@ declare module 'async_hooks' {
* Using `executionAsyncResource()` in the top-level execution context will
* return an empty object as there is no handle or request object to use,
* but having an object representing the top-level can be helpful.
*
* ```js
* import { open } from 'fs';
* import { executionAsyncId, executionAsyncResource } from 'async_hooks';
*
* console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
* open(new URL(import.meta.url), 'r', (err, fd) => {
* console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
* });
* ```
*
* This can be used to implement continuation local storage without the
* use of a tracking `Map` to store the metadata:
*
* ```js
* import { createServer } from 'http';
* import {
* executionAsyncId,
* executionAsyncResource,
* createHook
* } from 'async_hooks';
* const sym = Symbol('state'); // Private symbol to avoid pollution
*
* createHook({
* init(asyncId, type, triggerAsyncId, resource) {
* const cr = executionAsyncResource();
* if (cr) {
* resource[sym] = cr[sym];
* }
* }
* }).enable();
*
* const server = createServer((req, res) => {
* executionAsyncResource()[sym] = { state: req.url };
* setTimeout(function() {
* res.end(JSON.stringify(executionAsyncResource()[sym]));
* }, 100);
* }).listen(3000);
* ```
* @since v13.9.0, v12.17.0
* @return The resource representing the current execution. Useful to store data within the resource.
*/
function executionAsyncResource(): object;
/**
* Returns the ID of the resource responsible for calling the callback that is currently being executed.
* ```js
* const server = net.createServer((conn) => {
* // The resource that caused (or triggered) this callback to be called
* // was that of the new connection. Thus the return value of triggerAsyncId()
* // is the asyncId of "conn".
* async_hooks.triggerAsyncId();
*
* }).listen(port, () => {
* // Even though all callbacks passed to .listen() are wrapped in a nextTick()
* // the callback itself exists because the call to the server's .listen()
* // was made. So the return value would be the ID of the server.
* async_hooks.triggerAsyncId();
* });
* ```
*
* Promise contexts may not get valid `triggerAsyncId`s by default. See
* the section on `promise execution tracking`.
* @return The ID of the resource responsible for calling the callback that is currently being executed.
*/
function triggerAsyncId(): number;
interface HookCallbacks {
/**
* Called when a class is constructed that has the possibility to emit an asynchronous event.
@@ -38,73 +125,133 @@ declare module 'async_hooks' {
* @param resource reference to the resource representing the async operation, needs to be released during destroy
*/
init?(asyncId: number, type: string, triggerAsyncId: number, resource: object): void;
/**
* When an asynchronous operation is initiated or completes a callback is called to notify the user.
* The before callback is called just before said callback is executed.
* @param asyncId the unique identifier assigned to the resource about to execute the callback.
*/
before?(asyncId: number): void;
/**
* Called immediately after the callback specified in before is completed.
* @param asyncId the unique identifier assigned to the resource which has executed the callback.
*/
after?(asyncId: number): void;
/**
* Called when a promise has resolve() called. This may not be in the same execution id
* as the promise itself.
* @param asyncId the unique id for the promise that was resolve()d.
*/
promiseResolve?(asyncId: number): void;
/**
* Called after the resource corresponding to asyncId is destroyed
* @param asyncId a unique ID for the async resource
*/
destroy?(asyncId: number): void;
}
interface AsyncHook {
/**
* Enable the callbacks for a given AsyncHook instance. If no callbacks are provided enabling is a noop.
*/
enable(): this;
/**
* Disable the callbacks for a given AsyncHook instance from the global pool of AsyncHook callbacks to be executed. Once a hook has been disabled it will not be called again until enabled.
*/
disable(): this;
}
/**
* Registers functions to be called for different lifetime events of each async operation.
* @param options the callbacks to register
* @return an AsyncHooks instance used for disabling and enabling hooks
* Registers functions to be called for different lifetime events of each async
* operation.
*
* The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
* respective asynchronous event during a resource's lifetime.
*
* All callbacks are optional. For example, if only resource cleanup needs to
* be tracked, then only the `destroy` callback needs to be passed. The
* specifics of all functions that can be passed to `callbacks` is in the `Hook Callbacks` section.
*
* ```js
* import { createHook } from 'async_hooks';
*
* const asyncHook = createHook({
* init(asyncId, type, triggerAsyncId, resource) { },
* destroy(asyncId) { }
* });
* ```
*
* The callbacks will be inherited via the prototype chain:
*
* ```js
* class MyAsyncCallbacks {
* init(asyncId, type, triggerAsyncId, resource) { }
* destroy(asyncId) {}
* }
*
* class MyAddedCallbacks extends MyAsyncCallbacks {
* before(asyncId) { }
* after(asyncId) { }
* }
*
* const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
* ```
*
* Because promises are asynchronous resources whose lifecycle is tracked
* via the async hooks mechanism, the `init()`, `before()`, `after()`, and`destroy()` callbacks _must not_ be async functions that return promises.
* @since v8.1.0
* @param callbacks The `Hook Callbacks` to register
* @return Instance used for disabling and enabling hooks
*/
function createHook(options: HookCallbacks): AsyncHook;
function createHook(callbacks: HookCallbacks): AsyncHook;
interface AsyncResourceOptions {
/**
* The ID of the execution context that created this async event.
* @default executionAsyncId()
*/
triggerAsyncId?: number | undefined;
/**
* Disables automatic `emitDestroy` when the object is garbage collected.
* This usually does not need to be set (even if `emitDestroy` is called
* manually), unless the resource's `asyncId` is retrieved and the
* sensitive API's `emitDestroy` is called with it.
* @default false
*/
requireManualDestroy?: boolean | undefined;
/**
* The ID of the execution context that created this async event.
* @default executionAsyncId()
*/
triggerAsyncId?: number | undefined;
/**
* Disables automatic `emitDestroy` when the object is garbage collected.
* This usually does not need to be set (even if `emitDestroy` is called
* manually), unless the resource's `asyncId` is retrieved and the
* sensitive API's `emitDestroy` is called with it.
* @default false
*/
requireManualDestroy?: boolean | undefined;
}
/**
* The class AsyncResource was designed to be extended by the embedder's async resources.
* Using this users can easily trigger the lifetime events of their own resources.
* The class `AsyncResource` is designed to be extended by the embedder's async
* resources. Using this, users can easily trigger the lifetime events of their
* own resources.
*
* The `init` hook will trigger when an `AsyncResource` is instantiated.
*
* The following is an overview of the `AsyncResource` API.
*
* ```js
* import { AsyncResource, executionAsyncId } from 'async_hooks';
*
* // AsyncResource() is meant to be extended. Instantiating a
* // new AsyncResource() also triggers init. If triggerAsyncId is omitted then
* // async_hook.executionAsyncId() is used.
* const asyncResource = new AsyncResource(
* type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }
* );
*
* // Run a function in the execution context of the resource. This will
* // * establish the context of the resource
* // * trigger the AsyncHooks before callbacks
* // * call the provided function `fn` with the supplied arguments
* // * trigger the AsyncHooks after callbacks
* // * restore the original execution context
* asyncResource.runInAsyncScope(fn, thisArg, ...args);
*
* // Call AsyncHooks destroy callbacks.
* asyncResource.emitDestroy();
*
* // Return the unique ID assigned to the AsyncResource instance.
* asyncResource.asyncId();
*
* // Return the trigger ID for the AsyncResource instance.
* asyncResource.triggerAsyncId();
* ```
*/
class AsyncResource {
/**
@@ -114,115 +261,236 @@ declare module 'async_hooks' {
* @param type The type of async event.
* @param triggerAsyncId The ID of the execution context that created
* this async event (default: `executionAsyncId()`), or an
* AsyncResourceOptions object (since 9.3)
* AsyncResourceOptions object (since v9.3.0)
*/
constructor(type: string, triggerAsyncId?: number|AsyncResourceOptions);
constructor(type: string, triggerAsyncId?: number | AsyncResourceOptions);
/**
* Binds the given function to the current execution context.
*
* The returned function will have an `asyncResource` property referencing
* the `AsyncResource` to which the function is bound.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current execution context.
* @param type An optional name to associate with the underlying `AsyncResource`.
*/
static bind<Func extends (...args: any[]) => any>(fn: Func, type?: string): Func & { asyncResource: AsyncResource };
static bind<Func extends (this: ThisArg, ...args: any[]) => any, ThisArg>(
fn: Func,
type?: string,
thisArg?: ThisArg
): Func & {
asyncResource: AsyncResource;
};
/**
* Binds the given function to execute to this `AsyncResource`'s scope.
*
* The returned function will have an `asyncResource` property referencing
* the `AsyncResource` to which the function is bound.
* @since v14.8.0, v12.19.0
* @param fn The function to bind to the current `AsyncResource`.
*/
bind<Func extends (...args: any[]) => any>(fn: Func): Func & { asyncResource: AsyncResource };
bind<Func extends (...args: any[]) => any>(
fn: Func
): Func & {
asyncResource: AsyncResource;
};
/**
* Call the provided function with the provided arguments in the
* execution context of the async resource. This will establish the
* context, trigger the AsyncHooks before callbacks, call the function,
* trigger the AsyncHooks after callbacks, and then restore the original
* execution context.
* @param fn The function to call in the execution context of this
* async resource.
* Call the provided function with the provided arguments in the execution context
* of the async resource. This will establish the context, trigger the AsyncHooks
* before callbacks, call the function, trigger the AsyncHooks after callbacks, and
* then restore the original execution context.
* @since v9.6.0
* @param fn The function to call in the execution context of this async resource.
* @param thisArg The receiver to be used for the function call.
* @param args Optional arguments to pass to the function.
*/
runInAsyncScope<This, Result>(fn: (this: This, ...args: any[]) => Result, thisArg?: This, ...args: any[]): Result;
/**
* Call AsyncHooks destroy callbacks.
* Call all `destroy` hooks. This should only ever be called once. An error will
* be thrown if it is called more than once. This **must** be manually called. If
* the resource is left to be collected by the GC then the `destroy` hooks will
* never be called.
* @return A reference to `asyncResource`.
*/
emitDestroy(): this;
/**
* @return the unique ID assigned to this AsyncResource instance.
* @return The unique `asyncId` assigned to the resource.
*/
asyncId(): number;
/**
* @return the trigger ID for this AsyncResource instance.
*
* @return The same `triggerAsyncId` that is passed to the `AsyncResource` constructor.
*/
triggerAsyncId(): number;
}
/**
* When having multiple instances of `AsyncLocalStorage`, they are independent
* from each other. It is safe to instantiate this class multiple times.
* This class creates stores that stay coherent through asynchronous operations.
*
* While you can create your own implementation on top of the `async_hooks` module,`AsyncLocalStorage` should be preferred as it is a performant and memory safe
* implementation that involves significant optimizations that are non-obvious to
* implement.
*
* The following example uses `AsyncLocalStorage` to build a simple logger
* that assigns IDs to incoming HTTP requests and includes them in messages
* logged within each request.
*
* ```js
* import http from 'http';
* import { AsyncLocalStorage } from 'async_hooks';
*
* const asyncLocalStorage = new AsyncLocalStorage();
*
* function logWithId(msg) {
* const id = asyncLocalStorage.getStore();
* console.log(`${id !== undefined ? id : '-'}:`, msg);
* }
*
* let idSeq = 0;
* http.createServer((req, res) => {
* asyncLocalStorage.run(idSeq++, () => {
* logWithId('start');
* // Imagine any chain of async operations here
* setImmediate(() => {
* logWithId('finish');
* res.end();
* });
* });
* }).listen(8080);
*
* http.get('http://localhost:8080');
* http.get('http://localhost:8080');
* // Prints:
* // 0: start
* // 1: start
* // 0: finish
* // 1: finish
* ```
*
* Each instance of `AsyncLocalStorage` maintains an independent storage context.
* Multiple instances can safely exist simultaneously without risk of interfering
* with each other data.
* @since v13.10.0, v12.17.0
*/
class AsyncLocalStorage<T> {
/**
* This method disables the instance of `AsyncLocalStorage`. All subsequent calls
* to `asyncLocalStorage.getStore()` will return `undefined` until
* `asyncLocalStorage.run()` is called again.
* Disables the instance of `AsyncLocalStorage`. All subsequent calls
* to `asyncLocalStorage.getStore()` will return `undefined` until`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
*
* When calling `asyncLocalStorage.disable()`, all current contexts linked to the
* instance will be exited.
*
* Calling `asyncLocalStorage.disable()` is required before the
* `asyncLocalStorage` can be garbage collected. This does not apply to stores
* Calling `asyncLocalStorage.disable()` is required before the`asyncLocalStorage` can be garbage collected. This does not apply to stores
* provided by the `asyncLocalStorage`, as those objects are garbage collected
* along with the corresponding async resources.
*
* This method is to be used when the `asyncLocalStorage` is not in use anymore
* Use this method when the `asyncLocalStorage` is not in use anymore
* in the current process.
* @since v13.10.0, v12.17.0
* @experimental
*/
disable(): void;
/**
* This method returns the current store. If this method is called outside of an
* asynchronous context initialized by calling `asyncLocalStorage.run`, it will
* return `undefined`.
* Returns the current store.
* If called outside of an asynchronous context initialized by
* calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
* returns `undefined`.
* @since v13.10.0, v12.17.0
*/
getStore(): T | undefined;
/**
* This methods runs a function synchronously within a context and return its
* Runs a function synchronously within a context and returns its
* return value. The store is not accessible outside of the callback function or
* the asynchronous operations created within the callback.
*
* Optionally, arguments can be passed to the function. They will be passed to the
* callback function.
* The optional `args` are passed to the callback function.
*
* I the callback function throws an error, it will be thrown by `run` too. The
* stacktrace will not be impacted by this call and the context will be exited.
* If the callback function throws an error, the error is thrown by `run()` too.
* The stacktrace is not impacted by this call and the context is exited.
*
* Example:
*
* ```js
* const store = { id: 2 };
* try {
* asyncLocalStorage.run(store, () => {
* asyncLocalStorage.getStore(); // Returns the store object
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns undefined
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
*/
// TODO: Apply generic vararg once available
run<R>(store: T, callback: (...args: any[]) => R, ...args: any[]): R;
run<R, TArgs extends any[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* This methods runs a function synchronously outside of a context and return its
* return value. The store is not accessible within the callback function or the
* asynchronous operations created within the callback.
* Runs a function synchronously outside of a context and returns its
* return value. The store is not accessible within the callback function or
* the asynchronous operations created within the callback. Any `getStore()`call done within the callback function will always return `undefined`.
*
* Optionally, arguments can be passed to the function. They will be passed to the
* callback function.
* The optional `args` are passed to the callback function.
*
* If the callback function throws an error, it will be thrown by `exit` too. The
* stacktrace will not be impacted by this call and the context will be
* re-entered.
* If the callback function throws an error, the error is thrown by `exit()` too.
* The stacktrace is not impacted by this call and the context is re-entered.
*
* Example:
*
* ```js
* // Within a call to run
* try {
* asyncLocalStorage.getStore(); // Returns the store object or value
* asyncLocalStorage.exit(() => {
* asyncLocalStorage.getStore(); // Returns undefined
* throw new Error();
* });
* } catch (e) {
* asyncLocalStorage.getStore(); // Returns the same object or value
* // The error will be caught here
* }
* ```
* @since v13.10.0, v12.17.0
* @experimental
*/
// TODO: Apply generic vararg once available
exit<R>(callback: (...args: any[]) => R, ...args: any[]): R;
exit<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R;
/**
* Calling `asyncLocalStorage.enterWith(store)` will transition into the context
* for the remainder of the current synchronous execution and will persist
* through any following asynchronous calls.
* Transitions into the context for the remainder of the current
* synchronous execution and then persists the store through any following
* asynchronous calls.
*
* Example:
*
* ```js
* const store = { id: 1 };
* // Replaces previous store with the given store object
* asyncLocalStorage.enterWith(store);
* asyncLocalStorage.getStore(); // Returns the store object
* someAsyncOperation(() => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
* ```
*
* This transition will continue for the _entire_ synchronous execution.
* This means that if, for example, the context is entered within an event
* handler subsequent event handlers will also run within that context unless
* specifically bound to another context with an `AsyncResource`. That is why`run()` should be preferred over `enterWith()` unless there are strong reasons
* to use the latter method.
*
* ```js
* const store = { id: 1 };
*
* emitter.on('my-event', () => {
* asyncLocalStorage.enterWith(store);
* });
* emitter.on('my-event', () => {
* asyncLocalStorage.getStore(); // Returns the same object
* });
*
* asyncLocalStorage.getStore(); // Returns undefined
* emitter.emit('my-event');
* asyncLocalStorage.getStore(); // Returns the same object
* ```
* @since v13.11.0, v12.17.0
* @experimental
*/
enterWith(store: T): void;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,64 @@
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/Steve-Mcl/monaco-editor-esm-i18n */
/**
* A single instance of Node.js runs in a single thread. To take advantage of
* multi-core systems, the user will sometimes want to launch a cluster of Node.js
* processes to handle the load.
*
* The cluster module allows easy creation of child processes that all share
* server ports.
*
* ```js
* import cluster from 'cluster';
* import http from 'http';
* import { cpus } from 'os';
* import process from 'process';
*
* const numCPUs = cpus().length;
*
* if (cluster.isPrimary) {
* console.log(`Primary ${process.pid} is running`);
*
* // Fork workers.
* for (let i = 0; i < numCPUs; i++) {
* cluster.fork();
* }
*
* cluster.on('exit', (worker, code, signal) => {
* console.log(`worker ${worker.process.pid} died`);
* });
* } else {
* // Workers can share any TCP connection
* // In this case it is an HTTP server
* http.createServer((req, res) => {
* res.writeHead(200);
* res.end('hello world\n');
* }).listen(8000);
*
* console.log(`Worker ${process.pid} started`);
* }
* ```
*
* Running Node.js will now share port 8000 between the workers:
*
* ```console
* $ node server.js
* Primary 3596 is running
* Worker 4324 started
* Worker 4520 started
* Worker 6056 started
* Worker 5644 started
* ```
*
* On Windows, it is not yet possible to set up a named pipe server in a worker.
* @see [source](https://github.com/nodejs/node/blob/v16.9.0/lib/cluster.js)
*/
declare module 'cluster' {
import * as child from 'child_process';
import EventEmitter = require('events');
import * as net from 'net';
// interfaces
interface ClusterSettings {
import * as child from 'node:child_process';
import EventEmitter = require('node:events');
import * as net from 'node:net';
export interface ClusterSettings {
execArgv?: string[] | undefined; // default: process.execArgv
exec?: string | undefined;
args?: string[] | undefined;
@@ -17,24 +68,214 @@ declare module 'cluster' {
gid?: number | undefined;
inspectPort?: number | (() => number) | undefined;
}
interface Address {
export interface Address {
address: string;
port: number;
addressType: number | "udp4" | "udp6"; // 4, 6, -1, "udp4", "udp6"
addressType: number | 'udp4' | 'udp6'; // 4, 6, -1, "udp4", "udp6"
}
class Worker extends EventEmitter {
/**
* A `Worker` object contains all public information and method about a worker.
* In the primary it can be obtained using `cluster.workers`. In a worker
* it can be obtained using `cluster.worker`.
* @since v0.7.0
*/
export class Worker extends EventEmitter {
/**
* Each new worker is given its own unique id, this id is stored in the`id`.
*
* While a worker is alive, this is the key that indexes it in`cluster.workers`.
* @since v0.8.0
*/
id: number;
/**
* All workers are created using `child_process.fork()`, the returned object
* from this function is stored as `.process`. In a worker, the global `process`is stored.
*
* See: `Child Process module`.
*
* Workers will call `process.exit(0)` if the `'disconnect'` event occurs
* on `process` and `.exitedAfterDisconnect` is not `true`. This protects against
* accidental disconnection.
* @since v0.7.0
*/
process: child.ChildProcess;
send(message: child.Serializable, sendHandle?: child.SendHandle, callback?: (error: Error | null) => void): boolean;
/**
* Send a message to a worker or primary, optionally with a handle.
*
* In the primary this sends a message to a specific worker. It is identical to `ChildProcess.send()`.
*
* In a worker this sends a message to the primary. It is identical to`process.send()`.
*
* This example will echo back all messages from the primary:
*
* ```js
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* worker.send('hi there');
*
* } else if (cluster.isWorker) {
* process.on('message', (msg) => {
* process.send(msg);
* });
* }
* ```
* @since v0.7.0
* @param options The `options` argument, if present, is an object used to parameterize the sending of certain types of handles. `options` supports the following properties:
*/
send(message: child.Serializable, callback?: (error: Error | null) => void): boolean;
send(message: child.Serializable, sendHandle: child.SendHandle, callback?: (error: Error | null) => void): boolean;
send(message: child.Serializable, sendHandle: child.SendHandle, options?: child.MessageOptions, callback?: (error: Error | null) => void): boolean;
/**
* This function will kill the worker. In the primary, it does this
* by disconnecting the `worker.process`, and once disconnected, killing
* with `signal`. In the worker, it does it by disconnecting the channel,
* and then exiting with code `0`.
*
* Because `kill()` attempts to gracefully disconnect the worker process, it is
* susceptible to waiting indefinitely for the disconnect to complete. For example,
* if the worker enters an infinite loop, a graceful disconnect will never occur.
* If the graceful disconnect behavior is not needed, use `worker.process.kill()`.
*
* Causes `.exitedAfterDisconnect` to be set.
*
* This method is aliased as `worker.destroy()` for backward compatibility.
*
* In a worker, `process.kill()` exists, but it is not this function;
* it is `kill()`.
* @since v0.9.12
* @param [signal='SIGTERM'] Name of the kill signal to send to the worker process.
*/
kill(signal?: string): void;
destroy(signal?: string): void;
/**
* In a worker, this function will close all servers, wait for the `'close'` event
* on those servers, and then disconnect the IPC channel.
*
* In the primary, an internal message is sent to the worker causing it to call`.disconnect()` on itself.
*
* Causes `.exitedAfterDisconnect` to be set.
*
* After a server is closed, it will no longer accept new connections,
* but connections may be accepted by any other listening worker. Existing
* connections will be allowed to close as usual. When no more connections exist,
* see `server.close()`, the IPC channel to the worker will close allowing it
* to die gracefully.
*
* The above applies _only_ to server connections, client connections are not
* automatically closed by workers, and disconnect does not wait for them to close
* before exiting.
*
* In a worker, `process.disconnect` exists, but it is not this function;
* it is `disconnect()`.
*
* Because long living server connections may block workers from disconnecting, it
* may be useful to send a message, so application specific actions may be taken to
* close them. It also may be useful to implement a timeout, killing a worker if
* the `'disconnect'` event has not been emitted after some time.
*
* ```js
* if (cluster.isPrimary) {
* const worker = cluster.fork();
* let timeout;
*
* worker.on('listening', (address) => {
* worker.send('shutdown');
* worker.disconnect();
* timeout = setTimeout(() => {
* worker.kill();
* }, 2000);
* });
*
* worker.on('disconnect', () => {
* clearTimeout(timeout);
* });
*
* } else if (cluster.isWorker) {
* const net = require('net');
* const server = net.createServer((socket) => {
* // Connections never end
* });
*
* server.listen(8000);
*
* process.on('message', (msg) => {
* if (msg === 'shutdown') {
* // Initiate graceful close of any connections to server
* }
* });
* }
* ```
* @since v0.7.7
* @return A reference to `worker`.
*/
disconnect(): void;
/**
* This function returns `true` if the worker is connected to its primary via its
* IPC channel, `false` otherwise. A worker is connected to its primary after it
* has been created. It is disconnected after the `'disconnect'` event is emitted.
* @since v0.11.14
*/
isConnected(): boolean;
/**
* This function returns `true` if the worker's process has terminated (either
* because of exiting or being signaled). Otherwise, it returns `false`.
*
* ```js
* import cluster from 'cluster';
* import http from 'http';
* import { cpus } from 'os';
* import process from 'process';
*
* const numCPUs = cpus().length;
*
* if (cluster.isPrimary) {
* console.log(`Primary ${process.pid} is running`);
*
* // Fork workers.
* for (let i = 0; i < numCPUs; i++) {
* cluster.fork();
* }
*
* cluster.on('fork', (worker) => {
* console.log('worker is dead:', worker.isDead());
* });
*
* cluster.on('exit', (worker, code, signal) => {
* console.log('worker is dead:', worker.isDead());
* });
* } else {
* // Workers can share any TCP connection. In this case, it is an HTTP server.
* http.createServer((req, res) => {
* res.writeHead(200);
* res.end(`Current process\n ${process.pid}`);
* process.kill(process.pid);
* }).listen(8000);
* }
* ```
* @since v0.11.14
*/
isDead(): boolean;
/**
* This property is `true` if the worker exited due to `.kill()` or`.disconnect()`. If the worker exited any other way, it is `false`. If the
* worker has not exited, it is `undefined`.
*
* The boolean `worker.exitedAfterDisconnect` allows distinguishing between
* voluntary and accidental exit, the primary may choose not to respawn a worker
* based on this value.
*
* ```js
* cluster.on('exit', (worker, code, signal) => {
* if (worker.exitedAfterDisconnect === true) {
* console.log('Oh, it was just voluntary no need to worry');
* }
* });
*
* // kill worker
* worker.kill();
* ```
* @since v6.0.0
*/
exitedAfterDisconnect: boolean;
/**
* events.EventEmitter
* 1. disconnect
@@ -45,69 +286,67 @@ declare module 'cluster' {
* 6. online
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: "disconnect", listener: () => void): this;
addListener(event: "error", listener: (error: Error) => void): this;
addListener(event: "exit", listener: (code: number, signal: string) => void): this;
addListener(event: "listening", listener: (address: Address) => void): this;
addListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: "online", listener: () => void): this;
addListener(event: 'disconnect', listener: () => void): this;
addListener(event: 'error', listener: (error: Error) => void): this;
addListener(event: 'exit', listener: (code: number, signal: string) => void): this;
addListener(event: 'listening', listener: (address: Address) => void): this;
addListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: 'online', listener: () => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: "disconnect"): boolean;
emit(event: "error", error: Error): boolean;
emit(event: "exit", code: number, signal: string): boolean;
emit(event: "listening", address: Address): boolean;
emit(event: "message", message: any, handle: net.Socket | net.Server): boolean;
emit(event: "online"): boolean;
emit(event: 'disconnect'): boolean;
emit(event: 'error', error: Error): boolean;
emit(event: 'exit', code: number, signal: string): boolean;
emit(event: 'listening', address: Address): boolean;
emit(event: 'message', message: any, handle: net.Socket | net.Server): boolean;
emit(event: 'online'): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: "disconnect", listener: () => void): this;
on(event: "error", listener: (error: Error) => void): this;
on(event: "exit", listener: (code: number, signal: string) => void): this;
on(event: "listening", listener: (address: Address) => void): this;
on(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: "online", listener: () => void): this;
on(event: 'disconnect', listener: () => void): this;
on(event: 'error', listener: (error: Error) => void): this;
on(event: 'exit', listener: (code: number, signal: string) => void): this;
on(event: 'listening', listener: (address: Address) => void): this;
on(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: 'online', listener: () => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: "disconnect", listener: () => void): this;
once(event: "error", listener: (error: Error) => void): this;
once(event: "exit", listener: (code: number, signal: string) => void): this;
once(event: "listening", listener: (address: Address) => void): this;
once(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: "online", listener: () => void): this;
once(event: 'disconnect', listener: () => void): this;
once(event: 'error', listener: (error: Error) => void): this;
once(event: 'exit', listener: (code: number, signal: string) => void): this;
once(event: 'listening', listener: (address: Address) => void): this;
once(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: 'online', listener: () => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "disconnect", listener: () => void): this;
prependListener(event: "error", listener: (error: Error) => void): this;
prependListener(event: "exit", listener: (code: number, signal: string) => void): this;
prependListener(event: "listening", listener: (address: Address) => void): this;
prependListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependListener(event: "online", listener: () => void): this;
prependListener(event: 'disconnect', listener: () => void): this;
prependListener(event: 'error', listener: (error: Error) => void): this;
prependListener(event: 'exit', listener: (code: number, signal: string) => void): this;
prependListener(event: 'listening', listener: (address: Address) => void): this;
prependListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependListener(event: 'online', listener: () => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: "disconnect", listener: () => void): this;
prependOnceListener(event: "error", listener: (error: Error) => void): this;
prependOnceListener(event: "exit", listener: (code: number, signal: string) => void): this;
prependOnceListener(event: "listening", listener: (address: Address) => void): this;
prependOnceListener(event: "message", listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: "online", listener: () => void): this;
prependOnceListener(event: 'disconnect', listener: () => void): this;
prependOnceListener(event: 'error', listener: (error: Error) => void): this;
prependOnceListener(event: 'exit', listener: (code: number, signal: string) => void): this;
prependOnceListener(event: 'listening', listener: (address: Address) => void): this;
prependOnceListener(event: 'message', listener: (message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: 'online', listener: () => void): this;
}
interface Cluster extends EventEmitter {
Worker: Worker;
export interface Cluster extends EventEmitter {
disconnect(callback?: () => void): void;
fork(env?: any): Worker;
isMaster: boolean;
isWorker: boolean;
/** @deprecated since v16.0.0 - use isPrimary. */
readonly isMaster: boolean;
readonly isPrimary: boolean;
readonly isWorker: boolean;
schedulingPolicy: number;
settings: ClusterSettings;
readonly settings: ClusterSettings;
/** @deprecated since v16.0.0 - use setupPrimary. */
setupMaster(settings?: ClusterSettings): void;
worker?: Worker | undefined;
workers?: NodeJS.Dict<Worker> | undefined;
/**
* `setupPrimary` is used to change the default 'fork' behavior. Once called, the settings will be present in cluster.settings.
*/
setupPrimary(settings?: ClusterSettings): void;
readonly worker?: Worker | undefined;
readonly workers?: NodeJS.Dict<Worker> | undefined;
readonly SCHED_NONE: number;
readonly SCHED_RR: number;
/**
* events.EventEmitter
* 1. disconnect
@@ -119,150 +358,60 @@ declare module 'cluster' {
* 7. setup
*/
addListener(event: string, listener: (...args: any[]) => void): this;
addListener(event: "disconnect", listener: (worker: Worker) => void): this;
addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this;
addListener(event: "fork", listener: (worker: Worker) => void): this;
addListener(event: "listening", listener: (worker: Worker, address: Address) => void): this;
addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: "online", listener: (worker: Worker) => void): this;
addListener(event: "setup", listener: (settings: ClusterSettings) => void): this;
addListener(event: 'disconnect', listener: (worker: Worker) => void): this;
addListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
addListener(event: 'fork', listener: (worker: Worker) => void): this;
addListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
addListener(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
addListener(event: 'online', listener: (worker: Worker) => void): this;
addListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
emit(event: string | symbol, ...args: any[]): boolean;
emit(event: "disconnect", worker: Worker): boolean;
emit(event: "exit", worker: Worker, code: number, signal: string): boolean;
emit(event: "fork", worker: Worker): boolean;
emit(event: "listening", worker: Worker, address: Address): boolean;
emit(event: "message", worker: Worker, message: any, handle: net.Socket | net.Server): boolean;
emit(event: "online", worker: Worker): boolean;
emit(event: "setup", settings: ClusterSettings): boolean;
emit(event: 'disconnect', worker: Worker): boolean;
emit(event: 'exit', worker: Worker, code: number, signal: string): boolean;
emit(event: 'fork', worker: Worker): boolean;
emit(event: 'listening', worker: Worker, address: Address): boolean;
emit(event: 'message', worker: Worker, message: any, handle: net.Socket | net.Server): boolean;
emit(event: 'online', worker: Worker): boolean;
emit(event: 'setup', settings: ClusterSettings): boolean;
on(event: string, listener: (...args: any[]) => void): this;
on(event: "disconnect", listener: (worker: Worker) => void): this;
on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this;
on(event: "fork", listener: (worker: Worker) => void): this;
on(event: "listening", listener: (worker: Worker, address: Address) => void): this;
on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: "online", listener: (worker: Worker) => void): this;
on(event: "setup", listener: (settings: ClusterSettings) => void): this;
on(event: 'disconnect', listener: (worker: Worker) => void): this;
on(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
on(event: 'fork', listener: (worker: Worker) => void): this;
on(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
on(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
on(event: 'online', listener: (worker: Worker) => void): this;
on(event: 'setup', listener: (settings: ClusterSettings) => void): this;
once(event: string, listener: (...args: any[]) => void): this;
once(event: "disconnect", listener: (worker: Worker) => void): this;
once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this;
once(event: "fork", listener: (worker: Worker) => void): this;
once(event: "listening", listener: (worker: Worker, address: Address) => void): this;
once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: "online", listener: (worker: Worker) => void): this;
once(event: "setup", listener: (settings: ClusterSettings) => void): this;
once(event: 'disconnect', listener: (worker: Worker) => void): this;
once(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
once(event: 'fork', listener: (worker: Worker) => void): this;
once(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
once(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
once(event: 'online', listener: (worker: Worker) => void): this;
once(event: 'setup', listener: (settings: ClusterSettings) => void): this;
prependListener(event: string, listener: (...args: any[]) => void): this;
prependListener(event: "disconnect", listener: (worker: Worker) => void): this;
prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this;
prependListener(event: "fork", listener: (worker: Worker) => void): this;
prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): this;
prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this; // the handle is a net.Socket or net.Server object, or undefined.
prependListener(event: "online", listener: (worker: Worker) => void): this;
prependListener(event: "setup", listener: (settings: ClusterSettings) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): this;
prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): this;
prependOnceListener(event: "fork", listener: (worker: Worker) => void): this;
prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): this;
prependListener(event: 'disconnect', listener: (worker: Worker) => void): this;
prependListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
prependListener(event: 'fork', listener: (worker: Worker) => void): this;
prependListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
// the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this;
prependOnceListener(event: "online", listener: (worker: Worker) => void): this;
prependOnceListener(event: "setup", listener: (settings: ClusterSettings) => void): this;
prependListener(event: 'message', listener: (worker: Worker, message: any, handle?: net.Socket | net.Server) => void): this;
prependListener(event: 'online', listener: (worker: Worker) => void): this;
prependListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
prependOnceListener(event: string, listener: (...args: any[]) => void): this;
prependOnceListener(event: 'disconnect', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'exit', listener: (worker: Worker, code: number, signal: string) => void): this;
prependOnceListener(event: 'fork', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'listening', listener: (worker: Worker, address: Address) => void): this;
// the handle is a net.Socket or net.Server object, or undefined.
prependOnceListener(event: 'message', listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): this;
prependOnceListener(event: 'online', listener: (worker: Worker) => void): this;
prependOnceListener(event: 'setup', listener: (settings: ClusterSettings) => void): this;
}
const SCHED_NONE: number;
const SCHED_RR: number;
function disconnect(callback?: () => void): void;
function fork(env?: any): Worker;
const isMaster: boolean;
const isWorker: boolean;
let schedulingPolicy: number;
const settings: ClusterSettings;
function setupMaster(settings?: ClusterSettings): void;
const worker: Worker;
const workers: NodeJS.Dict<Worker>;
/**
* events.EventEmitter
* 1. disconnect
* 2. exit
* 3. fork
* 4. listening
* 5. message
* 6. online
* 7. setup
*/
function addListener(event: string, listener: (...args: any[]) => void): Cluster;
function addListener(event: "disconnect", listener: (worker: Worker) => void): Cluster;
function addListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster;
function addListener(event: "fork", listener: (worker: Worker) => void): Cluster;
function addListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster;
// the handle is a net.Socket or net.Server object, or undefined.
function addListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster;
function addListener(event: "online", listener: (worker: Worker) => void): Cluster;
function addListener(event: "setup", listener: (settings: ClusterSettings) => void): Cluster;
function emit(event: string | symbol, ...args: any[]): boolean;
function emit(event: "disconnect", worker: Worker): boolean;
function emit(event: "exit", worker: Worker, code: number, signal: string): boolean;
function emit(event: "fork", worker: Worker): boolean;
function emit(event: "listening", worker: Worker, address: Address): boolean;
function emit(event: "message", worker: Worker, message: any, handle: net.Socket | net.Server): boolean;
function emit(event: "online", worker: Worker): boolean;
function emit(event: "setup", settings: ClusterSettings): boolean;
function on(event: string, listener: (...args: any[]) => void): Cluster;
function on(event: "disconnect", listener: (worker: Worker) => void): Cluster;
function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster;
function on(event: "fork", listener: (worker: Worker) => void): Cluster;
function on(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster;
function on(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined.
function on(event: "online", listener: (worker: Worker) => void): Cluster;
function on(event: "setup", listener: (settings: ClusterSettings) => void): Cluster;
function once(event: string, listener: (...args: any[]) => void): Cluster;
function once(event: "disconnect", listener: (worker: Worker) => void): Cluster;
function once(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster;
function once(event: "fork", listener: (worker: Worker) => void): Cluster;
function once(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster;
function once(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster; // the handle is a net.Socket or net.Server object, or undefined.
function once(event: "online", listener: (worker: Worker) => void): Cluster;
function once(event: "setup", listener: (settings: ClusterSettings) => void): Cluster;
function removeListener(event: string, listener: (...args: any[]) => void): Cluster;
function removeAllListeners(event?: string): Cluster;
function setMaxListeners(n: number): Cluster;
function getMaxListeners(): number;
function listeners(event: string): Function[];
function listenerCount(type: string): number;
function prependListener(event: string, listener: (...args: any[]) => void): Cluster;
function prependListener(event: "disconnect", listener: (worker: Worker) => void): Cluster;
function prependListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster;
function prependListener(event: "fork", listener: (worker: Worker) => void): Cluster;
function prependListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster;
// the handle is a net.Socket or net.Server object, or undefined.
function prependListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster;
function prependListener(event: "online", listener: (worker: Worker) => void): Cluster;
function prependListener(event: "setup", listener: (settings: ClusterSettings) => void): Cluster;
function prependOnceListener(event: string, listener: (...args: any[]) => void): Cluster;
function prependOnceListener(event: "disconnect", listener: (worker: Worker) => void): Cluster;
function prependOnceListener(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): Cluster;
function prependOnceListener(event: "fork", listener: (worker: Worker) => void): Cluster;
function prependOnceListener(event: "listening", listener: (worker: Worker, address: Address) => void): Cluster;
// the handle is a net.Socket or net.Server object, or undefined.
function prependOnceListener(event: "message", listener: (worker: Worker, message: any, handle: net.Socket | net.Server) => void): Cluster;
function prependOnceListener(event: "online", listener: (worker: Worker) => void): Cluster;
function prependOnceListener(event: "setup", listener: (settings: ClusterSettings) => void): Cluster;
function eventNames(): string[];
const cluster: Cluster;
export default cluster;
}
declare module 'node:cluster' {
export * from 'cluster';
export { default as default } from 'cluster';
}

View File

@@ -1,100 +1,321 @@
/* NOTE: Do not edit directly! This file is generated using `npm run update-types` in https://github.com/Steve-Mcl/monaco-editor-esm-i18n */
/**
* The `console` module provides a simple debugging console that is similar to the
* JavaScript console mechanism provided by web browsers.
*
* The module exports two specific components:
*
* * A `Console` class with methods such as `console.log()`, `console.error()` and`console.warn()` that can be used to write to any Node.js stream.
* * A global `console` instance configured to write to `process.stdout` and `process.stderr`. The global `console` can be used without calling`require('console')`.
*
* _**Warning**_: The global console object's methods are neither consistently
* synchronous like the browser APIs they resemble, nor are they consistently
* asynchronous like all other Node.js streams. See the `note on process I/O` for
* more information.
*
* Example using the global `console`:
*
* ```js
* console.log('hello world');
* // Prints: hello world, to stdout
* console.log('hello %s', 'world');
* // Prints: hello world, to stdout
* console.error(new Error('Whoops, something bad happened'));
* // Prints error message and stack trace to stderr:
* // Error: Whoops, something bad happened
* // at [eval]:5:15
* // at Script.runInThisContext (node:vm:132:18)
* // at Object.runInThisContext (node:vm:309:38)
* // at node:internal/process/execution:77:19
* // at [eval]-wrapper:6:22
* // at evalScript (node:internal/process/execution:76:60)
* // at node:internal/main/eval_string:23:3
*
* const name = 'Will Robinson';
* console.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to stderr
* ```
*
* Example using the `Console` class:
*
* ```js
* const out = getStreamSomehow();
* const err = getStreamSomehow();
* const myConsole = new console.Console(out, err);
*
* myConsole.log('hello world');
* // Prints: hello world, to out
* myConsole.log('hello %s', 'world');
* // Prints: hello world, to out
* myConsole.error(new Error('Whoops, something bad happened'));
* // Prints: [Error: Whoops, something bad happened], to err
*
* const name = 'Will Robinson';
* myConsole.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to err
* ```
* @see [source](https://github.com/nodejs/node/blob/v16.9.0/lib/console.js)
*/
declare module 'console' {
import console = require('node:console');
export = console;
}
declare module 'node:console' {
import { InspectOptions } from 'util';
import { InspectOptions } from 'node:util';
global {
// This needs to be global to avoid TS2403 in case lib.dom.d.ts is present in the same build
interface Console {
Console: NodeJS.ConsoleConstructor;
Console: console.ConsoleConstructor;
/**
* A simple assertion test that verifies whether `value` is truthy.
* If it is not, an `AssertionError` is thrown.
* If provided, the error `message` is formatted using `util.format()` and used as the error message.
* `console.assert()` writes a message if `value` is [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) or omitted. It only
* writes a message and does not otherwise affect execution. The output always
* starts with `"Assertion failed"`. If provided, `message` is formatted using `util.format()`.
*
* If `value` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy), nothing happens.
*
* ```js
* console.assert(true, 'does nothing');
*
* console.assert(false, 'Whoops %s work', 'didn\'t');
* // Assertion failed: Whoops didn't work
*
* console.assert();
* // Assertion failed
* ```
* @since v0.1.101
* @param value The value tested for being truthy.
* @param message All arguments besides `value` are used as error message.
*/
assert(value: any, message?: string, ...optionalParams: any[]): void;
/**
* When `stdout` is a TTY, calling `console.clear()` will attempt to clear the TTY.
* When `stdout` is not a TTY, this method does nothing.
* When `stdout` is a TTY, calling `console.clear()` will attempt to clear the
* TTY. When `stdout` is not a TTY, this method does nothing.
*
* The specific operation of `console.clear()` can vary across operating systems
* and terminal types. For most Linux operating systems, `console.clear()`operates similarly to the `clear` shell command. On Windows, `console.clear()`will clear only the output in the
* current terminal viewport for the Node.js
* binary.
* @since v8.3.0
*/
clear(): void;
/**
* Maintains an internal counter specific to `label` and outputs to `stdout` the number of times `console.count()` has been called with the given `label`.
* Maintains an internal counter specific to `label` and outputs to `stdout` the
* number of times `console.count()` has been called with the given `label`.
*
* ```js
* > console.count()
* default: 1
* undefined
* > console.count('default')
* default: 2
* undefined
* > console.count('abc')
* abc: 1
* undefined
* > console.count('xyz')
* xyz: 1
* undefined
* > console.count('abc')
* abc: 2
* undefined
* > console.count()
* default: 3
* undefined
* >
* ```
* @since v8.3.0
* @param label The display label for the counter.
*/
count(label?: string): void;
/**
* Resets the internal counter specific to `label`.
*
* ```js
* > console.count('abc');
* abc: 1
* undefined
* > console.countReset('abc');
* undefined
* > console.count('abc');
* abc: 1
* undefined
* >
* ```
* @since v8.3.0
* @param label The display label for the counter.
*/
countReset(label?: string): void;
/**
* The `console.debug()` function is an alias for {@link console.log}.
* The `console.debug()` function is an alias for {@link log}.
* @since v8.0.0
*/
debug(message?: any, ...optionalParams: any[]): void;
/**
* Uses {@link util.inspect} on `obj` and prints the resulting string to `stdout`.
* Uses `util.inspect()` on `obj` and prints the resulting string to `stdout`.
* This function bypasses any custom `inspect()` function defined on `obj`.
* @since v0.1.101
*/
dir(obj: any, options?: InspectOptions): void;
/**
* This method calls {@link console.log} passing it the arguments received. Please note that this method does not produce any XML formatting
* This method calls `console.log()` passing it the arguments received.
* This method does not produce any XML formatting.
* @since v8.0.0
*/
dirxml(...data: any[]): void;
/**
* Prints to `stderr` with newline.
* Prints to `stderr` with newline. Multiple arguments can be passed, with the
* first used as the primary message and all additional used as substitution
* values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to `util.format()`).
*
* ```js
* const code = 5;
* console.error('error #%d', code);
* // Prints: error #5, to stderr
* console.error('error', code);
* // Prints: error 5, to stderr
* ```
*
* If formatting elements (e.g. `%d`) are not found in the first string then `util.inspect()` is called on each argument and the resulting string
* values are concatenated. See `util.format()` for more information.
* @since v0.1.100
*/
error(message?: any, ...optionalParams: any[]): void;
/**
* Increases indentation of subsequent lines by two spaces.
* If one or more `label`s are provided, those are printed first without the additional indentation.
* Increases indentation of subsequent lines by spaces for `groupIndentation`length.
*
* If one or more `label`s are provided, those are printed first without the
* additional indentation.
* @since v8.5.0
*/
group(...label: any[]): void;
/**
* The `console.groupCollapsed()` function is an alias for {@link console.group}.
* An alias for {@link group}.
* @since v8.5.0
*/
groupCollapsed(...label: any[]): void;
/**
* Decreases indentation of subsequent lines by two spaces.
* Decreases indentation of subsequent lines by spaces for `groupIndentation`length.
* @since v8.5.0
*/
groupEnd(): void;
/**
* The {@link console.info} function is an alias for {@link console.log}.
* The `console.info()` function is an alias for {@link log}.
* @since v0.1.100
*/
info(message?: any, ...optionalParams: any[]): void;
/**
* Prints to `stdout` with newline.
* Prints to `stdout` with newline. Multiple arguments can be passed, with the
* first used as the primary message and all additional used as substitution
* values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to `util.format()`).
*
* ```js
* const count = 5;
* console.log('count: %d', count);
* // Prints: count: 5, to stdout
* console.log('count:', count);
* // Prints: count: 5, to stdout
* ```
*
* See `util.format()` for more information.
* @since v0.1.100
*/
log(message?: any, ...optionalParams: any[]): void;
/**
* This method does not display anything unless used in the inspector.
* Prints to `stdout` the array `array` formatted as a table.
* Try to construct a table with the columns of the properties of `tabularData`(or use `properties`) and rows of `tabularData` and log it. Falls back to just
* logging the argument if it cant be parsed as tabular.
*
* ```js
* // These can't be parsed as tabular data
* console.table(Symbol());
* // Symbol()
*
* console.table(undefined);
* // undefined
*
* console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }]);
* // ┌─────────┬─────┬─────┐
* // │ (index) │ a │ b │
* // ├─────────┼─────┼─────┤
* // │ 0 │ 1 │ 'Y' │
* // │ 1 │ 'Z' │ 2 │
* // └─────────┴─────┴─────┘
*
* console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a']);
* // ┌─────────┬─────┐
* // │ (index) │ a │
* // ├─────────┼─────┤
* // │ 0 │ 1 │
* // │ 1 │ 'Z' │
* // └─────────┴─────┘
* ```
* @since v10.0.0
* @param properties Alternate properties for constructing the table.
*/
table(tabularData: any, properties?: ReadonlyArray<string>): void;
/**
* Starts a timer that can be used to compute the duration of an operation. Timers are identified by a unique `label`.
* Starts a timer that can be used to compute the duration of an operation. Timers
* are identified by a unique `label`. Use the same `label` when calling {@link timeEnd} to stop the timer and output the elapsed time in
* suitable time units to `stdout`. For example, if the elapsed
* time is 3869ms, `console.timeEnd()` displays "3.869s".
* @since v0.1.104
*/
time(label?: string): void;
/**
* Stops a timer that was previously started by calling {@link console.time} and prints the result to `stdout`.
* Stops a timer that was previously started by calling {@link time} and
* prints the result to `stdout`:
*
* ```js
* console.time('100-elements');
* for (let i = 0; i < 100; i++) {}
* console.timeEnd('100-elements');
* // prints 100-elements: 225.438ms
* ```
* @since v0.1.104
*/
timeEnd(label?: string): void;
/**
* For a timer that was previously started by calling {@link console.time}, prints the elapsed time and other `data` arguments to `stdout`.
* For a timer that was previously started by calling {@link time}, prints
* the elapsed time and other `data` arguments to `stdout`:
*
* ```js
* console.time('process');
* const value = expensiveProcess1(); // Returns 42
* console.timeLog('process', value);
* // Prints "process: 365.227ms 42".
* doExpensiveProcess2(value);
* console.timeEnd('process');
* ```
* @since v10.7.0
*/
timeLog(label?: string, ...data: any[]): void;
/**
* Prints to `stderr` the string 'Trace :', followed by the {@link util.format} formatted message and stack trace to the current position in the code.
* Prints to `stderr` the string `'Trace: '`, followed by the `util.format()` formatted message and stack trace to the current position in the code.
*
* ```js
* console.trace('Show me');
* // Prints: (stack trace will vary based on where trace is called)
* // Trace: Show me
* // at repl:2:9
* // at REPLServer.defaultEval (repl.js:248:27)
* // at bound (domain.js:287:14)
* // at REPLServer.runBound [as eval] (domain.js:300:12)
* // at REPLServer.<anonymous> (repl.js:412:12)
* // at emitOne (events.js:82:20)
* // at REPLServer.emit (events.js:169:7)
* // at REPLServer.Interface._onLine (readline.js:210:10)
* // at REPLServer.Interface._line (readline.js:549:8)
* // at REPLServer.Interface._ttyWrite (readline.js:826:14)
* ```
* @since v0.1.104
*/
trace(message?: any, ...optionalParams: any[]): void;
/**
* The {@link console.warn} function is an alias for {@link console.error}.
* The `console.warn()` function is an alias for {@link error}.
* @since v0.1.100
*/
warn(message?: any, ...optionalParams: any[]): void;
// --- Inspector mode only ---
/**
* This method does not display anything unless used in the inspector.
@@ -112,13 +333,67 @@ declare module 'node:console' {
*/
timeStamp(label?: string): void;
}
var console: Console;
namespace NodeJS {
/**
* The `console` module provides a simple debugging console that is similar to the
* JavaScript console mechanism provided by web browsers.
*
* The module exports two specific components:
*
* * A `Console` class with methods such as `console.log()`, `console.error()` and`console.warn()` that can be used to write to any Node.js stream.
* * A global `console` instance configured to write to `process.stdout` and `process.stderr`. The global `console` can be used without calling`require('console')`.
*
* _**Warning**_: The global console object's methods are neither consistently
* synchronous like the browser APIs they resemble, nor are they consistently
* asynchronous like all other Node.js streams. See the `note on process I/O` for
* more information.
*
* Example using the global `console`:
*
* ```js
* console.log('hello world');
* // Prints: hello world, to stdout
* console.log('hello %s', 'world');
* // Prints: hello world, to stdout
* console.error(new Error('Whoops, something bad happened'));
* // Prints error message and stack trace to stderr:
* // Error: Whoops, something bad happened
* // at [eval]:5:15
* // at Script.runInThisContext (node:vm:132:18)
* // at Object.runInThisContext (node:vm:309:38)
* // at node:internal/process/execution:77:19
* // at [eval]-wrapper:6:22
* // at evalScript (node:internal/process/execution:76:60)
* // at node:internal/main/eval_string:23:3
*
* const name = 'Will Robinson';
* console.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to stderr
* ```
*
* Example using the `Console` class:
*
* ```js
* const out = getStreamSomehow();
* const err = getStreamSomehow();
* const myConsole = new console.Console(out, err);
*
* myConsole.log('hello world');
* // Prints: hello world, to out
* myConsole.log('hello %s', 'world');
* // Prints: hello world, to out
* myConsole.error(new Error('Whoops, something bad happened'));
* // Prints: [Error: Whoops, something bad happened], to err
*
* const name = 'Will Robinson';
* myConsole.warn(`Danger ${name}! Danger!`);
* // Prints: Danger Will Robinson! Danger!, to err
* ```
* @see [source](https://github.com/nodejs/node/blob/v16.4.2/lib/console.js)
*/
namespace console {
interface ConsoleConstructorOptions {
stdout: WritableStream;
stderr?: WritableStream | undefined;
stdout: NodeJS.WritableStream;
stderr?: NodeJS.WritableStream | undefined;
ignoreErrors?: boolean | undefined;
colorMode?: boolean | 'auto' | undefined;
inspectOptions?: InspectOptions | undefined;
@@ -126,20 +401,15 @@ declare module 'node:console' {
* Set group indentation
* @default 2
*/
groupIndentation?: number | undefined;
groupIndentation?: number | undefined;
}
interface ConsoleConstructor {
prototype: Console;
new(stdout: WritableStream, stderr?: WritableStream, ignoreErrors?: boolean): Console;
new(options: ConsoleConstructorOptions): Console;
}
interface Global {
console: typeof console;
new (stdout: NodeJS.WritableStream, stderr?: NodeJS.WritableStream, ignoreErrors?: boolean): Console;
new (options: ConsoleConstructorOptions): Console;
}
}
var console: Console;
}
export = console;
export = globalThis.console;
}

File diff suppressed because it is too large Load Diff

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