Compare commits

...

268 Commits

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

* chore: Fix typos in settings.js

* chore: Use consistent terminology in settings.js
2023-01-19 09:28:46 +00:00
Kazuhito Yokoi
8f27dae7ea Add Japanese translations for v3.0.3 2023-01-16 00:56:39 +09:00
Dave Conway-Jones
94ae511a6d fix tcp to replace all in newline substitution
to close #3989
2023-01-09 09:26:24 +00:00
Hiroyasu Nishiyama
038f75e48f add support for mermaid diagram to markdown editor 2023-01-05 17:25:16 +09:00
Hiroyasu Nishiyama
b9fe4c5cd3 merge upstream/dev 2023-01-05 10:29:45 +09:00
Hiroyasu Nishiyama
7e8b7602b4 add support for inline image drag and drop to markdown editor 2023-01-05 10:28:48 +09:00
Dirk-Jan Faber
7c306a8430 Comment node: Clarify where the text will appear
The text will appear in the information tab of the side panel and not in the help tab of the side panel.
2023-01-03 13:41:31 +01:00
Nick O'Leary
dd2bc44c2d Merge branch 'dev' into dev-jpn 2023-01-01 22:38:20 +00:00
Nick O'Leary
74794fea09 Split node: avoid duplicate done call for buffer split
Fixes #3982
2023-01-01 22:21:49 +00:00
Nick O'Leary
1efd1a52a7 Ensure modules containing plugins and nodes are loaded properly
Fixes #3523
2023-01-01 19:36:49 +00:00
Nick O'Leary
928131cf08 Merge pull request #3941 from node-red-hitachi/global-env-var
add global environment variable feature
2023-01-01 14:10:15 +00:00
Nick O'Leary
4d202a7a37 Merge pull request #3971 from node-red/fix-cred-on-global
Ensure credentials object is removed before returning node in getFlow request
2023-01-01 14:09:54 +00:00
Nick O'Leary
e0d71abdc6 Merge pull request #3992 from node-red/fix-mqtt-reconnect
Fix mqtt nodes not reconnecting on modified-flows deploy
2023-01-01 14:09:33 +00:00
Nick O'Leary
550eb6ee2f Merge pull request #3995 from bonanitech/radialMenu
Let themes change radialMenu text colors
2023-01-01 14:06:00 +00:00
Kazuhito Yokoi
a661bc1d23 Add Japanese translation for v3.1.0-beta.0 2022-12-31 00:41:35 +09:00
Mauricio Bonani
f737162697 Let themes change radialMenu text colors 2022-12-29 09:47:05 -05:00
Nick O'Leary
ce57ba80eb Fix mqtt nodes not reconnected on modified-flows deploy 2022-12-27 14:56:32 +00:00
Hiroyasu Nishiyama
99bd957ea0 Resolve merge conflict 2022-12-27 23:45:25 +09:00
Nick O'Leary
270eb56718 Merge pull request #3916 from kazuhitoyokoi/dev
Add Japanese translation for v3.1.0-beta.0
2022-12-27 14:16:02 +00:00
Nick O'Leary
b3ce0c0079 Merge pull request #3987 from kazuhitoyokoi/master-fixproject
Ignore commit error in project feature
2022-12-27 14:15:47 +00:00
Nick O'Leary
e6cee58e0d Merge pull request #3974 from Steve-Mcl/remember-export-format
Remember compact/pretty flow export user choice
2022-12-27 14:09:47 +00:00
Nick O'Leary
4adc6b269c Merge pull request #3980 from kazuhitoyokoi/master-fixpalette
Hide subflow category after deleting subflow
2022-12-27 13:47:58 +00:00
Nick O'Leary
920b0178ec Merge pull request #3981 from kazuhitoyokoi/master-fixtypo
Fix typo in 25-status.html
2022-12-27 13:43:35 +00:00
Nick O'Leary
3583b40e02 Merge pull request #3990 from node-red/csv-change-to-replaceAll
Csv change replace to replaceAll
2022-12-27 13:43:12 +00:00
Nick O'Leary
7870830367 Merge pull request #3991 from hardillb/http-request-form-array
Support form-data arrays
2022-12-27 13:42:33 +00:00
Ben Hardill
4c1d7ad2d2 Merge branch 'master' into http-request-form-array 2022-12-24 20:37:33 +00:00
Ben Hardill
661b07c856 Add tests 2022-12-24 20:35:51 +00:00
Ben Hardill
5670bd8265 Support form-data arrays 2022-12-24 19:32:33 +00:00
Dave Conway-Jones
93a1911232 CSV - Add note about msg.reset to info page
to close #3976
2022-12-19 21:26:20 +00:00
Dave Conway-Jones
2429191838 CSV - swap to regex replace for node14 support 2022-12-19 13:48:21 +00:00
Dave Conway-Jones
f6901cd19f CSV node replace replace with replaceAll just in case
mentioned in Issue #3989
2022-12-19 09:50:29 +00:00
Kazuhito Yokoi
156c3984a7 Ignore commit error in project feature 2022-12-12 01:23:00 +09:00
Kazuhito Yokoi
f91af2153a Fix typo in 25-status.html 2022-12-10 00:33:40 +09:00
Kazuhito Yokoi
805f8a5ee7 Hide subflow category after deleting subflow 2022-12-08 23:43:47 +09:00
Hiroyasu Nishiyama
2ab8121a4a add description to global-config settings 2022-12-06 21:12:52 +09:00
Hiroyasu Nishiyama
601a4ec70d Add hasUsers to global-config 2022-12-06 10:33:10 +09:00
Hiroyasu Nishiyama
707b831c30 Update packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 10:25:38 +09:00
Hiroyasu Nishiyama
72ae375e44 Update packages/node_modules/@node-red/nodes/core/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 10:25:25 +09:00
Hiroyasu Nishiyama
3c1ddb5c9d Update packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:32:04 +09:00
Hiroyasu Nishiyama
817db23146 Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:29:51 +09:00
Hiroyasu Nishiyama
a8c820f558 Update packages/node_modules/@node-red/nodes/locales/ja/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:29:45 +09:00
Hiroyasu Nishiyama
6d09c81f11 Update packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:27:17 +09:00
Hiroyasu Nishiyama
192e537e5d Update packages/node_modules/@node-red/editor-client/locales/ja/editor.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-06 09:27:02 +09:00
Steve-Mcl
7b52ef34be Remember compact/pretty flow export user choice
closes #3849
2022-12-05 22:17:05 +00:00
Kazuhito Yokoi
7117472e73 Add Japanese translation for range node 2022-12-05 23:13:50 +09:00
Kazuhito Yokoi
c24b123917 Add Japanese translation for editor actions 2022-12-05 23:11:56 +09:00
Hiroyasu Nishiyama
9eb8cf121c Update Japanese message reflecting English message update 2022-12-05 11:29:37 +09:00
Hiroyasu Nishiyama
41ef9ae010 Update test/nodes/core/common/91-global-config_spec.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:29 +09:00
Hiroyasu Nishiyama
1674bbbde9 Update packages/node_modules/@node-red/nodes/locales/ja/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:20 +09:00
Hiroyasu Nishiyama
0fb739f7cd Update packages/node_modules/@node-red/nodes/locales/ja/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:17:04 +09:00
Hiroyasu Nishiyama
169fa940e4 Update packages/node_modules/@node-red/nodes/locales/en-US/messages.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:16:53 +09:00
Hiroyasu Nishiyama
c9664cc425 Update packages/node_modules/@node-red/nodes/locales/en-US/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:16:35 +09:00
Hiroyasu Nishiyama
e61cdff655 Update packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:15:43 +09:00
Hiroyasu Nishiyama
a479b8a5d7 Update packages/node_modules/@node-red/nodes/core/common/91-global-config.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2022-12-05 11:15:32 +09:00
Kazuhito Yokoi
26462e684b Merge branch 'node-red:dev' into dev 2022-12-05 01:08:45 +09:00
Nick O'Leary
113d42ef35 Merge pull request #3970 from node-red/prevent-text-selection
Prevent dbl-click opening node edit dialog with text selected
2022-12-03 23:34:57 +00:00
Nick O'Leary
c18018f017 Ensure credentials object is removed before returning node in getFlow request
Fixes #3953
2022-12-03 23:33:33 +00:00
Nick O'Leary
e804addf0a Prevent dbl-click opening node edit dialog with text selected
Fixes #3958
2022-12-03 23:13:44 +00:00
Nick O'Leary
04cea003b9 Merge pull request #3938 from node-red/locking-flows
Locking flows
2022-12-03 23:01:58 +00:00
Nick O'Leary
65fcc56a56 Merge pull request #3968 from node-red/context-menu-updates
Context menu updates
2022-12-03 23:01:37 +00:00
Nick O'Leary
c065d253e9 Merge pull request #3969 from node-red/bump-deps
Update dependencies
2022-12-03 23:01:10 +00:00
Nick O'Leary
4bb2b91ee6 Merge pull request #3966 from Steve-Mcl/fix-mqtt-single-subscription
fix single subscription mqtt node status
2022-12-03 22:47:42 +00:00
Nick O'Leary
b3f761776d Update dependencies 2022-12-03 22:43:03 +00:00
Nick O'Leary
dce1cccbde Allow subflow to be edited if instance exists on locked flow 2022-12-03 22:30:35 +00:00
Nick O'Leary
3630056ed8 Fix RED.nodes.clear() when handling locked flows 2022-12-03 21:44:33 +00:00
Nick O'Leary
2d6e1d7089 NLS updates for context menu 2022-12-03 21:29:27 +00:00
Nick O'Leary
71db79ba53 More context menu options 2022-12-03 21:16:57 +00:00
Steve-Mcl
5bb66ed7d4 fix single subscription mqtt node status 2022-12-01 13:08:48 +00:00
Nick O'Leary
14e74afb07 Merge pull request #3909 from node-red/warn-invalid-msg
Add check that node sends object rather than primitive type
2022-11-30 22:28:09 +00:00
Nick O'Leary
c4e216f839 Merge pull request #3912 from node-red/handle_username_spaces
Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname
2022-11-30 22:18:24 +00:00
Nick O'Leary
752fdfedf2 Merge pull request #3935 from node-red/Add-drop-mode-to-range-node
Add drop mode to range node
2022-11-30 22:17:02 +00:00
Nick O'Leary
2a49e7c8ef Merge pull request #3908 from node-red/add-httpheaders
Ensure msg.req.headers is enumerable
2022-11-30 22:15:23 +00:00
Nick O'Leary
02af01d2ca Merge pull request #3867 from Steve-Mcl/improve-nodesdir-scan
Fix nodesDir scan when node package has js/html in sub dir to package.json
2022-11-30 22:15:00 +00:00
Nick O'Leary
07c05c1f2a Merge pull request #3930 from node-red/tab-context-menu
Improve UX around hiding flows via context menu
2022-11-30 22:13:54 +00:00
Nick O'Leary
ee4af4c7bf Merge pull request #3920 from node-red/CSV-header-props-with-quotes
CSV node check header properties for ' and "
2022-11-30 22:10:40 +00:00
Nick O'Leary
3d565e74a5 Merge pull request #3917 from kazuhitoyokoi/master-fixpermission
Fix file permissions
2022-11-30 22:10:03 +00:00
Nick O'Leary
3cb84222f8 Merge pull request #3921 from node-red/fix-group-unknown
Handle replacing unknown node inside group or subflow
2022-11-30 22:09:35 +00:00
Nick O'Leary
1b013bb73b Merge pull request #3949 from Steveorevo/master
Fix #3939, red border red-ui-typedInput-container
2022-11-30 22:07:04 +00:00
Nick O'Leary
fd54e625d5 Merge pull request #3965 from we11adam/master
fix: fix typo in catch.html
2022-11-30 21:40:34 +00:00
Adam Lau
77f6412d3b fix: fix typo in catch.html 2022-11-30 17:51:13 +08:00
Stephen J. Carnam
c81cd5450f Support for PHP syntax highlight 2022-11-09 16:45:32 -08:00
Steveorevo
0b663abe50 Fix #3939, red border red-ui-typedInput-container 2022-11-09 10:33:05 -08:00
Nick O'Leary
3b27fb2aa7 Merge branch 'dev' into dev 2022-11-07 23:16:14 +00:00
Nick O'Leary
6bd67ae68c Merge pull request #3944 from node-red-hitachi/fix-deploy-locked-flow
fix deployment of locked flow
2022-11-07 23:13:03 +00:00
Nick O'Leary
14c362d4ba Merge pull request #3942 from node-red-hitachi/fix-watch-test
fix watch node test on MacOS/ARM
2022-11-07 21:12:55 +00:00
Nick O'Leary
f28bc1bff7 Remove jshint warning 2022-11-07 21:11:58 +00:00
Nick O'Leary
de8a5ea262 Merge pull request #3945 from node-red-hitachi/fix-jshint-es-version
fix to allow es11 for jshint check
2022-11-07 21:10:26 +00:00
Nick O'Leary
339013434b Merge pull request #3946 from node-red-hitachi/i18n-item-url-copy-notification
i18n item URL copy notification & add Japanese message
2022-11-07 21:07:57 +00:00
Nick O'Leary
8a3ad331d2 Merge pull request #3947 from node-red-hitachi/add-item-url-Japanese-action-message
add Japanese message for item url copy actions
2022-11-07 21:07:38 +00:00
Hiroyasu Nishiyama
e3892dc26d add Japanese message for item url copy actions 2022-11-07 16:07:46 +09:00
Hiroyasu Nishiyama
b95df6d883 i18n item URL copy notification & add Japanese message 2022-11-07 15:47:19 +09:00
Hiroyasu Nishiyama
11ad03b21e fix to allow es11 for jshint check 2022-11-07 10:42:07 +09:00
Hiroyasu Nishiyama
9cb474ea9c fix deployment of locked flow 2022-11-07 09:40:36 +09:00
Hiroyasu Nishiyama
fce43b4e1d fix condition for platform check 2022-11-05 14:50:14 +09:00
Hiroyasu Nishiyama
1d547500e8 fix watch node test on MacOS/ARM 2022-11-05 14:30:32 +09:00
Hiroyasu Nishiyama
f23d0480e4 add global environment variable feature 2022-11-04 18:42:51 +09:00
Nick O'Leary
fe9c630572 Prevent deleting subflow if instance on locked tab 2022-11-01 11:42:40 +00:00
Nick O'Leary
ce94226c3c Disable subflow/flow menu options if active is locked 2022-11-01 11:29:23 +00:00
Nick O'Leary
f12d36b5ed Locking flows fixes and context menu options 2022-11-01 10:48:48 +00:00
Nick O'Leary
3cb5259494 Initial locking flows UX 2022-11-01 10:37:18 +00:00
Nick O'Leary
a351cd9d9f Add move-to-start/end and better subflow menu options 2022-11-01 10:35:57 +00:00
Nick O'Leary
d8e01584f3 Remove add-flow-to-right option if clicked in tab bar 2022-10-31 20:20:05 +00:00
Kazuhito Yokoi
dd76840568 Fix uncleared translations in change node 2022-11-01 01:09:06 +09:00
Dave Conway-Jones
94690fad7a Merge branch 'CSV-header-props-with-quotes' of https://github.com/node-red/node-red into CSV-header-props-with-quotes 2022-10-29 17:36:17 +01:00
Dave Conway-Jones
d693af9615 CSV node check header properties for ' and "
and add test
to close #3919
2022-10-29 17:35:45 +01:00
Dave Conway-Jones
4cc18c25fe Add drop mode to range node
and include tests
2022-10-29 17:34:29 +01:00
Kazuhito Yokoi
8b398f49c0 Merge branch 'node-red:master' into master-fixpermission 2022-10-29 23:51:05 +09:00
Nick O'Leary
902ce68164 Merge branch 'master' into fix-group-unknown 2022-10-26 00:52:34 +01:00
Nick O'Leary
cd0474ce7b Update packages/node_modules/@node-red/editor-client/src/js/nodes.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-10-26 00:51:41 +01:00
Nick O'Leary
946def022f Merge pull request #3922 from node-red/fix-httpreq-tests
Fix httprequest tests to be more lenient on error message
2022-10-26 00:40:47 +01:00
Nick O'Leary
fb499be979 Add context menu to tab bar 2022-10-25 23:44:59 +01:00
Nick O'Leary
c62a101635 Fix httprequest tests to be more lenient on error message 2022-10-16 23:10:57 +01:00
Nick O'Leary
69a575097d Remove debug 2022-10-16 22:47:34 +01:00
Nick O'Leary
b40551a8fa Handle replacing unknown node inside group or subflow 2022-10-16 22:38:11 +01:00
Dave Conway-Jones
5b27bcd781 CSV node check header properties for ' and "
and add test
to close #3919
2022-10-16 18:05:21 +01:00
Kazuhito Yokoi
75725a38df Fix file permission 2022-10-12 23:50:33 +09:00
Kazuhito Yokoi
c4e277853c Add Japanese translation for button of node URL 2022-10-12 23:24:21 +09:00
Nick O'Leary
24b055b1b8 Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname 2022-10-04 15:44:29 +01:00
Nick O'Leary
7da3773f7f Merge pull request #3898 from node-red/delay-flush-reset
let delay node handle both flush then reset
2022-10-04 15:39:05 +01:00
Nick O'Leary
32999ffa84 Merge pull request #3906 from node-red/Fix-for-csv-undefined-property
Fix for CSV undefined property
2022-10-04 15:38:37 +01:00
Nick O'Leary
f06c53f1f1 Merge pull request #3905 from node-red/mqtt-followups
Fix birth topic handling in MQTT node
2022-10-04 15:36:49 +01:00
Nick O'Leary
a9eec28360 Merge pull request #3884 from node-red/fix-auto-complete
Fix autocomplete entry for responseUrl
2022-10-04 15:35:10 +01:00
Nick O'Leary
5cda972872 Merge pull request #3890 from kazuhitoyokoi/master-fixmqttproperty
Fix pull-down menus of MQTT configuration node
2022-10-04 15:34:50 +01:00
Nick O'Leary
087946876b Merge pull request #3907 from boahc077/github_actions_token_permission
ci: add minimum GitHub token permissions for workflows
2022-10-04 15:32:47 +01:00
Nick O'Leary
318f0f1b7e Merge pull request #3899 from node-red/fix-change-self-overwrite
Fix change node overwriting msg with itself
2022-10-04 15:29:23 +01:00
Stephen McLaughlin
6d1a12af4b remove debug/test code 2022-10-04 13:36:23 +01:00
Nick O'Leary
a40e5dbcd4 Add check that node sends object rather than primitive type
Fixes #3876
2022-10-04 11:49:49 +01:00
Nick O'Leary
7c79ca7878 Ensure msg.req.headers is enumerable
Fixes #3878
2022-10-04 11:28:26 +01:00
Nick O'Leary
93c1600980 Merge pull request #3886 from kazuhitoyokoi/master2
Limit number of ports in function node
2022-10-04 11:00:18 +01:00
Nick O'Leary
c3d1e6181f Merge pull request #3894 from hardillb/http-response-status-number
Ensure statusCode is a number
2022-10-04 10:28:04 +01:00
Ashish Kurmi
87e7f3a61c ci: add minimum GitHub token permissions for workflows
Signed-off-by: Ashish Kurmi <akurmi@stepsecurity.io>
2022-10-02 11:16:13 -07:00
Dave Conway-Jones
e724f216bf Fix for CSV undefined property
to close #3900 main issue
and add tests
(other fix is commented out but no tests)
2022-09-30 13:48:48 +01:00
Steve-Mcl
e6b379358a better logging of set with no types 2022-09-29 21:28:13 +01:00
Steve-Mcl
b0abba15a6 remove dud code instead of commenting 2022-09-29 19:08:46 +01:00
Steve-Mcl
81b4874a7c fix new test and fix bug found in previous PR 2022-09-29 19:05:53 +01:00
Steve-Mcl
f11b9c1e18 add test bad birth topic
part of #3865
2022-09-29 13:12:15 +01:00
Steve-Mcl
e15ecc00ce remove old unused code (5y+ not used) 2022-09-29 13:11:25 +01:00
Dave Conway-Jones
3e4c45ac6a Fix change node overwriting msg with itself
and add test
to close #3891
2022-09-22 20:22:11 +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
Ben Hardill
4115c13a65 Ensure statusCode is a number
Fixes #3893

Used parseInt instead of the suggested fix so that we don't end up
with statusCode = 200.5
2022-09-19 19:43:06 +01:00
Kazuhito Yokoi
f872e2ab80 Add icon to typedInput in MQTT node 2022-09-18 19:11:33 +09:00
Kazuhito Yokoi
a81b1aa0cb Support i18n in MQTT node property 2022-09-18 17:10:19 +09:00
Kazuhito Yokoi
efc0f1ab91 Fix default values for MQTT retain settings 2022-09-18 16:24:25 +09:00
Kazuhito Yokoi
ce31edc803 Fix handling of max and min values in function outputs 2022-09-18 02:22:52 +09:00
Kazuhito Yokoi
199caccbc3 Change the maximum number of ports to 500 2022-09-17 00:25:46 +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
f060309002 Merge pull request #3872 from node-red-hitachi/fix-Japanese-editor-message-for-JSONata
Fix Japanese translation for JSONata editor
2022-09-15 21:25:30 +01:00
Nick O'Leary
fc3d86e6ff Merge pull request #3882 from kazuhitoyokoi/master-addenvinfo2template
Add information about environment variable to template node
2022-09-15 21:24:32 +01:00
Kazuhito Yokoi
7507a7b459 Limit number of ports in function node 2022-09-16 02:10:14 +09:00
Kazuhito Yokoi
d657817211 Add information about environment variable to template node 2022-09-13 22:08:22 +09:00
Hiroyasu Nishiyama
954649007c merge master 2022-09-13 10:52:48 +09:00
Nick O'Leary
0caa308757 Add core:copy-item-link action and expose in info sidebar 2022-09-12 20:53:46 +01:00
Hiroyasu Nishiyama
7bd7c99dd4 Fix Japanese translation for JSONata editor 2022-09-06 16:53:38 +09:00
Nick O'Leary
1fa8f30550 Support uri fragments for nodes and groups including edit support 2022-09-05 21:08:36 +01:00
Steve-Mcl
6a19d8246c add test for specific arrangement of node package 2022-09-03 21:37:27 +01:00
Steve-Mcl
d6bb3a558f fix loading node package in nodesDir on linux
fixes #3861
2022-09-03 21:23:38 +01:00
Nick O'Leary
44300dbb34 Merge pull request #3807 from node-red-hitachi/env-var-jsonata
Env var jsonata
2022-09-02 20:47:30 +01:00
Hiroyasu Nishiyama
1ddbeaa50f add test cases 2022-07-27 20:25:43 +09: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
184 changed files with 4142 additions and 1785 deletions

View File

@@ -5,6 +5,9 @@ on:
release:
types: [published]
permissions:
contents: read
jobs:
generate:
name: 'Update node-red-docker image'

View File

@@ -6,12 +6,18 @@ on:
pull_request:
branches: [ master, dev ]
permissions:
contents: read
jobs:
build:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14, 16]
node-version: [16, 18]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
@@ -24,7 +30,7 @@ jobs:
run: |
npm run test
- name: Publish to coveralls.io
if: ${{ matrix.node-version == 14 }}
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,94 @@
#### 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"
]
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.0.2",
"version": "3.1.0-beta.1",
"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",
"got": "11.8.6",
"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.41",
"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.3.8",
"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"
},
"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.2.12",
"mermaid": "^9.3.0",
"minami": "1.2.3",
"mocha": "9.2.2",
"node-red-node-test-helper": "^0.3.0",
"nodemon": "2.0.19",
"nodemon": "2.0.20",
"proxy": "^1.0.2",
"sass": "1.54.2",
"sass": "1.58.3",
"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

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.0.2",
"version": "3.1.0-beta.1",
"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.1",
"@node-red/editor-client": "3.1.0-beta.1",
"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

View File

View File

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",
@@ -497,6 +510,7 @@
"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)",
@@ -683,7 +697,11 @@
"empty": "empty",
"globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action",
"find": "Find in workspace"
"find": "Find in workspace",
"copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard",
"showFlow": "Show",
"hideFlow": "Hide"
},
"help": {
"name": "Help",
@@ -984,7 +1002,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",
@@ -1206,5 +1227,10 @@
"node": "Node",
"junction": "Junction",
"linkNodes": "Link Nodes"
},
"env-var": {
"environment": "Environment",
"header": "Global Environment Variables",
"revert": "Revert"
}
}

View File

View File

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": "ノードを検索",
@@ -497,6 +510,7 @@
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
"deleteReconnect": "削除と再接続",
"importNode": "フローの読み込み",
"exportNode": "フローの書き出し",
"nudgeNode": "選択したノードを移動(移動量小)",
@@ -683,7 +697,11 @@
"empty": "空",
"globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索"
"find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました",
"showFlow": "表示",
"hideFlow": "非表示"
},
"help": {
"name": "ヘルプ",
@@ -935,11 +953,11 @@
"errors": {
"invalid-expr": "不正なJSONata式:\n __message__",
"invalid-msg": "不正なJSONメッセージ例:\n __message__",
"context-unsupported": "$flowContext や $globalContextの\nコンテキスト機能をテストできません",
"context-unsupported": "$flowContext や $globalContextの\nコンテキスト関数をテストできません",
"env-unsupported": "$env関数はテストできません",
"moment-unsupported": "$moment関数はテストできません",
"clone-unsupported": "$clone関数はテストできません",
"eval": "表現評価エラー:\n __message__"
"eval": "評価エラー:\n __message__"
}
},
"monaco": {
@@ -984,7 +1002,10 @@
"quote": "引用",
"link": "リンク",
"horizontal-rule": "区切り線",
"toggle-preview": "プレビュー表示切替え"
"toggle-preview": "プレビュー表示切替え",
"mermaid": {
"summary": "Mermaid図"
}
},
"bufferEditor": {
"title": "バッファエディタ",
@@ -1207,6 +1228,11 @@
"junction": "分岐点",
"linkNodes": "Linkード"
},
"env-var": {
"environment": "環境変数",
"header": "大域環境変数",
"revert": "破棄"
},
"action-list": {
"toggle-show-tips": "ヒント表示切替",
"show-about": "Node-REDの説明を表示",
@@ -1291,6 +1317,7 @@
"distribute-selection-vertically": "選択を上下に整列",
"wire-series-of-nodes": "ノードを一続きに接続",
"wire-node-to-multiple": "ノードを複数に接続",
"wire-multiple-to-node": "複数からノードへ接続",
"split-wire-with-link-nodes": "ワイヤーをlinkードで分割",
"generate-node-names": "ノード名を生成",
"show-user-settings": "ユーザ設定を表示",
@@ -1350,6 +1377,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

View File

View File

View File

View File

View File

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "3.0.2",
"version": "3.1.0-beta.1",
"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);
@@ -1046,6 +1137,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];
}
}
@@ -1651,6 +1745,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 +1754,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 +1780,7 @@ RED.nodes = (function() {
newNodes = newNodesObj;
}
if (!$.isArray(newNodes)) {
if (!Array.isArray(newNodes)) {
newNodes = [newNodes];
}
@@ -1965,7 +2060,7 @@ RED.nodes = (function() {
}
}
} else {
const keepNodesCurrentZ = reimport && n.z && RED.workspaces.contains(n.z)
const keepNodesCurrentZ = reimport && n.z && (RED.workspaces.contains(n.z) || RED.nodes.subflow(n.z))
if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
n.z = activeWorkspace;
}
@@ -1983,6 +2078,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 +2143,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;
@@ -2067,7 +2168,7 @@ RED.nodes = (function() {
node.id = getID();
} else {
node.id = n.id;
const keepNodesCurrentZ = reimport && node.z && RED.workspaces.contains(node.z)
const keepNodesCurrentZ = reimport && node.z && (RED.workspaces.contains(node.z) || RED.nodes.subflow(node.z))
if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
if (createMissingWorkspace) {
if (missingWorkspace === null) {
@@ -2265,7 +2366,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 +2416,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 +2438,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 +2461,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 +2622,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 +2653,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 +2676,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 +2707,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); });
@@ -2740,6 +2883,7 @@ RED.nodes = (function() {
}
});
const nodeGroupMap = {}
var replaceNodeIds = Object.keys(replaceNodes);
if (replaceNodeIds.length > 0) {
var reimportList = [];
@@ -2750,6 +2894,12 @@ RED.nodes = (function() {
} else {
allNodes.removeNode(n);
}
if (n.g) {
// reimporting a node *without* including its group object
// will cause the g property to be cleared. Cache it
// here so we can restore it
nodeGroupMap[n.id] = n.g
}
reimportList.push(convertNode(n));
RED.events.emit('nodes:remove',n);
});
@@ -2771,6 +2921,18 @@ RED.nodes = (function() {
var newNodeMap = {};
result.nodes.forEach(function(n) {
newNodeMap[n.id] = n;
if (nodeGroupMap[n.id]) {
// This node is in a group - need to substitute the
// node reference inside the group
n.g = nodeGroupMap[n.id]
const group = RED.nodes.group(n.g)
if (group) {
var index = group.nodes.findIndex(gn => gn.id === n.id)
if (index > -1) {
group.nodes[index] = n
}
}
}
});
RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) {
@@ -2783,6 +2945,9 @@ RED.nodes = (function() {
RED.view.redraw(true);
}
});
RED.events.on('deploy', function () {
allNodes.clearState()
})
},
registry:registry,
setNodeList: registry.setNodeList,
@@ -2831,7 +2996,7 @@ RED.nodes = (function() {
},
addWorkspace: addWorkspace,
removeWorkspace: removeWorkspace,
getWorkspaceOrder: function() { return workspacesOrder },
getWorkspaceOrder: function() { return [...workspacesOrder] },
setWorkspaceOrder: function(order) { workspacesOrder = order; },
workspace: getWorkspace,
@@ -2885,6 +3050,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,

View File

@@ -249,8 +249,35 @@ 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
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
if (showEditDialog) {
RED.editor.editGroup(nodeToShow)
}
}
}
}
if (RED.workspaces.count() > 0) {
const hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
@@ -321,6 +348,8 @@ var RED = (function() {
loader.end()
RED.notify($("<p>").text(message));
RED.sidebar.info.refresh()
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
});
});
return;
@@ -641,11 +670,6 @@ var RED = (function() {
]});
menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
{id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
{id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
{id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
{id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
null,
{id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
{id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
{id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
@@ -655,7 +679,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);
@@ -748,6 +777,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

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

@@ -238,6 +238,7 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, defaults, property,value);
if (((typeof valid) === "string") || !valid) {
input.addClass("input-error");
input.next(".red-ui-typedInput-container").addClass("input-error");
if ((typeof valid) === "string") {
var tooltip = input.data("tooltip");
if (tooltip) {
@@ -250,6 +251,7 @@ RED.editor = (function() {
}
} else {
input.removeClass("input-error");
input.next(".red-ui-typedInput-container").removeClass("input-error");
var tooltip = input.data("tooltip");
if (tooltip) {
input.data("tooltip", null);
@@ -858,6 +860,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;
@@ -1043,6 +1046,13 @@ RED.editor = (function() {
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.sidebar.help.show(editing_node.type);
}).appendTo(trayFooterLeft);
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
$('<input id="node-input-node-disabled" type="checkbox">').prop("checked",!!node.d).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
@@ -1146,6 +1156,8 @@ RED.editor = (function() {
var editing_config_node = RED.nodes.node(id);
var activeEditPanes = [];
if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return }
var configNodeScope = ""; // default to global
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
if (activeSubflow) {
@@ -1188,6 +1200,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",
@@ -1692,6 +1711,7 @@ RED.editor = (function() {
function showEditGroupDialog(group, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
if (group.z && RED.workspaces.isLocked(group.z)) { return }
var editing_node = group;
editStack.push(group);
RED.view.state(RED.state.EDITING);
@@ -1851,11 +1871,15 @@ RED.editor = (function() {
workspace.disabled = disabled;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
if (workspace.id === RED.workspaces.active()) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
}
var locked = $("#node-input-locked").prop("checked");
if (workspace.locked !== locked) {
editState.changes.locked = workspace.locked;
editState.changed = true;
workspace.locked = locked;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
}
if (editState.changed) {
var historyEvent = {
t: "edit",
@@ -1896,6 +1920,7 @@ RED.editor = (function() {
var trayBody = tray.find('.red-ui-tray-body');
trayBody.parent().css('overflow','hidden');
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var trayFooterRight = $('<div class="red-ui-tray-footer-right"></div>').appendTo(trayFooter)
var nodeEditPanes = [
'editor-tab-flow-properties',
@@ -1910,6 +1935,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

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

@@ -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,179 @@
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 (!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 ((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) {
@@ -434,6 +441,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 +469,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 +496,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 +530,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 +547,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 +584,7 @@ RED.group = (function() {
markDirty(group);
}
function removeFromGroup(group, nodes, reparent) {
if (RED.workspaces.isLocked()) { return }
if (!Array.isArray(nodes)) {
nodes = [nodes];
}
@@ -583,7 +602,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 {

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

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

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

@@ -292,6 +292,7 @@ RED.palette = (function() {
var hoverGroup;
var paletteWidth;
var paletteTop;
var dropEnabled;
$(d).draggable({
helper: 'clone',
appendTo: '#red-ui-editor',
@@ -299,6 +300,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 +311,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);
}
}
}
}
@@ -432,6 +438,7 @@ RED.palette = (function() {
categoryNode.find(".red-ui-palette-content").slideToggle();
categoryNode.find("i").toggleClass("expanded");
}
categoryNode.hide();
}
}
@@ -510,6 +517,7 @@ RED.palette = (function() {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
currentCategoryNode.hide();
}
}

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

View File

@@ -273,6 +273,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 +290,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 +439,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 +452,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 +573,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 +636,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");
@@ -776,7 +794,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 +1348,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

@@ -135,6 +135,10 @@ RED.sidebar.info.outliner = (function() {
RED.workspaces.show(n.id, null, true);
}
});
RED.popover.tooltip(toggleVisibleButton, function () {
var isHidden = !div.hasClass("red-ui-info-outline-item-hidden");
return RED._("sidebar.info." + (isHidden ? "hideFlow" : "showFlow"));
});
}
if (n.type !== 'subflow') {
var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
@@ -221,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();
@@ -364,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();
}
@@ -378,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) {
@@ -613,6 +637,9 @@ RED.sidebar.info.outliner = (function() {
objects[n.id].children = missingParents[n.id];
delete missingParents[n.id]
}
if (objects[n.id].children.length === 0) {
objects[n.id].children.push(getEmptyItem(n.id));
}
}
var parent = n.g||n.z||"__global__";

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

@@ -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,30 @@ 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 })
}
}
}
return {
init: function() {
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -1252,12 +1365,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() })
},
/**

1429
packages/node_modules/@node-red/editor-client/src/js/ui/view.js vendored Executable file → Normal file

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,58 @@ 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.sidebar.config.refresh();
// var selection = RED.view.selection();
// if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
// RED.sidebar.info.refresh(workspace);
// }
// if (changes.hasOwnProperty('disabled')) {
// RED.nodes.eachNode(function(n) {
// if (n.z === workspace.id) {
// n.dirty = true;
// }
// });
// RED.view.redraw();
// }
}
}
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 +761,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 +846,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 +907,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;

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);
@@ -287,9 +313,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

@@ -32,7 +32,8 @@
color: var(--red-ui-primary-text-color);
border: 1px solid var(--red-ui-notification-border-default);
border-left-width: 16px;
overflow: hidden;
overflow: auto;
max-height: 80vh;
.ui-dialog-buttonset {
margin-top: 20px;
margin-bottom: 10px;

View File

@@ -41,6 +41,7 @@
height: 50px;
background: var(--red-ui-secondary-background);
border: 2px solid var(--red-ui-primary-border-color);
color: var(--red-ui-primary-text-color);
text-align: center;
line-height:50px;
@@ -51,7 +52,7 @@
.red-ui-editor-radial-menu-opt-disabled {
border-color: var(--red-ui-tertiary-border-color);
color: var(--red-ui-tertiary-border-color);
color: var(--red-ui-secondary-text-color-disabled);
}
.red-ui-editor-radial-menu-opt-active {
background: var(--red-ui-secondary-background-hover);

View File

@@ -467,6 +467,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 +503,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 +539,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,155 @@
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へようこそ!"
},
description: {
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>"
}
},
{
title: {
"en-US": "Context Menu",
"ja": "コンテキストメニュー"
},
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>`
}
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
},
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>`
},
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
},
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>`
},
},
{
title: {
"en-US": "Debug Path Tooltip",
"ja": "デバッグパスのツールチップ"
},
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>`
},
},
{
title: {
"en-US": "Continuous Search",
"ja": "連続した検索"
},
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>`
},
},
{
title: {
"en-US": "New wiring actions",
"ja": "新しいワイヤー操作"
},
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>`,
},
},
{
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": "ノードの更新"
},
// 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>`
}
}
]
}

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

View File

@@ -1,137 +1,105 @@
export default {
version: "3.0.0",
version: "3.1.0-beta.1",
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 Beta 1!",
"ja": "Node-RED 3.1 ベータ1へようこそ!"
},
description: {
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>"
"en-US": "<p>This is the first beta release for 3.1.0 and we have a few new features to tell you about.</p>",
"ja": "<p>これは3.1.0の最初のベータリリースです。いくつかの新機能について説明します。</p>"
}
},
{
title: {
"en-US": "Context Menu",
"ja": "コンテキストメニュー"
"en-US": "Improved Context Menu",
"ja": "コンテキストメニューの改善"
},
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>`
}
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
"en-US": "Hiding Flows",
"ja": "フローを非表示"
},
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>`
},
},
{
title: {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
"en-US": "Locking Flows",
"ja": "フローを固定"
},
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>`
},
},
{
title: {
"en-US": "Debug Path Tooltip",
"ja": "デバッグパスのツールチップ"
"en-US": "Adding Images to node/flow descriptions",
"ja": "ノードやフローの説明へ画像を追加"
},
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>`
},
},
{
title: {
"en-US": "Continuous Search",
"ja": "連続した検索"
"en-US": "Adding Mermaid Diagrams",
"ja": "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>`
},
},
{
title: {
"en-US": "New wiring actions",
"ja": "新しいワイヤー操作"
"en-US": "Managing Global Environment Variables",
"ja": "グローバル環境変数の管理"
},
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>`
},
},
{
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",
@@ -139,16 +107,9 @@ export default {
},
// 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>`
}
}
]

View File

@@ -95,45 +95,64 @@ module.exports = function(RED) {
}
this.on("input", function(msg, send, done) {
var errors = [];
var props = this.props;
const errors = [];
let props = this.props;
if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
props = msg.__user_inject_props__;
}
delete msg.__user_inject_props__;
props.forEach(p => {
var property = p.p;
var value = p.v ? p.v : '';
var valueType = p.vt ? p.vt : 'str';
props = [...props]
function evaluateProperty(doneEvaluating) {
if (props.length === 0) {
doneEvaluating()
return
}
const p = props.shift()
const property = p.p;
const value = p.v ? p.v : '';
const valueType = p.vt ? p.vt : 'str';
if (!property) return;
if (valueType === "jsonata") {
if (p.v) {
try {
var exp = RED.util.prepareJSONataExpression(p.v, node);
var val = RED.util.evaluateJSONataExpression(exp, msg);
RED.util.setMessageProperty(msg, property, val, true);
if (property) {
if (valueType === "jsonata") {
if (p.v) {
try {
var exp = RED.util.prepareJSONataExpression(p.v, node);
var val = RED.util.evaluateJSONataExpression(exp, msg);
RED.util.setMessageProperty(msg, property, val, true);
}
catch (err) {
errors.push(err.message);
}
}
catch (err) {
errors.push(err.message);
evaluateProperty(doneEvaluating)
} else {
try {
RED.util.evaluateNodeProperty(value, valueType, node, msg, (err, newValue) => {
if (err) {
errors.push(err.toString())
} else {
RED.util.setMessageProperty(msg,property,newValue,true);
}
evaluateProperty(doneEvaluating)
})
} catch (err) {
errors.push(err.toString());
evaluateProperty(doneEvaluating)
}
}
return;
} else {
evaluateProperty(doneEvaluating)
}
try {
RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
} catch (err) {
errors.push(err.toString());
}
});
if (errors.length) {
done(errors.join('; '));
} else {
send(msg);
done();
}
evaluateProperty(() => {
if (errors.length) {
done(errors.join('; '));
} else {
send(msg);
done();
}
})
});
}

View File

@@ -18,7 +18,16 @@
color:"#c0edc0",
defaults: {
name: {value:""},
scope: {value:[], type:"*[]"},
scope: {
value: [],
type: "*[]",
validate: function (v, opt) {
if (v.length > 0) {
return true;
}
return RED._("node-red:complete.errors.scopeUndefined");
}
},
uncaught: {value:false}
},
inputs:0,

View File

@@ -4,7 +4,7 @@
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
<select id="node-input-scope-select">
<option value="all" data-i18n="catch.scope.all"></option>
<option value="target" data-i18n="catch.scope.selected"></options>
<option value="target" data-i18n="catch.scope.selected"></option>
</select>
</div>
<div class="form-row node-input-uncaught-row">

View File

@@ -4,7 +4,7 @@
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
<select id="node-input-scope-select">
<option value="all" data-i18n="status.scope.all"></option>
<option value="target" data-i18n="status.scope.selected"></options>
<option value="target" data-i18n="status.scope.selected"></option>
</select>
</div>
<div class="form-row node-input-target-row">

View File

@@ -1,4 +1,3 @@
<script type="text/html" data-template-name="link in">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -272,7 +271,17 @@
color:"#ddd",//"#87D8CF",
defaults: {
name: { value: "" },
links: { value: [], type:"link in[]" },
links: {
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}
return RED._("node-red:link.errors.linkUndefined");
}
},
linkType: { value:"static" },
timeout: {
value: "30",

View File

@@ -164,10 +164,10 @@ module.exports = function(RED) {
if (returnNode && returnNode.returnLinkMessage) {
returnNode.returnLinkMessage(messageEvent.id, msg);
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
done();
} else if (mode === "link") {
@@ -248,6 +248,14 @@ module.exports = function(RED) {
}
});
this.on("close", function () {
for (const event of Object.values(messageEvents)) {
if (event.ts) {
clearTimeout(event.ts)
}
}
})
this.returnLinkMessage = function(eventId, msg) {
if (Array.isArray(msg._linkSource) && msg._linkSource.length === 0) {
delete msg._linkSource;

View File

@@ -0,0 +1,27 @@
<script type="text/html" data-template-name="global-config">
  <div class="form-row">
<label style="width: 100%"><span data-i18n="global-config.label.open-conf"></span>:</label>
</div>
<div class="form-row">
<button class="red-ui-button" type="button" id="node-input-edit-env-var" data-i18n="editor:env-var.header" style="margin-left: 20px"></button>
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('global-config',{
category: 'config',
defaults: {
name: { value: "" },
env: { value: [] },
},
credentials: {
map: { type: "map" }
},
oneditprepare: function() {
$('#node-input-edit-env-var').on('click', function(evt) {
RED.actions.invoke('core:show-user-settings', 'envvar')
});
},
hasUsers: false
});
</script>

View File

@@ -0,0 +1,7 @@
module.exports = function(RED) {
"use strict";
function GlobalConfigNode(n) {
RED.nodes.createNode(this,n);
}
RED.nodes.registerType("global-config", GlobalConfigNode);
}

View File

@@ -17,6 +17,8 @@
display: flex;
background: var(--red-ui-tertiary-background);
padding-right: 75px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
#node-input-libs-container-row .red-ui-editableList-header > div {
flex-grow: 1;
@@ -451,11 +453,13 @@
tabs.activateTab("func-tab-body");
$( "#node-input-outputs" ).spinner({
min:0,
min: 0,
max: 500,
change: function(event, ui) {
var value = this.value;
if (!value.match(/^\d+$/)) { value = 1; }
else if (value < this.min) { value = this.min; }
var value = parseInt(this.value);
value = isNaN(value) ? 1 : value;
value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
if (value !== this.value) { $(this).spinner("value", value); }
}
});

View File

@@ -318,7 +318,7 @@ module.exports = function(RED) {
}
var r = node.rules[currentRule];
if (r.t === "move") {
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) {
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1) && (r.p !== r.to)) {
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => {
applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => {
completeApplyingRules(msg,currentRule,done);

View File

@@ -10,6 +10,7 @@
<option value="scale" data-i18n="range.scale.payload"></option>
<option value="clamp" data-i18n="range.scale.limit"></option>
<option value="roll" data-i18n="range.scale.wrap"></option>
<option value="drop" data-i18n="range.scale.drop"></option>
</select>
</div>
<br/>

View File

@@ -32,11 +32,15 @@ module.exports = function(RED) {
if (value !== undefined) {
var n = Number(value);
if (!isNaN(n)) {
if (node.action == "clamp") {
if (node.action === "drop") {
if (n < node.minin) { done(); return; }
if (n > node.maxin) { done(); return; }
}
if (node.action === "clamp") {
if (n < node.minin) { n = node.minin; }
if (n > node.maxin) { n = node.maxin; }
}
if (node.action == "roll") {
if (node.action === "roll") {
var divisor = node.maxin - node.minin;
n = ((n - node.minin) % divisor + divisor) % divisor + node.minin;
}

View File

@@ -21,6 +21,7 @@
<option value="javascript">JavaScript</option>
<option value="css">CSS</option>
<option value="markdown">Markdown</option>
<option value="php">PHP</option>
<option value="python">Python</option>
<option value="sql">SQL</option>
<option value="yaml">YAML</option>

View File

@@ -201,6 +201,7 @@ module.exports = function(RED) {
});
node.on("close", function() { clearDelayList(); });
}
else if (node.pauseType === "random") {
node.on("input", function(msg, send, done) {
var wait = node.randomFirst + (node.diff * Math.random());
@@ -226,34 +227,19 @@ module.exports = function(RED) {
// The rate limit/queue type modes
else if (node.pauseType === "rate") {
node.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) {
if (node.intervalID !== -1 ) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
delete node.lastSent;
node.buffer = [];
node.rate = node.fixedrate;
node.status({fill:"blue",shape:"ring",text:0});
done();
return;
}
if (!node.drop) {
var m = RED.util.cloneMessage(msg);
delete m.flush;
delete m.lifo;
if (Object.keys(m).length > 1) {
if (node.intervalID !== -1) {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate)) && node.rate !== msg.rate) {
node.rate = msg.rate;
if (node.allowrate && m.hasOwnProperty("rate") && !isNaN(parseFloat(m.rate)) && node.rate !== m.rate) {
node.rate = m.rate;
clearInterval(node.intervalID);
node.intervalID = setInterval(sendMsgFromBuffer, node.rate);
}
var max_msgs = maxKeptMsgsCount(node);
if ((max_msgs > 0) && (node.buffer.length >= max_msgs)) {
node.buffer = [];
node.error(RED._("delay.errors.too-many"), msg);
node.error(RED._("delay.errors.too-many"), m);
} else if (msg.toFront === true) {
node.buffer.unshift({msg: m, send: send, done: done});
node.reportDepth();
@@ -263,8 +249,8 @@ module.exports = function(RED) {
}
}
else {
if (node.allowrate && msg.hasOwnProperty("rate") && !isNaN(parseFloat(msg.rate))) {
node.rate = msg.rate;
if (node.allowrate && m.hasOwnProperty("rate") && !isNaN(parseFloat(m.rate))) {
node.rate = m.rate;
}
send(m);
node.reportDepth();
@@ -282,6 +268,8 @@ module.exports = function(RED) {
else {
while (len > 0) {
const msgInfo = node.buffer.shift();
delete msgInfo.msg.flush;
delete msgInfo.msg.reset;
if (Object.keys(msgInfo.msg).length > 1) {
node.send(msgInfo.msg);
msgInfo.done();
@@ -335,6 +323,21 @@ module.exports = function(RED) {
}
done();
}
if (msg.hasOwnProperty("reset")) {
if (msg.flush === undefined) {
if (node.intervalID !== -1 ) {
clearInterval(node.intervalID);
node.intervalID = -1;
}
delete node.lastSent;
}
node.buffer = [];
node.rate = node.fixedrate;
node.status({fill:"blue",shape:"ring",text:0});
done();
return;
}
});
node.on("close", function() {
clearInterval(node.intervalID);
@@ -387,6 +390,22 @@ module.exports = function(RED) {
node.buffer.push({msg, send, done}); // if not add to end of queue
node.reportDepth();
}
if (msg.hasOwnProperty("flush")) {
var len = node.buffer.length;
if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush,len)); }
while (len > 0) {
const msgInfo = node.buffer.shift();
delete msgInfo.msg.flush;
delete msgInfo.msg.reset;
if (Object.keys(msgInfo.msg).length > 2) {
node.send(msgInfo.msg);
msgInfo.done();
}
len = len - 1;
}
node.status({});
done();
}
if (msg.hasOwnProperty("reset")) {
while (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
@@ -397,21 +416,6 @@ module.exports = function(RED) {
node.status({text:"reset"});
done();
}
if (msg.hasOwnProperty("flush")) {
var len = node.buffer.length;
if (typeof(msg.flush) == 'number') { len = Math.min(Math.floor(msg.flush,len)); }
while (len > 0) {
const msgInfo = node.buffer.shift();
delete msgInfo.msg.flush;
if (Object.keys(msgInfo.msg).length > 2) {
node.send(msgInfo.msg);
msgInfo.done();
}
len = len - 1;
}
node.status({});
done();
}
});
node.on("close", function() {
clearInterval(node.intervalID);

View File

@@ -25,7 +25,7 @@
<select id="node-then-type" style="width:70%;">
<option value="block" data-i18n="trigger.wait-reset"></option>
<option value="wait" data-i18n="trigger.wait-for"></option>
<option value="loop" data-i18n="trigger.wait-loop"></option>
<option id="node-trigger-wait-loop" value="loop" data-i18n="trigger.wait-loop"></option>
</select>
</div>
<div class="form-row node-type-duration">
@@ -181,6 +181,13 @@
$("#node-input-op2type").val('str');
}
$("#node-input-op1").on("change", function() {
if ($("#node-input-op1type").val() === "nul") {
$("#node-trigger-wait-loop").hide();
}
else { $("#node-trigger-wait-loop").show(); }
});
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false};
var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false};

View File

@@ -421,7 +421,11 @@
<script type="text/javascript">
(function() {
var typedInputNoneOpt = { value: 'none', label: '', hasValue: false };
var typedInputNoneOpt = {
value: 'none',
label: RED._("node-red:mqtt.label.none"),
hasValue: false
};
var makeTypedInputOpt = function(value){
return {
value: value,
@@ -436,7 +440,11 @@
makeTypedInputOpt("text/csv"),
makeTypedInputOpt("text/html"),
makeTypedInputOpt("text/plain"),
{value:"other", label:""}
{
value: "other",
label: RED._("node-red:mqtt.label.other"),
icon: "red/images/typedInput/az.svg"
}
];
function getDefaultContentType(value) {
@@ -499,17 +507,17 @@
cleansession: {value: true},
birthTopic: {value:"", validate:validateMQTTPublishTopic},
birthQos: {value:"0"},
birthRetain: {value:false},
birthRetain: {value:"false"},
birthPayload: {value:""},
birthMsg: { value: {}},
closeTopic: {value:"", validate:validateMQTTPublishTopic},
closeQos: {value:"0"},
closeRetain: {value:false},
closeRetain: {value:"false"},
closePayload: {value:""},
closeMsg: { value: {}},
willTopic: {value:"", validate:validateMQTTPublishTopic},
willQos: {value:"0"},
willRetain: {value:false},
willRetain: {value:"false"},
willPayload: {value:""},
willMsg: { value: {}},
userProps: { value: ""},

View File

@@ -295,7 +295,7 @@ module.exports = function(RED) {
/* mute error - it simply isnt JSON, just leave payload as a string */
}
}
} //else {
} //else {
//leave as buffer
//}
}
@@ -357,7 +357,7 @@ module.exports = function(RED) {
return;
}
done(err);
});
});
} else {
done();
}
@@ -366,6 +366,16 @@ module.exports = function(RED) {
}
}
function updateStatus(node, allNodes) {
let setStatus = setStatusDisconnected
if(node.connecting) {
setStatus = setStatusConnecting
} else if(node.connected) {
setStatus = setStatusConnected
}
setStatus(node, allNodes)
}
function setStatusDisconnected(node, allNodes) {
if(allNodes) {
for (var id in node.users) {
@@ -459,7 +469,6 @@ module.exports = function(RED) {
if(!opts || typeof opts !== "object") {
return; //nothing to change, simply return
}
const originalBrokerURL = node.brokerurl;
//apply property changes (only if the property exists in the opts object)
setIfHasProperty(opts, node, "url", init);
@@ -468,7 +477,6 @@ module.exports = function(RED) {
setIfHasProperty(opts, node, "clientid", init);
setIfHasProperty(opts, node, "autoConnect", init);
setIfHasProperty(opts, node, "usetls", init);
setIfHasProperty(opts, node, "usews", init);
setIfHasProperty(opts, node, "verifyservercert", init);
setIfHasProperty(opts, node, "compatmode", init);
setIfHasProperty(opts, node, "protocolVersion", init);
@@ -571,9 +579,6 @@ module.exports = function(RED) {
if (typeof node.usetls === 'undefined') {
node.usetls = false;
}
if (typeof node.usews === 'undefined') {
node.usews = false;
}
if (typeof node.verifyservercert === 'undefined') {
node.verifyservercert = false;
}
@@ -702,16 +707,21 @@ module.exports = function(RED) {
if (Object.keys(node.users).length === 1) {
if(node.autoConnect) {
node.connect();
//update nodes status
setTimeout(function() {
updateStatus(node, true)
}, 1)
}
}
};
node.deregister = function(mqttNode,done) {
node.deregister = function(mqttNode, done, autoDisconnect) {
delete node.users[mqttNode.id];
if (!node.closing && node.connected && Object.keys(node.users).length === 0) {
node.disconnect();
if (autoDisconnect && !node.closing && node.connected && Object.keys(node.users).length === 0) {
node.disconnect(done);
} else {
done();
}
done();
};
node.canConnect = function() {
return !node.connected && !node.connecting;
@@ -845,7 +855,7 @@ module.exports = function(RED) {
let waitEnd = (client, ms) => {
return new Promise( (resolve, reject) => {
node.closing = true;
if(!client) {
if(!client) {
resolve();
} else {
const t = setTimeout(() => {
@@ -1000,7 +1010,7 @@ module.exports = function(RED) {
node.client.publish(msg.topic, msg.payload, options, function (err) {
if (done) {
done(err)
} else {
} else if(err) {
node.error(err, msg)
}
})
@@ -1024,7 +1034,7 @@ module.exports = function(RED) {
/**
* Add event handlers to the MQTT.js client and track them so that
* we do not remove any handlers that the MQTT client uses internally.
* we do not remove any handlers that the MQTT client uses internally.
* Use {@link node._clientRemoveListeners `node._clientRemoveListeners`} to remove handlers
* @param {string} event The name of the event
* @param {function} handler The handler for this event
@@ -1032,11 +1042,11 @@ module.exports = function(RED) {
node._clientOn = function(event, handler) {
node.clientListeners.push({event, handler})
node.client.on(event, handler)
}
}
/**
* Remove event handlers from the MQTT.js client & only the events
* that we attached in {@link node._clientOn `node._clientOn`}.
* Remove event handlers from the MQTT.js client & only the events
* that we attached in {@link node._clientOn `node._clientOn`}.
* * If `event` is omitted, then all events matching `handler` are removed
* * If `handler` is omitted, then all events named `event` are removed
* * If both parameters are omitted, then all events are removed
@@ -1225,7 +1235,7 @@ module.exports = function(RED) {
} else {
node.brokerConn.unsubscribe(node.topic,node.id, removed);
}
node.brokerConn.deregister(node, done);
node.brokerConn.deregister(node, done, removed);
node.brokerConn = null;
} else {
done();
@@ -1288,9 +1298,9 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
}
node.brokerConn.register(node);
node.on('close', function(done) {
node.on('close', function(removed, done) {
if (node.brokerConn) {
node.brokerConn.deregister(node,done);
node.brokerConn.deregister(node, done, removed)
node.brokerConn = null;
} else {
done();

View File

@@ -46,7 +46,7 @@ module.exports = function(RED) {
isText = true;
} else if (parsedType.type !== "application") {
isText = false;
} else if ((parsedType.subtype !== "octet-stream")
} else if ((parsedType.subtype !== "octet-stream")
&& (parsedType.subtype !== "cbor")
&& (parsedType.subtype !== "x-protobuf")) {
checkUTF = true;
@@ -200,6 +200,15 @@ module.exports = function(RED) {
this.callback = function(req,res) {
var msgid = RED.util.generateId();
res._msgid = msgid;
// Since Node 15, req.headers are lazily computed and the property
// marked as non-enumerable.
// That means it doesn't show up in the Debug sidebar.
// This redefines the property causing it to be evaluated *and*
// marked as enumerable again.
Object.defineProperty(req, 'headers', {
value: req.headers,
enumerable: true
})
if (node.method.match(/^(post|delete|put|options|patch)$/)) {
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
} else if (node.method == "get") {
@@ -282,7 +291,7 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
this.headers = n.headers||{};
this.statusCode = n.statusCode;
this.statusCode = parseInt(n.statusCode);
this.on("input",function(msg,_send,done) {
if (msg.res) {
var headers = RED.util.cloneMessage(node.headers);
@@ -323,7 +332,7 @@ module.exports = function(RED) {
}
}
}
var statusCode = node.statusCode || msg.statusCode || 200;
var statusCode = node.statusCode || parseInt(msg.statusCode) || 200;
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
msg.res._res.status(statusCode).jsonp(msg.payload);
} else {

View File

@@ -435,6 +435,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
formData.append(opt, val);
} else if (typeof val === 'object' && val.hasOwnProperty('value')) {
formData.append(opt,val.value,val.options || {});
} else if (Array.isArray(val)) {
for (var i=0; i<val.length; i++) {
formData.append(opt, val[i])
}
} else {
formData.append(opt,JSON.stringify(val));
}

View File

@@ -86,7 +86,7 @@ module.exports = function(RED) {
this.topic = n.topic;
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t");
this.newline = (n.newline||"").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t");
this.base64 = n.base64;
this.trim = n.trim || false;
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");

View File

@@ -19,9 +19,9 @@ module.exports = function(RED) {
function CSVNode(n) {
RED.nodes.createNode(this,n);
this.template = (n.temp || "");
this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r");
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.quo = '"';
this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r");
this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.winflag = (this.ret === "\r\n");
this.lineend = "\n";
this.multi = n.multi || "one";
@@ -110,7 +110,12 @@ module.exports = function(RED) {
if (msg.payload[s].hasOwnProperty(p)) {
/* istanbul ignore else */
if (typeof msg.payload[s][p] !== "object") {
var q = "" + msg.payload[s][p];
// Fix to honour include null values flag
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
var q = "";
if (msg.payload[s][p] !== undefined) {
q += msg.payload[s][p];
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""');
ou += node.quo + q + node.quo + node.sep;
@@ -130,9 +135,15 @@ module.exports = function(RED) {
ou += node.sep;
}
else {
var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']"));
var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */
if (p === "undefined") { p = ""; }
if (p === undefined) { p = ""; }
// fix to honour include null values flag
//if (p === null && node.include_null_values !== true) { p = "";}
p = RED.util.ensureString(p);
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
p = p.replace(/"/g, '""');
ou += node.quo + p + node.quo + node.sep;

View File

@@ -224,7 +224,12 @@
outputs:1,
icon: "join.svg",
label: function() {
return this.name||this._("join.join");
var nam = this.name||this._("join.join");
if (this.mode === "custom" && !isNaN(Number(this.count))) {
nam += " "+this.count;
if (this.accumulate === true) { nam+= "+"; }
}
return nam;
},
labelStyle: function() {
return this.name?"node_label_italic":"";

View File

@@ -251,7 +251,9 @@ module.exports = function(RED) {
}
else {
node.buffer = buff.slice(p,buff.length);
node.pendingDones.push(done);
if (node.buffer.length > 0) {
node.pendingDones.push(done);
}
}
if (node.buffer.length == 0) {
done();
@@ -476,7 +478,7 @@ module.exports = function(RED) {
var completeSend = function(partId) {
var group = inflight[partId];
if (group.timeout) { clearTimeout(group.timeout); }
if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; }
if (node.mode === 'auto' || node.accumulate !== true || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; }
if (group.type === 'array' && group.arrayLen > 1) {
var newArray = [];
group.payload.forEach(function(n) {

View File

@@ -107,7 +107,14 @@
outputs:1,
icon: "batch.svg",
label: function() {
return this.name||this._("batch.batch");;
var nam = this.name||this._("batch.batch");
if (this.mode === "count" && !isNaN(Number(this.count))) {
nam += " "+this.count;
}
if (this.mode === "interval" && !isNaN(Number(this.interval))) {
nam += " "+this.interval+"s";
}
return nam;
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";

0
packages/node_modules/@node-red/nodes/core/storage/10-file.html vendored Executable file → Normal file
View File

View File

View File

View File

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