Compare commits

..

587 Commits

Author SHA1 Message Date
Nick O'Leary
b1a706f811 Add comment on RED.nodes.getNodeLinkCount api 2022-01-12 18:04:53 +00:00
Nick O'Leary
c27dd336d9 Move ports behind node body and increase touch zone for each port 2022-01-12 18:01:11 +00:00
Nick O'Leary
5e9ff98c49 Add arrow-heads to links 2022-01-12 18:01:06 +00:00
Nick O'Leary
50cb074172 Visually distinguish ports with connected wires 2022-01-12 18:01:01 +00:00
Nick O'Leary
510a09ecba Add RED.nodes.getNodeLinkCount to provide O(1) lookup of link count on node port 2022-01-12 18:00:53 +00:00
Nick O'Leary
f86e743cce Merge branch 'master' into dev 2022-01-12 17:59:26 +00:00
Nick O'Leary
cb96fb735e Merge pull request #3289 from node-red/snap-grid
Snap nodes on grid using either edge as reference
2022-01-12 17:57:26 +00:00
Nick O'Leary
459a52d31d Merge pull request #3340 from node-red/cut-wires
Add wire-slice mode to delete wires with Ctrl-RHClick-Drag
2022-01-12 17:52:54 +00:00
Nick O'Leary
44ef9a13d6 Merge pull request #3338 from node-red/detach-action
Detach action
2022-01-12 17:52:32 +00:00
Nick O'Leary
0bb3652a63 Merge pull request #3323 from node-red/highlight-links
Highlight links when selecting nodes
2022-01-12 17:52:03 +00:00
Nick O'Leary
6a82d683a9 Merge pull request #3262 from node-red/search-history
Add search history to main search box
2022-01-12 17:51:46 +00:00
Nick O'Leary
fad708e8de Merge pull request #3294 from node-red/multi-select-links
Allow multiple links to be selected by ctrl-click
2022-01-12 17:51:15 +00:00
Nick O'Leary
c6a38b8355 Merge pull request #3300 from node-red/remove-use-of-verbose-flag-in-core-nodes
Remove use of verbose flag in core nodes - and use node.debug level instead
2022-01-12 17:38:34 +00:00
Nick O'Leary
ee84eb666b Merge pull request #3307 from node-red/add-tls-option-to-tcp-node
Add TLS option to tcp client nodes
2022-01-12 17:37:43 +00:00
Nick O'Leary
6d2793cac6 Merge pull request #3336 from node-red-hitachi/add-link-call-example
add link call example
2022-01-12 17:31:57 +00:00
Nick O'Leary
86d518fc2e Merge pull request #3343 from node-red/adminAuth-logging
Initialize passport when only adminAuth.tokens is set
2022-01-12 17:31:44 +00:00
Nick O'Leary
25dba1a6d5 Merge pull request #3342 from node-red/add-log-logging
Add log logging
2022-01-12 17:29:29 +00:00
Nick O'Leary
af949c62c2 Allow 'escape' to cancel wire-slicing 2022-01-12 17:24:25 +00:00
Nick O'Leary
ea20342d76 Handle non-error responses from token function 2022-01-12 13:48:06 +00:00
Nick O'Leary
7732d52583 Initialize passport when only adminAuth.tokens is set
Fixes #3341
2022-01-12 13:27:01 +00:00
Nick O'Leary
c801bc5e6b Add error logging if Node.log throws an error
Part of #3327
2022-01-12 11:13:29 +00:00
Nick O'Leary
ea43729063 Add _module to Node object to provide module info 2022-01-12 11:07:25 +00:00
Nick O'Leary
e26bae8027 Merge pull request #3333 from tobiasoort/dev
Implemented support for Websocket Subprotocols in WS Client Node.
2022-01-12 09:31:55 +00:00
Tobias Oort
555f155cad Added support for commaseparated subprotocols
Removed placeholder from html
2022-01-12 09:10:35 +01:00
Nick O'Leary
f8c47f59bc Merge pull request #3339 from node-red/fix-filter-node-start-condition
Fix for filter node narrrowbandEq mode start condition failure
2022-01-11 22:39:14 +00:00
Nick O'Leary
f3997128b9 Add wire-slice mode to delete wires with Ctrl-RHClick-Drag 2022-01-11 19:49:40 +00:00
Nick O'Leary
154a4e23dd Add delete-selection-and-reconnect action 2022-01-11 14:11:55 +00:00
Dave Conway-Jones
52e4e0e569 Fix for narrrowbandEq mode start condition failure
And add test
As discussed https://discourse.nodered.org/t/the-filter-node-or-the-old-rbe-node/56371
2022-01-11 13:56:44 +00:00
Nick O'Leary
30f2b96c68 Pressing escape whilst DETACH_DRAGGING should revert change 2022-01-11 00:24:34 +00:00
Nick O'Leary
58c94b7773 Add core:detach-selected-nodes action 2022-01-11 00:16:24 +00:00
Tobias Oort
83203d5f5d Fixed property name in unit-test 2022-01-10 22:23:45 +01:00
Dave Conway-Jones
f77d161643 remove debug msg from inject close 2022-01-10 20:22:20 +00:00
Nick O'Leary
6580b139c0 Merge pull request #3288 from node-red/wiring-shortcut
Wiring shortcut
2022-01-10 18:34:06 +00:00
Nick O'Leary
2743c7c6ac Merge pull request #3304 from node-red-hitachi/dev-unknownconfig
Check availability of type of config node on deploy
2022-01-10 18:33:36 +00:00
Nick O'Leary
062f76214e Merge pull request #3325 from hardillb/http-basic-username-only
Fix basic auth with empty username or password
2022-01-10 18:27:55 +00:00
Nick O'Leary
ce98ed98a2 Merge pull request #3334 from Steve-Mcl/fix-monaco-expanded
load extralibs when expanding monaco. fixes #3319
2022-01-10 18:23:07 +00:00
Nick O'Leary
dce9d93f6c Merge pull request #3335 from node-red-hitachi/fix-file-node-example
Update file node examples according to node name change
2022-01-10 18:22:39 +00:00
Nick O'Leary
f7e35a6cbe Merge pull request #3337 from node-red-hitachi/fix-selection
resume focus after import/export dialog close
2022-01-10 18:21:55 +00:00
Hiroyasu Nishiyama
931335220f resume focus after import/export dialog close 2022-01-09 23:34:08 +09:00
Hiroyasu Nishiyama
3ce35a8a4b add link call example 2022-01-09 20:24:15 +09:00
Hiroyasu Nishiyama
9ac4e5cf6a update file node examples according to node name change 2022-01-09 19:49:06 +09:00
Tobias Oort
bd77d7eec3 Implemented support for Websocket Subprotocols in WS Client Node. 2022-01-08 22:18:05 +01:00
Steve-Mcl
b5e48aa509 load extralibs when expanding monaco. fixes #3319 2022-01-08 19:29:49 +00:00
Kazuhito Yokoi
b14c42b6a4 Update Japanese translations in node help (#3332)
* Update Japanese translations in node help

* Fix typos
2022-01-07 17:43:53 +00:00
Ben Hardill
44616c6872 Fix basic auth with empty username or password
fix for #3324
2022-01-05 20:56:46 +00:00
Nick O'Leary
aaa2b4c3db Merge pull request #3322 from node-red/inject-props-fix
Fix incorrect clearing of blank payload property in Inject node
2022-01-04 18:59:05 +00:00
Nick O'Leary
8974d8e4df Merge pull request #3321 from node-red/fix-prev-tab-action
Fix findPreviousVisibleTab action
2022-01-04 18:58:45 +00:00
Nick O'Leary
699063cbb0 Merge pull request #3312 from node-red/hide-tabs-fix
Fix storing hidden tab state when not hidden via action
2022-01-04 18:58:32 +00:00
Nick O'Leary
e76000b713 Merge pull request #3311 from node-red/strip-empty-env
Avoid adding empty env properties to tabs/groups
2022-01-04 18:57:53 +00:00
Nick O'Leary
332b372e31 Merge pull request #3310 from node-red/group-env-fix
Update Function to use correct api to access env vars
2022-01-04 18:57:33 +00:00
Nick O'Leary
cb3fcb7bfa Ctrl-Shift-Click on node should add to existing selection 2022-01-03 23:29:42 +00:00
Nick O'Leary
bef641609e Select links when nodes on either side selected 2022-01-03 23:29:12 +00:00
Nick O'Leary
24b52f09df Add RED.nodes.getNodeLinks to help node-link lookup 2022-01-03 23:27:25 +00:00
Nick O'Leary
942b17b807 Fix incorrect clearing of blank payload property in Inject node
Fixes #3316
2022-01-03 21:51:49 +00:00
Nick O'Leary
cf19d7f3ad Fix findPreviousVisibleTab action
Fixes #3320
2022-01-03 21:20:23 +00:00
Nick O'Leary
ebd62a4112 Fix storing hidden tab state when not hidden via action
Fixes #3305
2021-12-28 10:29:42 +00:00
Nick O'Leary
9af7357ca4 Avoid adding empty env properties to tabs/groups
Fixes #3306
2021-12-27 12:03:40 +00:00
Nick O'Leary
0dbc35c252 Update Function to use correct api to access env vars
Fixes #3299
2021-12-27 11:55:10 +00:00
Dave Conway-Jones
c9f03f1ac5 better tests
(and a small fix as a result)
2021-12-27 09:51:50 +00:00
Dave Conway-Jones
02bd292b8c fix and test 2021-12-26 16:12:47 +00:00
Dave Conway-Jones
e5f1029d0c fix variable names for test 2021-12-26 15:37:41 +00:00
Dave Conway-Jones
cae247160f Let tcprequest split incoming strings on delimiter (as per tcpin node)
and fixup i18n messages
2021-12-26 15:28:16 +00:00
Dave Conway-Jones
6692b1992c TCP add tls option to inbound nodes 2021-12-26 12:12:31 +00:00
Dave Conway-Jones
0937837b7f Add TLS config option to TCP client nodes
(not yet when in server mode)
2021-12-24 16:18:00 +00:00
Kunihiko Toumura
828888490a check availability of config node on deploy 2021-12-21 18:10:42 +09:00
Kazuhito Yokoi
91cb6ba73b Add Japanese translations for hidden flow (#3302) 2021-12-20 09:34:41 +00:00
Nick O'Leary
4ee4d32b2e Merge pull request #3301 from kazuhitoyokoi/master-fixwelcometour
Fix hide icon in tour guide
2021-12-17 11:19:27 +00:00
Kazuhito Yokoi
8df630a2f5 Fix hide button icon in tour guide 2021-12-17 19:37:29 +09:00
Dave Conway-Jones
2cad42870e chaneg exec, file and inject node to use node.debug rather than -v flag 2021-12-17 09:58:10 +00:00
Nick O'Leary
43651135f3 Replace selectedLinks array with wrapped Set object 2021-12-13 21:02:55 +00:00
Nick O'Leary
ecaf866613 Fix serialisation of selection in view 2021-12-07 09:29:11 +00:00
Nick O'Leary
5ea3329b36 Allow multiple links to be selected by ctrl-click 2021-12-07 09:19:27 +00:00
Nick O'Leary
7bb7149f4c Snap nodes on grid using either edge as reference 2021-12-05 23:23:57 +00:00
Nick O'Leary
5856d043ca Add node wiring actions 2021-12-05 19:52:47 +00:00
Nick O'Leary
7cd3e49f04 Merge branch 'master' into dev 2021-12-05 19:32:13 +00:00
Nick O'Leary
173e75175e update changelog 2021-12-02 09:30:41 +00:00
Nick O'Leary
800006dd76 Merge pull request #3282 from node-red/tab-buttons
Change tab hide button icon to an eye and add search option
2021-12-02 09:29:28 +00:00
Nick O'Leary
a824b6910a Add tooltips to tabs and close/hide buttons 2021-12-02 09:23:13 +00:00
Nick O'Leary
dcea382b38 Change tab hide button icon to an eye and add search option 2021-12-01 17:54:17 +00:00
Nick O'Leary
d9f976baea Update package for 2.1.4 2021-12-01 16:06:03 +00:00
Nick O'Leary
1fa13efe19 Merge pull request #3281 from node-red/i18n-space-ns-fix
Fix i18n handling of namespaces with spaces in
2021-12-01 15:15:38 +00:00
Nick O'Leary
33af5cd7c6 Apply i18n namespace fix to runtime component 2021-12-01 15:15:08 +00:00
Nick O'Leary
7cb8f97ef1 Fix i18n handling of namespaces with spaces in 2021-12-01 15:08:04 +00:00
Nick O'Leary
bf965a9cde Merge pull request #3280 from node-red/auto-complete-fix
Trigger change event when autoComplete fills in input
2021-12-01 14:38:10 +00:00
Nick O'Leary
17ffff685a Trigger change event when autoComplete fills in input
Fixes #3271
2021-12-01 14:35:45 +00:00
Nick O'Leary
ae76271cff Merge pull request #3279 from node-red/cn-fix
Apply CN i18n fix
2021-12-01 14:26:37 +00:00
Nick O'Leary
8f3a96d615 Apply CN i18n fix
Fixes #3272

Verified with Google Translate this is a better translation for
"Export to library"
2021-12-01 14:25:35 +00:00
Nick O'Leary
30e750dfe5 Merge pull request #3278 from node-red-hitachi/fix-env-var-access
fix env var access using $parent for groups
2021-12-01 14:23:44 +00:00
Nick O'Leary
682dff7c6f Merge pull request #3273 from node-red-hitachi/fix-config-select-name
fix select menu label of config node to use paletteLabel
2021-12-01 14:02:55 +00:00
Nick O'Leary
85415eb8a8 Merge pull request #3275 from node-red-hitachi/fix-node-conflict
fix removed tab not to cause node conflict
2021-12-01 14:01:50 +00:00
Nick O'Leary
68a80b9244 Merge pull request #3277 from node-red/debug-path-fix
Add proper error handling for 404 errors when serving debug files
2021-12-01 13:48:25 +00:00
Hiroyasu Nishiyama
c331da7323 fix env var access using $env for groups 2021-11-27 19:29:57 +09:00
Nick O'Leary
04ffa06221 Add proper error handling for 404 errors when serving debug files 2021-11-26 11:30:51 +00:00
Hiroyasu Nishiyama
1f0690c6ec fix removed tab not to cause node conflict 2021-11-25 13:26:52 +09:00
Hiroyasu Nishiyama
711467abcd fix select menu label of config node to use paletteLabel 2021-11-24 14:06:45 +09:00
Nick O'Leary
9439cd0e3d Merge pull request #3179 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for Node-RED v2.1.0-beta.1
2021-11-22 23:25:33 +00:00
Nick O'Leary
314c19650d Merge pull request #3239 from node-red/group-diff-fix
Group diff fix
2021-11-22 23:25:19 +00:00
Nick O'Leary
ed6afcd802 Merge pull request #3251 from node-red/regex-debug
Handle RegExp types in Debug sidebar
2021-11-22 23:24:55 +00:00
Nick O'Leary
082d4fe8e1 Merge pull request #3252 from node-red/disable-hidden-flow
Only toggle disabled workspace flag if on activeWorkspace
2021-11-22 23:24:39 +00:00
Nick O'Leary
cd23b44506 Merge pull request #3253 from node-red/clear-disabled-status
Do not show status for disabled nodes
2021-11-22 23:24:18 +00:00
Nick O'Leary
46b6b024b9 Merge pull request #3269 from node-red/auth-id
Include full user object on login audit events
2021-11-22 23:24:00 +00:00
Nick O'Leary
cb88cc35e5 Merge pull request #3261 from node-red/fix-delay-2nd-output-when-in-per-topic-mode
fix 2nd output when in rate limit per topic modes
2021-11-22 23:23:35 +00:00
Nick O'Leary
75c0c44809 Merge pull request #3265 from kazuhitoyokoi/master-fixtourguide
Set dimension value for tour guide
2021-11-22 23:23:03 +00:00
Nick O'Leary
a091b82ba9 Merge pull request #3263 from node-red/ti-fix
Avoid redundant initialisation of TypedInput type
2021-11-22 23:20:40 +00:00
Nick O'Leary
a3b8f022e6 Merge pull request #3270 from bonanitech/node-port-label-color
Don't let themes change flow port label color
2021-11-22 23:20:30 +00:00
Nick O'Leary
279fcb7c51 Merge pull request #3267 from node-red-hitachi/fix-link-target-select
fix to show link target when selected
2021-11-22 23:20:04 +00:00
Mauricio Bonani
49a9376073 Change flow port label CSS variable 2021-11-22 16:24:14 -05:00
Nick O'Leary
96840ede56 Include full user object on login audit events 2021-11-22 19:58:48 +00:00
Hiroyasu Nishiyama
7e7f481f99 fix to show link target when selected 2021-11-19 17:14:22 +09:00
Kazuhito Yokoi
3edbf52bc6 Set dimension value for tour guide 2021-11-18 15:38:49 +09:00
Nick O'Leary
fba6e801fc Avoid redundant initialisation of TypedInput type 2021-11-16 14:32:09 +00:00
Nick O'Leary
720a163273 Bump dev version 2021-11-15 23:38:01 +00:00
Nick O'Leary
a9b12e5172 Add search history to main search box 2021-11-15 23:24:57 +00:00
Dave Conway-Jones
6ac0c0a367 fix 2nd output when in rate limit per topic modes
to fix issue found in #3260
2021-11-15 14:05:16 +00:00
Kazuhito Yokoi
300402d253 Add Japanese translation for tour button 2021-11-12 18:15:57 +09:00
Kazuhito Yokoi
0d9bfae503 Add Japanese translations to tour guide for first flow 2021-11-11 13:02:52 +09:00
Nick O'Leary
bfe0d3b8a3 Do not show status for disabled nodes
Fixes #3249
2021-11-08 21:12:13 +00:00
Nick O'Leary
5fdd9c0546 Only toggle disabled workspace flag if on activeWorkspace
Fixes #3244
2021-11-08 20:38:34 +00:00
Nick O'Leary
b6570a16b8 Handle RegExp types in Debug sidebar
Fixes #3245
2021-11-08 20:24:10 +00:00
Kazuhito Yokoi
8e2d3ea16f Remove unused messages in language catalog 2021-11-08 13:46:24 +09:00
Kazuhito Yokoi
bc2c81f058 Add Japanese translations for link call, mqtt and file nodes 2021-11-08 13:38:17 +09:00
Nick O'Leary
3e0f080ea7 Merge pull request #3242 from node-red/inject-fix
Do not modify inject node props in oneditprepare
2021-11-01 21:40:47 +00:00
Nick O'Leary
679e07189d Do not modify inject node props in oneditprepare
Fixes #3241
2021-11-01 20:10:52 +00:00
Nick O'Leary
a38ebef100 Display group changes property in diff view 2021-10-28 12:14:56 +01:00
Nick O'Leary
b8ad6475e1 Strip off env property if it is empty - avoid inflated flow files 2021-10-28 12:14:34 +01:00
Nick O'Leary
0f0cb3ac6d Fix setting object env var properties so they can be undone 2021-10-28 12:14:03 +01:00
Nick O'Leary
d3efb9d7cc Merge pull request #3238 from node-red/treeLister-gutter-fix
Fix treeList gutter calculation to handle floating gutters
2021-10-28 10:46:04 +01:00
Nick O'Leary
84a237d3f5 Fix treeList gutter calculation to handle floating gutters
Fixes #3234
2021-10-28 10:44:50 +01:00
Nick O'Leary
e6de52eede Merge pull request #3236 from hardillb/http-basic-username-only
HTTP Basic Auth should always add : between username and password even if empty
2021-10-28 10:14:02 +01:00
Nick O'Leary
98aee964d7 Merge pull request #3237 from node-red/remove-de-style
Remove styling from de locale files
2021-10-28 10:11:54 +01:00
Nick O'Leary
570e5442e0 Remove styling from de locale files
Fixes #3230
2021-10-28 10:10:22 +01:00
Ben Hardill
b77a2dc353 Better fix 2021-10-28 10:08:28 +01:00
Ben Hardill
87af31de20 HTTP Basic Auth should always add : to username
fix for #3235
2021-10-28 09:18:17 +01:00
Nick O'Leary
cfe201dbe1 Bump for 2.1.3 2021-10-26 10:42:53 +01:00
Nick O'Leary
6ccdab35e0 Merge pull request #3227 from node-red/reload-node-settings
Refresh editor settings whenever a node is added or enabled
2021-10-26 09:51:41 +01:00
Nick O'Leary
55b9f36b45 Merge pull request #3229 from node-red/spinner-css
Revert spinner css change that made it shrink in some cases
2021-10-26 09:51:31 +01:00
Nick O'Leary
fbcb1130c9 Merge pull request #3221 from hardillb/disable-tours
Add environment variable to enable/disable tours
2021-10-26 09:47:17 +01:00
Nick O'Leary
d4f7a6d2bc Revert spinner css change that made it shrink in some cases 2021-10-26 09:44:32 +01:00
Nick O'Leary
8a19f71abe Refresh editor settings whenever a node is added or enabled
Fixes #3217

This ensures any node-provided settings are loaded and ready
for use by the nodes
2021-10-25 20:48:01 +01:00
Nick O'Leary
fb153757b5 Merge pull request #3225 from node-red/i18n-fix
Fix loading non-default language files leaving runtime in wrong locale
2021-10-25 16:06:12 +01:00
Nick O'Leary
4f175fc93e Fix loading non-default language files leaving runtime in wrong locale 2021-10-25 15:58:07 +01:00
Nick O'Leary
2d4ca7cec0 Merge pull request #3224 from node-red/import-msg-fix
Fix import notification message when importing config nodes
2021-10-25 15:12:46 +01:00
Nick O'Leary
bf0ea89969 Fix import notification message when importing config nodes 2021-10-25 15:08:30 +01:00
Nick O'Leary
073f0c2a20 Merge pull request #3223 from node-red/ti-fix
Handle changing types of TypedInput repeatedly
2021-10-25 13:55:38 +01:00
Nick O'Leary
ba83be9062 Handle changing types of TypedInput repeatedly
Fixes #3222
2021-10-25 13:54:42 +01:00
Ben Hardill
2e7188ea4f Add environment variable to enable/disable tours
NODE_RED_ENABLE_TOURS

Also had to patch editor-api/lib/editor/themes.js to pass
`editorTheme.tours` to the editor.
2021-10-25 11:25:31 +01:00
Nick O'Leary
5a012182d9 Update gen-publish script to update 'next' tag for main releases 2021-10-25 10:05:23 +01:00
Nick O'Leary
b855438af6 Fix changelog 2021-10-25 09:42:44 +01:00
Nick O'Leary
2ffea143e7 Bump for 2.1.2 2021-10-25 09:38:32 +01:00
Duncan Bellamy
61d85b49e6 Remove bash dependency (#3216)
Change backticks to dollar sign and parentheses
2021-10-25 08:44:49 +01:00
Nick O'Leary
35f617e96c Merge pull request #3213 from GerwinvBeek/markdown-regex
Improved regex for markdown renderer
2021-10-24 23:04:20 +01:00
Nick O'Leary
6b6ad47c35 Merge pull request #3220 from node-red/ti-fixes
Fix TypedInput initialisation
2021-10-24 22:58:55 +01:00
Nick O'Leary
e57183ed0e Merge pull request #3219 from Steve-Mcl/mqtt-use-datatype
fix datatype in node config not used. fixes #3215
2021-10-24 22:58:36 +01:00
Nick O'Leary
ecfd61a822 Fix TypedInput initialisation handle 2021-10-24 22:53:22 +01:00
Steve-Mcl
153f87704b fix datatype in node config not used. fixes #3215 2021-10-24 22:21:44 +01:00
Gerwin van Beek
836f7d2163 Improved regex for markdown renderer 2021-10-22 17:05:37 +02:00
Nick O'Leary
d4d6f71cf4 Bump for 2.1.1 2021-10-22 09:27:52 +01:00
Nick O'Leary
42a9da006e Merge pull request #3212 from node-red/fix-tour-guide-width
Ensure tourGuide popover doesn't fall offscreen
2021-10-22 09:26:31 +01:00
Nick O'Leary
2bd5c4f527 Ensure tourGuide popover doesn't fall offscreen
Only handles the left hand edge - will need expanding to cover
the other edges as needed
2021-10-22 09:24:40 +01:00
Nick O'Leary
6a49b5c106 Merge pull request #3210 from node-red/fix-old-inject-migration
Fix issue with old inject nodes that migrated topic to 'string' type
2021-10-22 09:16:54 +01:00
Nick O'Leary
23e14d1b72 Merge pull request #3211 from node-red/cache-bust
Add cache-busting query params to index.mst
2021-10-22 09:16:45 +01:00
Nick O'Leary
f4f11c8884 Add cache-busting query params to index.mst 2021-10-22 09:14:01 +01:00
Nick O'Leary
2b220abdb7 Fix issue with old inject nodes that migrated topic to 'string' type 2021-10-22 09:01:24 +01:00
Nick O'Leary
c1d947ebe3 Merge pull request #3207 from node-red/fix-debug-all
Fix TypedInput validation of type without options
2021-10-21 14:45:09 +01:00
Nick O'Leary
d695cf392e Fix TypedInput validation of type without options
Fixes #3206
2021-10-21 14:44:15 +01:00
Nick O'Leary
21304a695c Fix tcprequest deprecation warnings 2021-10-21 09:40:25 +01:00
Nick O'Leary
fa51b06c46 Merge pull request #3205 from node-red/dev
Node-RED 2.1.0
2021-10-21 09:13:09 +01:00
Nick O'Leary
7560bb8d7b Bump for 2.1.0 2021-10-21 09:08:35 +01:00
Nick O'Leary
fc9d65abcc Merge pull request #3204 from node-red/tcp-req-text-output
Add string option to TCP request node output
2021-10-20 09:40:44 +01:00
Dave Conway-Jones
a7413cccd0 reuse existing labels for tcp request buffer/string option 2021-10-20 09:36:08 +01:00
Nick O'Leary
7610353f07 Position popover properly on a scrolled page
Not needed in the core editor, but needed in the docs pages that can scroll
2021-10-19 22:22:53 +01:00
Dave Conway-Jones
d3f978c90c Add optional string type output to tcp request node
to be similar to tcp in. node
2021-10-19 21:34:23 +01:00
Kazuhito Yokoi
b55a8ef62a Merge branch 'dev' into dev-addjpn 2021-10-19 19:20:32 +09:00
Nick O'Leary
8d79deffb5 Merge pull request #3202 from node-red/tour-guide-fix
Fixes from 2.1.0-beta.2
2021-10-19 10:20:24 +01:00
Nick O'Leary
8158487c3e Reduce churn in setting Switch rule typedInput type 2021-10-19 10:12:13 +01:00
Nick O'Leary
0cc061196d Handle changing value before type without reseting TypedInput 2021-10-19 10:11:37 +01:00
Nick O'Leary
d0ec055222 Fix Inject/Change node restoring typedInput options 2021-10-18 21:23:42 +01:00
Nick O'Leary
ae12ddd32b Change node: Fix deepCopy checkbox spacing on Safari 2021-10-18 20:38:46 +01:00
Nick O'Leary
31da3adaa9 Fix hide/show of palette in tour and add MQTT nodes 2021-10-18 20:29:37 +01:00
Nick O'Leary
9fd5213f13 Merge pull request #3201 from node-red/link-fixes
Fix saving link out node links
2021-10-18 09:43:30 +01:00
Nick O'Leary
de882f5849 Fix saving link out node links 2021-10-18 09:32:00 +01:00
Nick O'Leary
fded1e0021 Refix #3170 - copy switch rule type when adding new rule 2021-10-14 21:47:49 +01:00
Nick O'Leary
6cb06c146d Update for 2.1.0-beta.2 2021-10-14 13:07:58 +01:00
Nick O'Leary
b8f1386ad0 Dynamic MQTT connections (#3189)
* add mqtt-control
- adds auto-connect option to broker
- add new node mqtt-control
- adds i18n messages
- adds documentation

* documentation tweaks

* built in documentation improvements

* fix tip layout causing oversized editor

* remove unused requires

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

* ensure clientid is updated dynamically

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

* Remove dynamic label

* remove redundant mqtt-control code left overs

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

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

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

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

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

* delete temprary debugger statements

* Final rework of dynamic mqtt connections

Co-authored-by: Steve-Mcl <sdmclaughlin@gmail.com>
2021-10-14 12:05:06 +01:00
Nick O'Leary
2b38b5ea50 Merge pull request #3200 from node-red/fix-link-call-label
Fix link call label
2021-10-14 12:04:25 +01:00
Nick O'Leary
2f707a6b16 Merge pull request #3199 from node-red/switch-project-fix
Fix switching projects
2021-10-14 12:04:07 +01:00
Nick O'Leary
d4c2fcd559 Merge pull request #3198 from node-red/locale-fix
Use locale setting when installing/enabling node
2021-10-14 12:03:54 +01:00
Nick O'Leary
082970cdb7 Merge pull request #3197 from node-red/tour-projects-fix
Do not show projects-wecome dialog until welcome tour completes
2021-10-14 12:03:41 +01:00
Nick O'Leary
fe97c78977 Merge pull request #3196 from node-red/link-fixes
Fix converting selection to subflow
2021-10-14 12:03:28 +01:00
Nick O'Leary
79394aa69f Merge pull request #3195 from node-red/keyboard-fix
Avoid conflicts with native browser cmd-ctrl type shortcuts
2021-10-14 12:03:17 +01:00
Nick O'Leary
21fd6e3c21 Ensure link call node label updates when link-in changes 2021-10-14 12:00:27 +01:00
Nick O'Leary
de4944cd83 Ensure node is resized if its label changes 2021-10-14 11:53:55 +01:00
Nick O'Leary
3fde5c27ed Fix switching projects 2021-10-14 11:07:49 +01:00
Nick O'Leary
e1d492813e Use locale setting when installing/enabling node
Fixes #3194
2021-10-14 10:57:11 +01:00
Nick O'Leary
48d0ee3b6d Do not show projects-wecome dialog until welcome tour completes
Fixes #3193
2021-10-14 10:17:27 +01:00
Nick O'Leary
eebb64901c Fix converting selection to subflow
Fixes #3191
2021-10-14 09:48:35 +01:00
Nick O'Leary
60e0ed2af6 Avoid conflicts with native browser cmd-ctrl type shortcuts
Fixes #3190
2021-10-14 09:34:46 +01:00
Nick O'Leary
f030694ef4 Merge pull request #3183 from node-red/debug-filter-opts
Redesign debug filter options and make them persistant
2021-10-13 14:47:08 +01:00
Nick O'Leary
e9ed13459a Merge pull request #3184 from node-red/inject-spinner-css
Widen Inject interval box for >1 digit
2021-10-13 14:41:52 +01:00
Nick O'Leary
af1e38fdf7 Merge pull request #3185 from node-red/switch-focus-fix
Fix rule focus when switch 'otherwise' rule is used
2021-10-13 14:41:42 +01:00
Nick O'Leary
b12900e680 Merge pull request #3186 from node-red/context-tools-fix
Ensure message tools stay attached to top-level entry in Debug/Context
2021-10-13 14:41:29 +01:00
Nick O'Leary
44aa1f4a5e Filter out Link Out Return nodes in Link In edit dialog
Fixes #3187
2021-10-13 14:23:43 +01:00
Nick O'Leary
9425548a85 Ensure message tools stay attached to top-level entry in Debug/Context
Fixes #3180
2021-10-12 21:11:20 +01:00
Nick O'Leary
bfd4fc81fe Widen Inject interval box for >1 digit 2021-10-12 17:25:30 +01:00
Nick O'Leary
439af2a325 Fix rule focus when switch 'otherwise' rule is used
Fixes #3182
2021-10-12 17:25:13 +01:00
Nick O'Leary
3204b04455 Overhaul Debug sidebar filter and clear button options 2021-10-12 17:24:51 +01:00
Nick O'Leary
bed1be14ba Allow toolip action/content to be updated 2021-10-12 17:24:46 +01:00
Nick O'Leary
7cd92faf0d Separate 'focus' from 'selected' state in treeList 2021-10-12 17:24:41 +01:00
Nick O'Leary
be7e28af5d Update package dependencies 2021-10-12 17:24:10 +01:00
Nick O'Leary
8eaa762ec5 Merge branch 'dev' of github.com:node-red/node-red into dev 2021-10-11 10:01:46 +01:00
Nick O'Leary
953a9f7cd4 Update to latest node-red-admin 2021-10-11 09:58:55 +01:00
Kazuhito Yokoi
36f099d68b Add Japanese translations for Node-RED v2.1.0-beta.1 2021-10-11 16:02:18 +09:00
Nick O'Leary
c8fd5090bd Merge pull request #3178 from node-red/fix-md-help
Fix table tokenizer for node help markdown renderer
2021-10-08 18:14:10 +01:00
Nick O'Leary
155e1be494 Fix table tokenizer for node help markdown renderer 2021-10-08 17:56:24 +01:00
Nick O'Leary
cf5e125cb3 Merge pull request #3175 from node-red/tab-disabled-fix
Ensure tab state updates properly when toggling enable state
2021-10-08 15:52:16 +01:00
Nick O'Leary
764fc8477d Merge pull request #3176 from node-red/long-module-names
Improve handling of long labels in TreeList
2021-10-08 15:52:05 +01:00
Nick O'Leary
d35e62f8cf Merge pull request #3177 from node-red/scroll-tab-bar
Shift-click tab scroll arrows to jump to start/end
2021-10-08 15:51:56 +01:00
Nick O'Leary
904babdd13 Shift-click tab scroll arrows to jump to start/end 2021-10-08 15:45:17 +01:00
Nick O'Leary
154d3842a8 Improve handling of long labels in TreeList 2021-10-08 15:30:43 +01:00
Nick O'Leary
edb8a120bd Ensure tab state updates properly when toggling enable state 2021-10-08 15:09:51 +01:00
Nick O'Leary
cdfeba0b82 Update changelog 2021-10-07 09:37:24 +01:00
Nick O'Leary
8ce1465e9f Merge branch 'master' into dev 2021-10-07 09:35:52 +01:00
Nick O'Leary
a296b1c9c8 Merge pull request #3172 from node-red/tour-210
Update welcome tour for 2.1.0-beta.1
2021-10-07 09:34:52 +01:00
Nick O'Leary
bb8d7058a4 Merge pull request #3173 from mailsvb/mailsvb-typo-http-request
Typo in http request set method translation
2021-10-07 09:33:03 +01:00
mailsvb
816cfa1c7e Typo in http request set method translation 2021-10-07 08:07:57 +02:00
Nick O'Leary
53938200fc Update changelog 2021-10-06 17:36:51 +01:00
Nick O'Leary
3885bb039d Merge branch 'dev' of github.com:node-red/node-red into dev 2021-10-06 17:35:34 +01:00
Nick O'Leary
4adad6e424 Merge pull request #3166 from node-red/output-discarded-rate-limit
Delay node: add option to send intermediate messages on separate output
2021-10-06 17:34:44 +01:00
Nick O'Leary
5fb9531338 Merge branch 'pr_3146' into dev 2021-10-06 17:33:28 +01:00
Nick O'Leary
273d9c76a7 Add fallback when logging uncaughtExceptions 2021-10-06 17:33:04 +01:00
Nick O'Leary
79a1d6c561 RED.notifiy.update should not change fixed state unless explicitly set 2021-10-06 13:52:36 +01:00
Nick O'Leary
4b0eb8475d Fix updating shortcuts in menus after keyboard dialog closed 2021-10-06 13:51:38 +01:00
Nick O'Leary
3dc874b517 Update welcome tour for 2.1.0-beta.1 2021-10-06 12:05:49 +01:00
Nick O'Leary
8a3da1ce8d update changelog 2021-10-06 11:41:27 +01:00
Nick O'Leary
42d90542b5 Merge pull request #3171 from node-red/auto-complete
Add autoComplete widget and add to TypedInput for msg. props
2021-10-06 11:40:20 +01:00
Nick O'Leary
690a93d82d Update changelog 2021-10-06 10:04:30 +01:00
Nick O'Leary
8042fe4e2b Merge branch 'master' into dev 2021-10-06 09:12:16 +01:00
Nick O'Leary
a27ce375db Update package dependencies 2021-10-06 09:11:20 +01:00
Nick O'Leary
db3688799d Merge pull request #3170 from node-red/switch-rule-type
Copy previous rule type when adding rule to switch node
2021-10-05 23:32:17 +01:00
Nick O'Leary
a88be35292 Merge pull request #3168 from node-red/improve-unit-test-coverage
Improve unit test coverage
2021-10-05 23:31:58 +01:00
Nick O'Leary
e2d7fcbfc2 Add lots of docs to RED.popover 2021-10-05 23:18:29 +01:00
Nick O'Leary
421d155586 Add some docs to autoComplete widget 2021-10-05 20:41:21 +01:00
Nick O'Leary
7f9e318214 Add autoComplete widget and add to TypedInput for msg. props 2021-10-05 17:59:44 +01:00
Nick O'Leary
57386edb7c Merge branch 'dev' of github.com:node-red/node-red into dev 2021-10-05 11:26:19 +01:00
Nick O'Leary
94d5ba4550 Tweak table styles 2021-10-05 11:25:35 +01:00
Nick O'Leary
2a0b4ea828 Merge pull request #3167 from node-red/httprequest-send-error
Add option to only send http response errors to Catch node
2021-10-05 10:44:08 +01:00
Nick O'Leary
893ef227d4 Merge pull request #3084 from Steve-Mcl/theme-monaco-theme
Permit plugin theme to theme monaco editor
2021-10-05 10:42:08 +01:00
Nick O'Leary
1fe6e5a00d Merge pull request #3104 from hardillb/client-editor-fixes
Fix editor-client package.json
2021-10-05 10:40:28 +01:00
Nick O'Leary
6c96cde73c Copy previous rule type when adding rule to switch node 2021-10-05 10:34:46 +01:00
Steve-Mcl
2b12834d53 fix typo in boolean logic test 2021-10-05 00:06:25 +01:00
Steve-Mcl
8a2e74b3b8 fix grunt errors 2021-10-04 23:52:55 +01:00
Steve-Mcl
aa1721ab3d Only style ordered list with dl.message-properties 2021-10-04 23:36:22 +01:00
Steve-Mcl
c0a256306b clean up temp enable flag 2021-10-04 23:34:38 +01:00
Steve-Mcl
f0b03b4ada add styling for mardown tables 2021-10-04 23:02:23 +01:00
Steve-Mcl
5503f53af2 Handle marked adding a <p> to ordered lists.
- This causes the text after a number in the numbered list to be on a new line.
- To fix this, a new style rule is required
2021-10-04 23:00:30 +01:00
Steve-Mcl
a89d294b27 Custom markdown rendering to generate style guide 2021-10-04 22:59:34 +01:00
Nick O'Leary
012e1cbcc5 Improve unit test coverage 2021-10-04 17:53:14 +01:00
Nick O'Leary
3759e0f778 Add option to only send http response errors to Catch node 2021-10-04 14:04:59 +01:00
Nick O'Leary
1c18641699 Delay node: add option to send intermediate messages on separate output 2021-10-04 13:00:24 +01:00
Nick O'Leary
e50e2201b1 Merge pull request #3108 from sammachin/patch-2
change template characters
2021-10-04 10:57:04 +01:00
Nick O'Leary
1419729458 Merge pull request #3163 from node-red/notification-fixes
Allow RED.notify.update to modify any notification setting
2021-10-04 10:56:26 +01:00
Nick O'Leary
c14177b0e8 Merge pull request #3160 from node-red/filter-configs
Allow a node to specify a filter for the config nodes it can pick from
2021-10-04 10:56:16 +01:00
Nick O'Leary
126df969b3 Merge pull request #3157 from node-red/relabel-file-nodes
Add paletteLabel to file nodes to make read/write more obvious
2021-10-04 10:56:01 +01:00
Nick O'Leary
f8ee92ba06 Merge pull request #3158 from node-red/node-install-err-reporting
Improve node install error reporting
2021-10-04 10:55:47 +01:00
Nick O'Leary
81a278dd8c Merge pull request #3156 from node-red/change-clone
Add option to deep-clone properties in Change node
2021-10-04 10:54:52 +01:00
Nick O'Leary
a98013806c Merge pull request #3164 from node-red/fix-http-response-layout
Fix sizing of HTTP Response header fields
2021-10-04 10:54:01 +01:00
Nick O'Leary
0171ffac6a Fix sizing of HTTP Response header fields 2021-10-04 10:52:36 +01:00
Nick O'Leary
7544241316 Allow RED.notify.update to modify any notification setting
Fixes #3162
2021-10-04 10:31:46 +01:00
Nick O'Leary
061afb3a94 Improve RED.actions api to ensure actions cannot be overridden
Also alows multiple args to be passed to an action.
2021-10-02 23:26:01 +01:00
Nick O'Leary
8a5eda9c1f Ensure type select button is not focussable when TypedInput only has one type 2021-10-02 23:25:56 +01:00
Nick O'Leary
f62040f0ec Merge pull request #3161 from node-red/tour-theme
Improve themeing of tourGuide
2021-10-02 23:24:22 +01:00
Nick O'Leary
f2e51779e4 Improve themeing of tourGuide 2021-10-02 23:15:35 +01:00
Nick O'Leary
da114fa3a5 Merge pull request #3152 from node-red/link-call
Add Link Call node
2021-10-01 15:59:43 +01:00
Nick O'Leary
3775a1657b Merge pull request #3153 from node-red/update-monaco
Update monaco to 0.28.1
2021-10-01 15:59:16 +01:00
Nick O'Leary
2bd7c4bc81 Merge pull request #3159 from node-red/multi-select-typedInput
Do better remembering TypedInput values whilst switching types
2021-10-01 15:40:59 +01:00
Nick O'Leary
253c489a33 Allow a node to specify a filter for the config nodes it can pick from 2021-09-30 19:44:34 +01:00
Nick O'Leary
ac84b6fe3f Do better remembering TypedInput values whilst switching types 2021-09-30 18:14:45 +01:00
Nick O'Leary
8761e61439 Include load error in notification of unknown node 2021-09-30 15:39:45 +01:00
Nick O'Leary
29e903e1c8 Improve error reporting when installing bad nodes 2021-09-30 15:28:09 +01:00
Nick O'Leary
ec27e19e3f Add unit tests for Join restartTimeout and update help 2021-09-30 10:59:45 +01:00
Nick O'Leary
5df0dae11a Merge pull request #3121 from magma1447/dev
SPLIT Join node - support for msg.resetTimeout …
2021-09-30 10:45:42 +01:00
Nick O'Leary
7fffc1a36d Merge pull request #3069 from node-red/delay-push-to-front
Add push to front of rate limit queue.
2021-09-30 10:44:14 +01:00
Nick O'Leary
f3d0179834 Add paletteLabel to file nodes to make read/write more obvious 2021-09-30 10:41:18 +01:00
Nick O'Leary
8bf69c598a Ensure tab scroll handles are cleared when hide-all tabs run 2021-09-30 10:34:18 +01:00
Nick O'Leary
aa5fad6628 Add deep copy option to Change node 'set' action 2021-09-30 09:49:51 +01:00
Nick O'Leary
b0f1fad4e2 Change label for 'set' change node action to make it clearer 2021-09-29 19:43:30 +01:00
Nick O'Leary
3b6d0995b4 Merge pull request #3155 from node-red/config-select
Ensure config-node select inherits width properly from input
2021-09-29 19:14:12 +01:00
Nick O'Leary
0cbf4ac37d Better regex for parsing config select width placeholder style 2021-09-29 19:12:13 +01:00
Nick O'Leary
9ccffee82c Ensure config-node select inherits width properly from input
Fixes #3001
2021-09-29 19:06:34 +01:00
Nick O'Leary
1b38e2eedf Merge pull request #3154 from node-red/fix-windows-themes
Fix serving of theme files on Windows
2021-09-29 18:00:41 +01:00
Nick O'Leary
ce87abe96e Fix serving of theme files on Windows
Fixes #3097
2021-09-29 17:59:25 +01:00
Nick O'Leary
becbda8483 Update monaco types 2021-09-29 17:28:06 +01:00
Nick O'Leary
da210e2ae4 Ignore top-level await errs in monaco
Fixes #3138
2021-09-29 17:19:12 +01:00
Nick O'Leary
d4fc6feeba Update to monaco 0.28.1 2021-09-29 17:17:34 +01:00
Nick O'Leary
f1cbca8d76 Add link-call help 2021-09-29 14:46:09 +01:00
Nick O'Leary
dfd9364061 Add timeout option to link-call node 2021-09-29 14:28:12 +01:00
Nick O'Leary
1931395fdb Add basic link-call/return tests 2021-09-29 13:49:55 +01:00
Nick O'Leary
b01fd24e15 Add link-call node and add return mode for link-out node 2021-09-29 10:45:00 +01:00
Nick O'Leary
c9d1329fc2 Add 'radio' option to treeList 2021-09-29 10:44:09 +01:00
Nick O'Leary
ab2d3bfd80 Fix RED.nodes.filterNodes when searching full node set 2021-09-29 10:43:41 +01:00
Nick O'Leary
36bb172f29 Merge pull request #3151 from GerwinvBeek/dev
Restore tab selection after merging conflicts
2021-09-29 00:43:25 +01:00
Gerwin van Beek
01e64be39d rename 2021-09-28 17:33:23 +02:00
Gerwin van Beek
4ebe160f6c Restore tab selection after merging conflicts 2021-09-28 17:19:22 +02:00
Nick O'Leary
b427eca21f Merge pull request #3136 from node-red/tour-guide-2
Add Tour Guide component
2021-09-27 17:59:05 +01:00
Nick O'Leary
3eb438c8d2 Merge pull request #3116 from node-red-hitachi/dev-httpreqlog
Extend HTTP request node to log detailed timing information
2021-09-27 17:56:34 +01:00
Nick O'Leary
068f425833 Merge pull request #3139 from node-red/fs-promises
Fixes to fs promises so that an electron built version runs
2021-09-27 17:51:10 +01:00
Nick O'Leary
b3c84242dc Bump version for 2.1.0-beta.1 2021-09-27 17:50:09 +01:00
Nick O'Leary
24672d91d8 Fix linting error in tourGuide 2021-09-27 17:49:38 +01:00
Nick O'Leary
4422af26ec Try to handle smaller screen sizes in tourGuide 2021-09-27 17:35:49 +01:00
Nick O'Leary
5329e803e2 Add keyboard handling to tourGuide - escape/enter 2021-09-27 17:35:24 +01:00
Nick O'Leary
ad542b91fa Add i18n support to tours 2021-09-27 17:34:30 +01:00
Nick O'Leary
e9e03c945b Add core:show-welcome-tour action and logic to display on first-run 2021-09-27 16:42:51 +01:00
Nick O'Leary
e20cfb3dae Ignore dynamicImport warning from jshint 2021-09-26 20:46:43 +01:00
Nick O'Leary
48baac916c Add initial tour-guide feature 2021-09-26 20:46:38 +01:00
Nick O'Leary
adadf38b08 Allow menu items to be opened via css class 2021-09-26 20:46:33 +01:00
Nick O'Leary
d4e1469450 Allow default tab to be specified when editing flows/groups/subflows 2021-09-26 20:46:28 +01:00
Nick O'Leary
2c456f044f Allow popovers to be moved and styled 2021-09-26 20:46:23 +01:00
Nick O'Leary
228c15ace3 Fix icon position in dropdown menu 2021-09-26 20:38:55 +01:00
Nick O'Leary
a0d15e6e7b Fix menu error for workspace tab button 2021-09-26 20:08:15 +01:00
Dave Conway-Jones
2fe78cf971 Update packages/node_modules/@node-red/util/lib/i18n.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-09-25 14:51:47 +01:00
Nick O'Leary
5ec3544340 Merge pull request #3148 from node-red/login-fixes
Improve error reporting with oauth login strategies
2021-09-24 22:40:15 +01:00
Nick O'Leary
5443a17775 Merge pull request #3120 from node-red/hide-flows
Allow tabs to be hidden
2021-09-24 22:39:42 +01:00
Nick O'Leary
40d60e4eb3 Add allowUpdate to settings file 2021-09-24 22:23:50 +01:00
Nick O'Leary
f3312a6403 Merge pull request #3143 from node-red/upgrade-deny-list
Add allowUpdate feature to externalModules.palette
2021-09-24 22:20:58 +01:00
Nick O'Leary
e638b55b30 Merge pull request #3149 from hardillb/coreNodesDir-false
Allow coreNodesDir to be set to false
2021-09-24 22:20:11 +01:00
Nick O'Leary
5caa76a8b3 Widden default menu to handle shortcuts 2021-09-24 22:15:02 +01:00
Nick O'Leary
901a5ce9d2 Better fade effect when hiding tabs 2021-09-24 22:10:53 +01:00
Nick O'Leary
6ab74951f4 Fix menu shortcut display when menu label is long 2021-09-24 19:44:05 +01:00
Nick O'Leary
c2625d696d Merge branch 'dev' into hide-flows 2021-09-24 19:12:34 +01:00
Ben Hardill
77fb5ef2ab Allow coreNodesDir to be set to false
This will disable all core nodes
2021-09-22 10:21:23 +01:00
Steve-Mcl
c20ca3399e codeEditor.options should take precedence
- over plugins monacoOptions
2021-09-21 14:12:21 +01:00
Steve-Mcl
5825da9c76 Merge branch 'dev' into theme-monaco-theme 2021-09-21 14:06:54 +01:00
Nick O'Leary
e3853ae402 Improve error reporting with oauth login strategies 2021-09-20 11:48:51 +01:00
Renato Junior
bd142a9710 uncaughtException debug improvements
Changed error uncaughtException tracing
2021-09-17 22:02:48 -03:00
Dave Conway-Jones
4f23847546 Fix UDP node to not not use port if unassigned 2021-09-16 16:22:01 +01:00
Nick O'Leary
85820c571d Add unit tests for allowUpdate settings 2021-09-14 10:50:26 +01:00
Nick O'Leary
d9bed03025 Add allowUpdate feature to externalModules.palette 2021-09-13 21:25:12 +01:00
Nick O'Leary
d6e05962c9 Merge pull request #3140 from harmonic7/dev
Allow colouring of tab icon svg
2021-09-13 09:23:59 +01:00
Casey Wilkes
d32636ed6b Tweak theme tests for tabicon colour config 2021-09-10 23:16:42 +12:00
Dave Conway-Jones
bbf066f030 Fixes to fs promises so that electron version runs 2021-09-10 08:33:07 +01:00
Casey Wilkes
a3d2f6592e Add support for colouring tab icon in settings.js
```
editorTheme: {
    page: {
       tabicon: {
           icon: "full/path/of/tabicon.svg",
           colour: "#008f00"
    }
}
```

The old way still works also (but doesn't allow the tabicon to
be coloured:
```
editorTheme: {
    page: {
       tabicon: "full/path/of/tabicon.svg"
    }
}
```
2021-09-10 13:10:01 +12:00
Nick O'Leary
87b6327c5e Merge branch 'dev' into hide-flows 2021-09-07 13:53:45 +01:00
Nick O'Leary
abaebb329d Merge branch 'dev' into edit-panes-2 2021-09-07 13:42:49 +01:00
Nick O'Leary
8970fe412d Merge pull request #3124 from node-red/raise-lower-nodes
Allow nodes to be raised/lowered in the workspace
2021-09-07 12:19:56 +01:00
Nick O'Leary
9dc5ae21c4 Add menu options for raise/lower actions 2021-09-07 12:04:58 +01:00
Nick O'Leary
4132fb79a6 Allow nodes to be raised/lowered in the workspace 2021-09-07 11:49:31 +01:00
Nick O'Leary
9a4dc30604 Merge pull request #3110 from node-red/view-tools
Add align actions to editor
2021-09-07 11:49:10 +01:00
Nick O'Leary
192b542fe4 Add menu options for align tools and show shortcuts on menu 2021-09-07 11:47:04 +01:00
Kunihiko Toumura
490547cd3d Use httpRequestTimingLog for enable detailed timing log 2021-09-05 15:22:13 +09:00
Dave Conway-Jones
17f9829498 Fix for incorrect tcpout connection count
to Close #3098
seems to need dummy data receiver in order to recognise other callbacks.
2021-09-03 13:00:06 +01:00
Nick O'Leary
234e77fd06 Rename registerEditPane function 2021-09-02 22:12:32 +01:00
Nick O'Leary
87ac831c8a Simplify edit pane api 2021-09-02 21:59:30 +01:00
Nick O'Leary
4e92492165 Edit panes should not need to create own tray content div 2021-09-02 21:44:06 +01:00
Nick O'Leary
c3d0b1114f Fix jshint error 2021-09-02 16:37:13 +01:00
Nick O'Leary
4463a7d4ba Move envVar list component to own file 2021-09-02 16:14:35 +01:00
Nick O'Leary
741fe3dd90 Move tab edit dialog into editor and use new edit panes 2021-09-02 14:29:58 +01:00
Nick O'Leary
e910f3915d Initial refactor of edit dialogs to separate panes 2021-09-02 10:33:43 +01:00
Nick O'Leary
39aafc5007 Ensure treeList row has suitable min-height when no content
Fixes #3109
2021-09-02 10:26:54 +01:00
Nick O'Leary
9b83afae42 Fix typo in ko editor.json
Fixes #3119
2021-09-02 10:26:49 +01:00
Mauricio Bonani
bdf54f6cff Change fade color when hovering an inactive tab 2021-09-02 10:26:44 +01:00
Mauricio Bonani
f2a9887a12 Place close tab link in front of fade 2021-09-02 10:26:39 +01:00
bartbutenaers
4f4d78bfab Key and certificate as string or buffer 2021-09-02 10:26:34 +01:00
Nick O'Leary
3b460fb8fa Bump for 2.0.6 2021-09-02 09:00:54 +01:00
Nick O'Leary
ee15e9acc5 Update tar to latest 2021-09-02 08:21:10 +01:00
Nick O'Leary
48fce35fb3 Merge pull request #3128 from aksswami/master
Update tar to 6.1.9 to resolve vulnerability issue
2021-09-02 08:20:15 +01:00
Amit Kumar Swami
78899378c2 Update tar to 6.1.9 to resolve vulnerability issue 2021-09-02 13:36:41 +08:00
Hiroyasu Nishiyama
67404a327d merge getFlowSetting to getSetting 2021-09-01 22:26:31 +09:00
Sam Machin
702dfa4b79 Update packages/node_modules/@node-red/nodes/locales/en-US/function/80-template.html
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-08-31 16:59:32 +01:00
Nick O'Leary
2144407e41 Merge pull request #3117 from dschmidt/passport-callback-arity
Give passport verfiy callback wrapper the same arity as the original callback
2021-08-31 16:45:43 +01:00
Nick O'Leary
b36dd62c50 Remove console.log 2021-08-31 15:23:50 +01:00
Nick O'Leary
7026df7d96 Track hidden tabs in localStorage 2021-08-31 15:19:04 +01:00
Nick O'Leary
ed8e7afdf6 Add flow tab bar menu 2021-08-31 14:46:11 +01:00
Hiroyasu Nishiyama
d78e5932f9 update according to PR comments 2021-08-30 08:00:58 +09:00
Nick O'Leary
26d83bb9ea Ensure treeList row has suitable min-height when no content
Fixes #3109
2021-08-27 16:32:43 +01:00
Nick O'Leary
8e89b1bdf2 Fix typo in ko editor.json
Fixes #3119
2021-08-27 16:22:58 +01:00
Nick O'Leary
c880cc0987 Add core:hide-other-tabs and handle multiple tab selection 2021-08-27 11:46:30 +01:00
Nick O'Leary
0874ba7a03 Add core:show-last-hidden-tab and update keymap 2021-08-26 22:20:25 +01:00
Nick O'Leary
7962278475 Allow tabs to be hidden via x-button on tab 2021-08-26 12:06:35 +01:00
Nick O'Leary
c8949f5eeb Fix treeList gutter padding for outliner 2021-08-26 12:06:30 +01:00
Nick O'Leary
8108b93c5f Add core:toggle-tabs action 2021-08-26 12:06:25 +01:00
Nick O'Leary
9dbe531bf7 Remove duplicate hide-tab action 2021-08-26 12:06:20 +01:00
Nick O'Leary
46e2ff1001 Allow tabs to be hidden 2021-08-26 12:06:15 +01:00
Nick O'Leary
d2cdc67ec7 Add distribute-horizontally/vertically actions to editor 2021-08-26 12:05:56 +01:00
Nick O'Leary
56121203bf Rename align actions to match existing align-selection-to-grid 2021-08-26 12:05:51 +01:00
Nick O'Leary
e13133fd2b Add align actions to editor 2021-08-26 12:05:45 +01:00
Magnus Månsson
f20565fd16 Renamed resetTimeout to restartTimeout
Updated documentation differently
2021-08-25 21:53:37 +02:00
Magnus Månsson
cf2d5841f5 SPLIT Join node - support for payload.resetTimeout to restore original timeout 2021-08-25 12:52:10 +02:00
Dominik Schmidt
630d2ca926 Give passport verfiy callback wrapper the same arity as the original callback passed in via options 2021-08-23 15:57:44 +02:00
Nick O'Leary
34cb93794c Merge pull request #3106 from bonanitech/fade-on-hover
Fix fade colors when hovering over tabs
2021-08-23 12:34:42 +01:00
Nick O'Leary
122b5ba468 Merge pull request #3115 from bartbutenaers/https-settings-as-string
Key and certificate as string or buffer
2021-08-23 12:25:56 +01:00
Kunihiko Toumura
401466d6c0 Add timing log 2021-08-23 11:54:05 +09:00
bartbutenaers
7f2627dbc8 Key and certificate as string or buffer 2021-08-21 22:21:58 +02:00
Hiroyasu Nishiyama
6aecc3915c add support of environtment variable for tab & group 2021-08-19 21:15:13 +09:00
Dave Conway-Jones
ef1b3aa7f5 Add comment to info docs re queue depth limit setting 2021-08-18 14:05:42 +01:00
Sam Machin
1aaab2a814 change template characters
Added note about changing template characters
2021-08-12 16:41:32 +01:00
Mauricio Bonani
e93734b209 Change fade color when hovering an inactive tab 2021-08-10 14:26:41 -04:00
Mauricio Bonani
9e5218f6b4 Place close tab link in front of fade 2021-08-10 14:21:54 -04:00
Ben Hardill
711ec39327 Fix editor-client package.json
The main entry pointed to non-existant entry.

Also fixed error when loading nodes lazily
2021-08-06 21:16:34 +01:00
Steve-Mcl
08049252f2 permit plugin theme to change other monaco options 2021-07-31 10:24:48 +01:00
Nick O'Leary
f1e7ec0c6b Bump for 2.0.5 2021-07-30 13:28:52 +01:00
Nick O'Leary
23765d9139 Update tar dependency 2021-07-30 13:20:26 +01:00
Nick O'Leary
43febe269c Add support for maintenance streams in generate-publish-script 2021-07-30 13:19:36 +01:00
Nick O'Leary
40233c7702 Fix regression in Join node when manual joining array with msg.parts present
Fixes #3096
2021-07-30 13:17:05 +01:00
Nick O'Leary
27ed81614b Remove default ctrl-enter keybinding from monaco editor
Fixes #3093
2021-07-30 11:50:21 +01:00
Nick O'Leary
889d23e9bd Update changelog 2021-07-28 11:02:11 +01:00
Nick O'Leary
f8571023f6 Fix inject now button unable to send empty props 2021-07-28 10:59:16 +01:00
Nick O'Leary
6364e00202 Merge pull request #3092 from hardillb/http-req-ca-fix
Copy tls.cert to tls.certificate for GOT
2021-07-28 10:05:19 +01:00
Ben Hardill
a76c6f86c6 Add Testcase & Fix typo 2021-07-28 08:52:35 +01:00
Ben Hardill
555e815402 Copy tls.cert to tls.certificate for GOT 2021-07-27 22:19:35 +01:00
Steve-Mcl
8a1d81989b use codeEditor.options.theme 2021-07-26 21:02:11 +01:00
Nick O'Leary
ee9234b2c6 Bump for 2.0.4 2021-07-26 17:09:26 +01:00
Nick O'Leary
735b9c5844 Handle just-copied-but-not-deployed node with credentials in editor
Fixes #3090
2021-07-26 14:50:00 +01:00
Nick O'Leary
064f3eb3bc Fix RBE node handling of default topi property
Fixes #3087
2021-07-26 14:36:09 +01:00
Nick O'Leary
f1775d4fd1 Handle partially encoded url query strings in request node 2021-07-26 14:21:52 +01:00
Nick O'Leary
a9bc111c4f Merge pull request #3089 from hardillb/http-req-ca-fix
Fix support for supplied CA certs
2021-07-26 14:20:08 +01:00
Nick O'Leary
c100612473 Merge pull request #3085 from bonanitech/fade-colors
Fix tab fade CSS for when using themes
2021-07-26 14:08:15 +01:00
Ben Hardill
26087f8dc7 Fix support for supplied CA certs 2021-07-26 10:25:06 +01:00
Mauricio Bonani
36e75cb728 Fix tab fade CSS for when using themes 2021-07-25 11:08:55 -04:00
Steve-Mcl
d7a2fc2be4 use them plugin theme monacoTheme if present 2021-07-25 15:16:49 +01:00
Nick O'Leary
142176f194 Bump for 2.0.3 2021-07-23 14:38:43 +01:00
Nick O'Leary
c5892fc17e Fix HTML parsing when body is included in the select tag
Fixes #3079
2021-07-23 10:09:00 +01:00
Nick O'Leary
6e69cfbca4 Preserve case of user-provided http headers in request node
Fixes #3081
2021-07-23 09:55:32 +01:00
Nick O'Leary
775181f761 Set decompress to false for HTTP Request to keep 1.x compatibility
Fixes #3083
2021-07-23 08:57:44 +01:00
Nick O'Leary
36e83d628e Add unit tests for HTTP Request encodeURI and error response 2021-07-23 00:10:17 +01:00
Nick O'Leary
5f6fcb2bc0 Do not throw HTTP errors in request node
Fixes #3082

GOT will throw errors for non-successful http responses by default. We need to turn that
off to be consistent with the 1.x behaviour using the request module
2021-07-22 23:48:30 +01:00
Nick O'Leary
7b106e5650 Ensure uri is properly encoded before passing to got module
Fixes #3080
2021-07-22 23:47:32 +01:00
Nick O'Leary
79d9c83a2d Better detection of broken agent-base function patching 2021-07-22 11:19:25 +01:00
Nick O'Leary
269669ba28 Bump for 2.0.2 2021-07-22 11:17:48 +01:00
Nick O'Leary
4ef7240598 Handle other error types in Manage Palette view
We still get [object Object] appearing in the palette manager sometimes.
This fix nails down another cause - where the err property has a 'code'
but no 'message' property. This happens with the type_already_registered
error
2021-07-21 17:26:19 +01:00
Nick O'Leary
efdf689c31 Merge pull request #3076 from node-red/unpatch-https
Detect if agent-base has patch https.request and undo it
2021-07-21 17:07:29 +01:00
Nick O'Leary
f7606e92ca Detect if agent-base has patch https.request and undo it
Fixes #3072
2021-07-21 16:22:28 +01:00
Nick O'Leary
6750be3ec9 HTTP Request node - ignore invalid cookies rather than fail request
Fixes #3075

Keeps behaviour consistent with the request module
2021-07-21 14:09:15 +01:00
Nick O'Leary
68fb5089f8 Fix msg.reset handling in Delay node
Fixes #3074
2021-07-21 11:32:45 +01:00
Nick O'Leary
a8d093bacd Use file:// url with dynamic import 2021-07-21 11:00:24 +01:00
Nick O'Leary
233a1995b3 Fix tab fade css because Safari
Fixes #3073
2021-07-21 10:26:06 +01:00
Nick O'Leary
8ef3baaffb Fix error closing library dialog with monaco 2021-07-21 10:10:07 +01:00
Nick O'Leary
c9597b9447 Bump for 2.0.1 2021-07-20 14:44:47 +01:00
Nick O'Leary
b2dc1d8b23 Ensure default module export is exposed in Function node 2021-07-20 14:42:43 +01:00
Nick O'Leary
859c0c7f6c Update to latest node-red-admin 2021-07-20 11:19:13 +01:00
Nick O'Leary
aaf18e2416 Merge branch 'master' into dev 2021-07-20 11:11:28 +01:00
Nick O'Leary
fd679ef117 Update contributing docs 2021-07-20 11:10:41 +01:00
Nick O'Leary
6cc611b3f1 Merge branch 'master' into dev 2021-07-20 11:04:25 +01:00
Nick O'Leary
77ee726f66 Fix focus outline overlap in button-groups
Fixes #3070
2021-07-20 11:03:28 +01:00
Nick O'Leary
dc603b76a4 Fix focus outline overlap in button-groups
Fixes #3070
2021-07-20 11:02:24 +01:00
Nick O'Leary
bcb3371acc Fix another typo in issue template 2021-07-16 12:34:54 +01:00
Nick O'Leary
d14ce7e476 Fix typo in issue template 2021-07-16 12:30:21 +01:00
Dave Conway-Jones
4d26b806dd delay - add test for push toFront and flush=1 2021-07-16 11:38:06 +01:00
Dave Conway-Jones
a2b95dbb39 delay node - change lifo property to toFront
add info to sidebar
add tests
2021-07-16 11:31:21 +01:00
Nick O'Leary
47f7b43bcc Update bug_report.yml 2021-07-16 10:21:51 +01:00
Nick O'Leary
77fd8c120c Update bug_report.yml 2021-07-16 10:20:23 +01:00
Nick O'Leary
a1a6f40158 Update issue templates to use GH forms 2021-07-16 10:16:09 +01:00
Nick O'Leary
ed09cd7489 Merge pull request #3068 from hardillb/http-auth-fix
Fix for #3067
2021-07-16 09:47:57 +01:00
Nick O'Leary
eb3330d145 Fix vertical sizing of function node edit dialog 2021-07-16 09:44:56 +01:00
Nick O'Leary
5ba0588c7b Merge branch 'master' into dev 2021-07-16 08:53:35 +01:00
Nick O'Leary
d4a199f0e1 Avoid prototype pollution in RED.view.calculateTextDimensions 2021-07-16 08:52:00 +01:00
Ben Hardill
32dd186f4d Prevent Overwite of configured creds 2021-07-16 08:47:30 +01:00
Dave Conway-Jones
d820f55358 Add push to front of rate limit queue.
(moved random delay to top to group with other delay types.
Tests and docs to follow
2021-07-15 22:15:46 +01:00
Ben Hardill
81f0fb3c74 Fix creds in URL 2021-07-15 18:32:51 +01:00
Ben Hardill
972c83cd52 Fix for #3067
Check if there are any content to the credential object
2021-07-15 17:35:03 +01:00
Nick O'Leary
66a704af55 Fallback to 'require' if on node < 12.17 2021-07-15 15:38:07 +01:00
Nick O'Leary
31c5d6e1c1 Update changelog 2021-07-15 15:38:02 +01:00
Nick O'Leary
bf0ab95c09 Merge pull request #3066 from node-red-hitachi/update-message-jp
Update Japanese message catalogue
2021-07-15 13:17:41 +01:00
Hiroyasu Nishiyama
c1d85f760d update Japanese message catalogue 2021-07-15 21:09:20 +09:00
Nick O'Leary
88ad2f4c18 Merge pull request #3065 from node-red/funcExtMod-default
Enable functionExternalModules by default
2021-07-15 10:14:28 +01:00
Nick O'Leary
be9f9e7b0c Change default functionExternalModules test 2021-07-15 10:13:21 +01:00
Nick O'Leary
2cc1973f62 Enable functionExternalModules by default 2021-07-15 10:07:52 +01:00
Nick O'Leary
eb4625a0b9 Merge pull request #3064 from node-red/revert-external-modules-dir
Move externalModules back into the user dir
2021-07-15 09:56:23 +01:00
Nick O'Leary
5bfb01254b Add unit tests for externalModules.import 2021-07-15 09:52:53 +01:00
Nick O'Leary
bb80fa4a2d Fix externalModules tests 2021-07-14 23:38:46 +01:00
Nick O'Leary
ddb715d88d Record runtime-installed modules in .config.modules.json 2021-07-14 23:13:52 +01:00
Nick O'Leary
395b499856 Fix async loading of modules in function node 2021-07-14 21:10:59 +01:00
Nick O'Leary
cce6a47f11 Rework function module screen 2021-07-14 20:23:01 +01:00
Nick O'Leary
7fd17b4ec0 Add RED.import to support importing ES6 modules 2021-07-14 19:18:39 +01:00
Nick O'Leary
e16ab2a0fd Bump for 2.0.0 2021-07-13 11:51:23 +01:00
Nick O'Leary
15f5364c30 Update changelog 2021-07-13 11:50:38 +01:00
Nick O'Leary
65081767bf Fix externalModules tests 2021-07-13 11:44:12 +01:00
Nick O'Leary
c7c595e5fa Merge pull request #3063 from kazuhitoyokoi/dev-addjpn
Add Japanese translations for Node-RED v2.0
2021-07-13 11:37:23 +01:00
Nick O'Leary
5b24e8b69c Merge pull request #3059 from node-red/delay-queue
Delay node updates to added numeric flush control for releasing things from queue
2021-07-13 11:37:13 +01:00
Nick O'Leary
e6a845e606 Move externalModules back into the user dir 2021-07-13 11:24:10 +01:00
Kazuhito Yokoi
ec8b8a7b87 Change translation to be same as other usages 2021-07-13 14:15:37 +09:00
Kazuhito Yokoi
51a9205105 Add Japanese translations for Node-RED v2.0 2021-07-13 14:05:44 +09:00
Nick O'Leary
ed5567fc73 Merge pull request #3056 from node-red/ws-ping
Allow websocket client node to send pings
2021-07-12 19:40:57 +01:00
Nick O'Leary
4b3f5d74a0 Merge pull request #3058 from kazuhitoyokoi/master-fixdependencies
Add necessary modules and remove unnecessary module in dependencies
2021-07-12 19:40:40 +01:00
Nick O'Leary
b01c5a05e7 Fix reporting of type_already_registered error 2021-07-12 16:09:25 +01:00
Nick O'Leary
36eddabc1c Ensure node.types is defined if node html file missing 2021-07-12 16:09:02 +01:00
Dave Conway-Jones
ea11aa7a0d add some part flush tests 2021-07-08 13:51:37 +01:00
Nick O'Leary
e7efa76e6d Add a slight fade to tab labels that overflow 2021-07-08 12:23:15 +01:00
Dave Conway-Jones
41c8ca8ab4 Delay node updates to added flush control for queuing 2021-07-08 12:13:23 +01:00
Nick O'Leary
4624079be7 Merge pull request #3057 from kazuhitoyokoi/dev-fixdependencies
Add necessary modules and remove unnecessary module in dependencies
2021-07-08 12:07:07 +01:00
Nick O'Leary
c6f6042271 Show config node details when selected in outliner 2021-07-08 12:03:20 +01:00
Nick O'Leary
e9e3b9b7c6 Fix layout of info outliner for subflow entries 2021-07-08 12:02:11 +01:00
Kazuhito Yokoi
becbb09a29 Add necessary modules and remove unnecessary module in dependencies 2021-07-08 19:50:26 +09:00
Kazuhito Yokoi
6f6ab50995 Add necessary modules and remove unnecessary module in dependencies 2021-07-08 19:14:53 +09:00
Nick O'Leary
d8ee766860 Allow websocket client node to send pings 2021-07-08 10:51:36 +01:00
Nick O'Leary
108c26d8af Merge pull request #3055 from bonanitech/grip-horizontal
Fix grip on panels separator
2021-07-08 08:41:48 +01:00
Nick O'Leary
ed8d3088ca Merge pull request #3054 from bonanitech/scriptFiles
Fix scriptFiles
2021-07-08 08:41:18 +01:00
Nick O'Leary
46c4e2d212 Merge branch 'master' into dev 2021-07-08 08:40:44 +01:00
Nick O'Leary
94891d45f9 Merge pull request #3052 from kazuhitoyokoi/dev-fixdependencies
Add fs-extra module to dependencies in @node-red/util module
2021-07-08 08:39:51 +01:00
Nick O'Leary
7448ad109e Add missing dependency to @node-red/utils module 2021-07-08 08:39:20 +01:00
Mauricio Bonani
6211dfe024 Fix grip on horizontally displayed panels separator 2021-07-07 09:52:47 -04:00
Mauricio Bonani
9b85200954 Fix grip on panels separator 2021-07-07 09:24:14 -04:00
Mauricio Bonani
94ee739d91 Fix scriptFiles 2021-07-07 09:12:58 -04:00
Kazuhito Yokoi
e81a6db9a3 Add fs-extra module to dependencies in @node-red/util module 2021-07-05 13:34:56 +09:00
Nick O'Leary
b2f5a259ab Fix exec node test for gh-actions
The node adds `shell` to `execOpts` if `/bin/bash` exists. Clearly it
has not existed in Travis or running locally - but it does exist
on gh-action runner, so the test was failing.
2021-07-02 10:15:44 +01:00
Nick O'Leary
c8a0d3c10d Add error handling to exec test 2021-07-02 10:09:00 +01:00
Nick O'Leary
97df964051 Update changelog 2021-07-02 10:02:08 +01:00
Nick O'Leary
66dd05f8bc Merge branch 'master' into dev 2021-07-02 10:01:07 +01:00
Nick O'Leary
19589d9117 Merge branch 'master' into dev 2021-07-02 10:00:43 +01:00
Nick O'Leary
8147b2e0b1 Update changelog 2021-07-02 09:58:56 +01:00
Nick O'Leary
be22f8cd14 Update js-yaml 2021-07-02 09:39:19 +01:00
Nick O'Leary
868be9b7ff Update node-red-admin to 2.1 2021-07-02 09:37:14 +01:00
Nick O'Leary
5011281104 Merge branch 'dev' of github.com:node-red/node-red into dev 2021-07-02 09:22:56 +01:00
Nick O'Leary
42992c64ec Merge branch 'pr_3026' into dev 2021-07-02 09:22:35 +01:00
Nick O'Leary
2baff243ed Rename exec node hideWin option 2021-07-02 09:22:08 +01:00
Nick O'Leary
83440a6b0f Merge pull request #3047 from node-red/config-outliner-fix
Emit `nodes:change` for any updated config node when node deleted/added
2021-07-02 08:47:46 +01:00
Nick O'Leary
87c9a1c06c Include hasUser=false config nodes when exporting whole flow (#3048) 2021-07-01 21:40:57 +01:00
Nick O'Leary
b848fe249f Remove stray console.log from mqtt.html 2021-07-01 21:01:11 +01:00
Nick O'Leary
1e804d97ce Fix padding of compact notification
Closes #3045
2021-07-01 20:57:42 +01:00
Nick O'Leary
218d3c144b Emit nodes:change for any updated config node when node deleted/added 2021-07-01 20:52:13 +01:00
Nick O'Leary
05a4905490 Fix padding of compact notification
Closes #3045
2021-07-01 17:23:52 +01:00
Nick O'Leary
75103da378 Merge pull request #3044 from hardillb/hpagent-update
Bump hpagent to 0.1.2
2021-07-01 17:09:07 +01:00
Ben Hardill
9db9b53c81 Fix package.json
Also back out auth changes
2021-06-30 14:12:20 +01:00
Ben Hardill
0e4787f3e8 Bump hpagent to 0.1.2
Also remove workaround for empty proxy credentials
2021-06-30 13:42:56 +01:00
Nick O'Leary
f8d8d4b186 Update dependencies 2021-06-29 16:19:28 +01:00
Nick O'Leary
45e0a1ffea Merge pull request #3042 from node-red/bye-travis
Bye Travis. Hello GitHub Actions
2021-06-29 15:28:49 +01:00
Nick O'Leary
75c58093f1 Remove bye-travis branch trigger 2021-06-29 14:25:22 +01:00
Nick O'Leary
cc708e9fb4 Move to GH Actions in place of Travis 2021-06-29 14:19:24 +01:00
Nick O'Leary
2ce0e38827 Merge pull request #3040 from node-red/map-set-debug
Add support for Map/Set property types on Debug
2021-06-29 14:08:29 +01:00
Nick O'Leary
5b980e8c13 Merge pull request #3032 from node-red/view-annotations
Add RED.view.annotations api
2021-06-29 14:08:08 +01:00
Nick O'Leary
21b602650c Merge branch 'dev' of github.com:node-red/node-red into dev 2021-06-29 14:01:12 +01:00
Nick O'Leary
fa4b7a1a69 Ensure any html in changelog is escaped before displaying 2021-06-29 13:58:24 +01:00
Kazuhito Yokoi
977dfe700b Fix change log in help tab 2021-06-29 19:51:41 +09:00
Nick O'Leary
48ac50e1c9 Add support for Map/Set property types on Debug 2021-06-29 11:09:30 +01:00
Nick O'Leary
1a817947eb Merge pull request #3039 from kazuhitoyokoi/dev-fixfileinlabel
Fix output label in file-in node
2021-06-29 10:27:40 +01:00
Kazuhito Yokoi
be64603097 Fix indents from tab to space 2021-06-29 18:08:02 +09:00
Kazuhito Yokoi
f6b90c8271 Fix output label in file-in node 2021-06-29 18:02:06 +09:00
Kazuhito Yokoi
26e4be87c7 Add Japanese translation for file-in node (#3037) 2021-06-29 08:42:16 +01:00
Nick O'Leary
cddbb8d80d Merge pull request #3035 from node-red/file-node-msg-clone-option
Add option for file-in node to include all properties (default off)
2021-06-28 15:48:12 +01:00
Nick O'Leary
58023b4bf0 Merge branch 'pr_3024' into dev 2021-06-28 15:32:32 +01:00
Nick O'Leary
4f18a5f1c3 Merge pull request #3025 from node-red/comms-batch
Batch messages sent over Comms link
2021-06-28 15:28:40 +01:00
Nick O'Leary
56df8d8bd3 Merge pull request #3031 from Steve-Mcl/monaco0252
Update Monaco Editor to V0.25.2 (latest)
2021-06-28 15:28:09 +01:00
Nick O'Leary
211ec104c2 Merge pull request #3015 from Steve-Mcl/monaco-beta-bug-fix
Patches for Monaco editor
2021-06-28 15:27:27 +01:00
Nick O'Leary
3fb573247d Add 'theme' to default settings file 2021-06-25 18:10:13 +01:00
Dave Conway-Jones
6aac44db14 Add option for file-in node to include all properties (default off)
and add test
2021-06-25 14:39:18 +01:00
Nick O'Leary
3255e11cfc Limit annotations to badge type 2021-06-24 17:59:32 +01:00
Nick O'Leary
844bf29de1 Add RED.view.annotations api 2021-06-24 17:40:26 +01:00
Steve-Mcl
04d91d1422 prevent promise rejection when loading editor 2021-06-24 11:28:39 +01:00
Steve-Mcl
db90e1f801 remove unnecessary global uncaught promise handler
- monaco issue https://github.com/microsoft/monaco-editor/issues/2382
2021-06-24 11:27:24 +01:00
Steve-Mcl
7f30748a41 dont try to load en-gb or en-us (they're builtin)
- also monaco now comes with Turkish, Polish, Portuguese, Czech langs
2021-06-24 11:25:26 +01:00
Steve-Mcl
a4e0abb48f Update monaco editor to V0.5.2 2021-06-24 11:21:49 +01:00
Steve-Mcl
3f27dc89d8 Add overloads for context/flow/global set/get/keys 2021-06-23 22:27:13 +01:00
Nick O'Leary
d6f6efc189 Lower tray zIndex when overlay tray being opened
Fixes #3019
2021-06-23 14:34:52 +01:00
Nick O'Leary
2cda49fc38 Reduce z-Index of Function expand buttons to prevent overlap
Part of #3019
2021-06-23 14:16:36 +01:00
Nathanaël Lécaudé
04f4a76b41 exec node: clarify scope 2021-06-18 14:46:16 -04:00
Steve-Mcl
0a8f7085f3 allow static private field to have initializer 2021-06-17 08:44:44 +01:00
Nathanaël Lécaudé
7ae48d7390 exec: solidify tests 2021-06-16 17:38:34 -04:00
Steve-Mcl
c908502644 add 2 ace compatible functions
- clearSelection - blocky node
- selectAll - obvious addition
2021-06-16 22:36:00 +01:00
Nathanaël Lécaudé
2f0631809d exec node: modify test 2021-06-16 17:09:21 -04:00
Nathanaël Lécaudé
91ab3bd972 exec node: add windowsHide option to hide windows under Windows 2021-06-16 17:00:00 -04:00
Sam Machin
672636313c Update packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-06-16 17:13:52 +01:00
Nick O'Leary
79875ef50d Merge branch 'master' into dev 2021-06-16 17:03:50 +01:00
Nick O'Leary
aea5445495 Support loading external module sub path
Fixes #3023
2021-06-16 17:02:24 +01:00
Nick O'Leary
754a36fbc9 Merge branch 'master' into dev 2021-06-16 13:42:56 +01:00
Nick O'Leary
85dafc0b3c Ensure RED.clipboard.import displays the right library
Fixes #3021
2021-06-16 13:41:58 +01:00
Nick O'Leary
b516ab9b4f Batch messages sent over comms to prevent flooding 2021-06-16 11:45:02 +01:00
Nick O'Leary
1a27e60e55 Allow RED.popover.panel to specify a closeButton to ignore click events on 2021-06-16 11:44:40 +01:00
Nick O'Leary
2c710736e8 Use browser default language for initial page load 2021-06-16 11:44:35 +01:00
Nick O'Leary
69b9ff69be Add css var for node font color 2021-06-16 11:44:30 +01:00
Nick O'Leary
a3a4fc0cc2 Fix label padding of toggleButton 2021-06-16 11:44:25 +01:00
Nick O'Leary
ae686bb15d Give sidebar open tab a bit more room for its label 2021-06-16 11:44:20 +01:00
Nick O'Leary
68a5325849 Bump version to 2.0.0-beta.2 2021-06-16 11:44:14 +01:00
Nick O'Leary
75e3bddfa9 Remove the request module as a dependency 2021-06-16 11:44:09 +01:00
Sam Machin
bd3a8db438 log readOnly on startup 2021-06-16 10:10:31 +01:00
Nick O'Leary
102868bf74 Merge pull request #3022 from kazuhitoyokoi/update-message-files
Add additional Japanese translations for Node-RED 2.0 beta-1
2021-06-15 23:28:19 +01:00
Steve-Mcl
1a73a27102 add diagnosticCodesToIgnore 2345
- handles issue with Intl.DateTimeFormat when `options` is a variable
2021-06-15 23:15:54 +01:00
Steve-Mcl
a9cf34ab56 prevent IE script error
- even though monaco does not work in EI, scripts must not fail to load
2021-06-15 21:02:30 +01:00
Steve-Mcl
46d17c3314 update code comments 2021-06-15 21:01:33 +01:00
Nick O'Leary
40f816c311 Merge pull request #3020 from node-red-hitachi/update-message-files
Update message catalogue
2021-06-15 08:53:02 +01:00
Kazuhito Yokoi
13f1c12912 Add translations to message catalog 2021-06-14 15:54:05 +09:00
Hiroyasu Nishiyama
93c25f5d1b Update message catalogue 2021-06-13 14:19:36 +09:00
Steve-Mcl
aa6ec60c34 fix up issues with function node types
- node.status add overload
- remove examples as they dont render correctly
- correct Number and Object types to number and object
2021-06-11 18:44:37 +01:00
Nick O'Leary
ac159bb52e Merge pull request #3017 from hardillb/dev
Fix for basic auth with @ in username
2021-06-11 14:51:38 +01:00
Ben Hardill
919aee64f9 Add support for user/pass in URL 2021-06-11 14:48:41 +01:00
Ben Hardill
553bec1a1f Add testcase 2021-06-11 14:34:16 +01:00
Ben Hardill
bcb6d1cf93 Fix for basic auth with @ in username 2021-06-11 14:25:18 +01:00
Steve-Mcl
7d24e5b279 node.status snippet enums for fill and shape 2021-06-11 08:01:38 +01:00
Steve-Mcl
12253e23b5 Add setReadOnly support 2021-06-10 21:18:29 +01:00
310 changed files with 30655 additions and 13371 deletions

View File

@@ -1,35 +0,0 @@
<!--
## Before you hit that Submit button....
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
If your issue is:
- a general 'how-to' type question,
- a feature request or suggestion for a change,
- or problems with 3rd party (`node-red-contrib-`) nodes
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
## So you have a real issue to raise...
To help us understand the issue, please fill-in as much of the following information as you can:
-->
### What are the steps to reproduce?
### What happens?
### What do you expect to happen?
### Please tell us about your environment:
- [ ] Node-RED version:
- [ ] Node.js version:
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:
- [ ] running in Docker:

View File

@@ -1,39 +0,0 @@
---
name: Bug report
about: Reproducible software issues in the core of Node-RED
title: ''
labels: ''
assignees: ''
---
<!--
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
If your issue is:
- a general 'how-to' type question,
- a feature request or suggestion for a change,
- or problems with 3rd party (`node-red-contrib-`) nodes
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
To help us understand the issue, please fill-in as much of the following information as you can:
-->
### What are the steps to reproduce?
### What happens?
### What do you expect to happen?
### Please tell us about your environment:
- [ ] Node-RED version:
- [ ] Node.js version:
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:

View File

@@ -1,17 +0,0 @@
---
name: Anything Else
about: Something that is not a bug report
title: ''
labels: ''
assignees: ''
---
Please DO NOT raise an issue.
We DO NOT use the issue tracker for general support or feature requests. Only bug reports should be raised here using the 'Bug report' template.
For general support, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack). You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
For feature requests, please use the Node-RED Forum](https://discourse.nodered.org). Many ideas have already been discussed there and you should search that for your request before starting a new discussion.

61
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: 🐞 Report a bug
description: File a bug/issue on the core of Node-RED
labels: [needs-triage]
body:
- type: markdown
attributes:
value: |
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
If your issue is:
- a general 'how-to' type question,
- a feature request or suggestion for a change,
- or problems with 3rd party (`node-red-contrib-`) nodes
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
To help us understand the issue, please fill-in as much of the following information as you can:
- type: textarea
attributes:
label: Current Behavior
description: A clear & concise description of what you're experiencing.
validations:
required: false
- type: textarea
attributes:
label: Expected Behavior
description: A clear & concise description of what you expected to happen.
validations:
required: false
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
validations:
required: false
- type: textarea
attributes:
label: Example flow
description: If you have a minimal example flow that demonstrates the issue, share it here.
value: |
```
paste your flow here
```
validations:
required: false
- type: textarea
attributes:
label: Environment
description: Please tell us about your environment. Include any relevant information on how you are running Node-RED.
value: |
- Node-RED version:
- Node.js version:
- npm version:
- Platform/OS:
- Browser:
validations:
required: false

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: true
contact_links:
- name: ❓ Questions
url: https://discourse.nodered.org
about: Ask your question on the Node-RED forum
- name: ⭐️ Feature Request
url: https://discourse.nodered.org/c/development/feature-requests
about: Discuss your request with the community
- name: 🗂 Documentation
url: https://nodered.org/docs
about: Go straight to the documentation
- name: 💬 Slack
url: https://nodered.org/slack
about: Chat about the project on our slack team

View File

@@ -1,4 +1,4 @@
name: PublishDockerImage
name: Publish Release
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
on:

30
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Run tests
on:
push:
branches: [ master, dev ]
pull_request:
branches: [ master, dev ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12, 14, 16]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm install
- name: Run tests
run: |
npm run test
- name: Publish to coveralls.io
if: ${{ matrix.node-version == 14 }}
uses: coverallsapp/github-action@v1.1.2
with:
github-token: ${{ github.token }}

View File

@@ -1,22 +0,0 @@
sudo: false
addons:
chrome: stable
language: node_js
matrix:
include:
- node_js: "16"
script:
- ./node_modules/.bin/grunt no-coverage
- node_js: "14"
script:
- ./node_modules/.bin/grunt && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
# - scripts/install-ui-test-dependencies.sh && grunt test-ui
before_script:
- npm install -g coveralls
- node_js: "12"
script:
- ./node_modules/.bin/grunt no-coverage
allow_failures:
- node_js: "16"
script:
- ./node_modules/.bin/grunt no-coverage

View File

@@ -1,6 +1,257 @@
#### 2.0.0-beta.1: Beta Release
#### 2.2.0-beta.1: Beta Release
Migration from 1.x
#### 2.1.4: Maintenance Release
Runtime
- fix env var access using $parent for groups (#3278) @HiroyasuNishiyama
- Add proper error handling for 404 errors when serving debug files (#3277) @knolleary
- Add Japanese translations for Node-RED v2.1.0-beta.1 (#3179) @kazuhitoyokoi
- Include full user object on login audit events (#3269) @knolleary
- Remove styling from de locale files (#3237) @knolleary
Editor
- Change tab hide button icon to an eye and add search option (#3282) @knolleary
- Fix i18n handling of namespaces with spaces in (#3281) @knolleary
- Trigger change event when autoComplete fills in input (#3280) @knolleary
- Apply CN i18n fix (#3279) @knolleary
- fix select menu label of config node to use paletteLabel (#3273) @HiroyasuNishiyama
- fix removed tab not to cause node conflict (#3275) @HiroyasuNishiyama
- Group diff fix (#3239) @knolleary
- Only toggle disabled workspace flag if on activeWorkspace (#3252) @knolleary
- Do not show status for disabled nodes (#3253) @knolleary
- Set dimension value for tour guide (#3265) @kazuhitoyokoi
- Avoid redundant initialisation of TypedInput type (#3263) @knolleary
- Don't let themes change flow port label color (#3270) @bonanitech
- Fix treeList gutter calculation to handle floating gutters (#3238) @knolleary
Nodes
- Debug: Handle RegExp types in Debug sidebar (#3251) @knolleary
- Delay: fix 2nd output when in rate limit per topic modes (#3261) @dceejay
- Link: fix to show link target when selected (#3267) @HiroyasuNishiyama
- Inject: Do not modify inject node props in oneditprepare (#3242) @knolleary
- HTTP Request: HTTP Basic Auth should always add : between username and password even if empty (#3236) @hardillb
#### 2.1.3: Maintenance Release
Runtime
- Update gen-publish script to update 'next' tag for main releases
- Add environment variable to enable/disable tours (#3221) @hardillb
- Fix loading non-default language files leaving runtime in wrong locale (#3225) @knolleary
Editor
- Refresh editor settings whenever a node is added or enabled (#3227) @knolleary
- Revert spinner css change that made it shrink in some cases (#3229) @knolleary
- Fix import notification message when importing config nodes (#3224) @knolleary
- Handle changing types of TypedInput repeatedly (#3223) @knolleary
#### 2.1.2: Maintenance Release
Runtime
- node-red-pi: Remove bash dependency (#3216) @a16bitsysop
Editor
- Improved regex for markdown renderer (#3213) @GerwinvBeek
- Fix TypedInput initialisation (#3220) @knolleary
Nodes
- MQTT: fix datatype in node config not used. fixes #3215 (#3219) @Steve-Mcl
#### 2.1.1: Maintenance Release
Editor
- Ensure tourGuide popover doesn't fall offscreen (#3212) @knolleary
- Fix issue with old inject nodes that migrated topic to 'string' type (#3210) @knolleary
- Add cache-busting query params to index.mst (#3211) @knolleary
- Fix TypedInput validation of type without options (#3207) @knolleary
#### 2.1.0: Milestone Release
Editor
- Position popover properly on a scrolled page
- Fixes from 2.1.0-beta.2 (#3202) @knolleary
Nodes
- Link Out: Fix saving link out node links (#3201) @knolleary
- Switch: Refix #3170 - copy switch rule type when adding new rule
- TCP Request: Add string option to TCP request node output (#3204) @dceejay
#### 2.1.0-beta.2: Beta Release
Editor
- Fix switching projects (#3199) @knolleary
- Use locale setting when installing/enabling node (#3198) @knolleary
- Do not show projects-wecome dialog until welcome tour completes (#3197) @knolleary
- Fix converting selection to subflow (#3196) @knolleary
- Avoid conflicts with native browser cmd-ctrl type shortcuts (#3195) @knolleary
- Ensure message tools stay attached to top-level entry in Debug/Context (#3186) @knolleary
- Ensure tab state updates properly when toggling enable state (#3175) @knolleary
- Improve handling of long labels in TreeList (#3176) @knolleary
- Shift-click tab scroll arrows to jump to start/end (#3177) @knolleary
Runtime
- Update package dependencies
- Update to latest node-red-admin
Nodes
- Dynamic MQTT connections (#3189)
- Link: Filter out Link Out Return nodes in Link In edit dialog Fixes #3187
- Link: Fix link call label (#3200) @knolleary
- Debug: Redesign debug filter options and make them persistant (#3183) @knolleary
- Inject: Widen Inject interval box for >1 digit (#3184) @knolleary
- Switch: Fix rule focus when switch 'otherwise' rule is used (#3185) @knolleary
#### 2.1.0-beta.1: Beta Release
Editor
- Add Tour Guide component (#3136) @knolleary
- Allow tabs to be hidden (#3120) @knolleary
- Add align actions to editor (#3110) @knolleary
- Add support of environment variable for tab & group (#3112) @HiroyasuNishiyama
- Add autoComplete widget and add to TypedInput for msg. props (#3171) @knolleary
- Render node documentation to node-red style guide when written in markdown. (#3169) @Steve-Mcl
- Allow colouring of tab icon svg (#3140) @harmonic7
- Restore tab selection after merging conflicts (#3151) @GerwinvBeek
- Fix serving of theme files on Windows (#3154) @knolleary
- Ensure config-node select inherits width properly from input (#3155) @knolleary
- Do better remembering TypedInput values whilst switching types (#3159) @knolleary
- Update monaco to 0.28.1 (#3153) @knolleary
- Improve themeing of tourGuide (#3161) @knolleary
- Allow a node to specify a filter for the config nodes it can pick from (#3160) @knolleary
- Allow RED.notify.update to modify any notification setting (#3163) @knolleary
- Fix typo in ko editor.json Fixes #3119
- Improve RED.actions api to ensure actions cannot be overridden
- Ensure treeList row has suitable min-height when no content Fixes #3109
- Refactor edit dialogs to use separate edit panes
- Ensure type select button is not focussable when TypedInput only has one type
- Place close tab link in front of fade
Runtime
- Improve error reporting with oauth login strategies (#3148) @knolleary
- Add allowUpdate feature to externalModules.palette (#3143) @knolleary
- Improve node install error reporting (#3158) @knolleary
- Improve unit test coverage (#3168) @knolleary
- Allow coreNodesDir to be set to false (#3149) @hardillb
- Update package dependencies
- uncaughtException debug improvements (#3146) @renatojuniorrs
Nodes
- Change: Add option to deep-clone properties in Change node (#3156) @knolleary
- Delay: Add push to front of rate limit queue. (#3069) @dceejay
- File: Add paletteLabel to file nodes to make read/write more obvious (#3157) @knolleary
- HTTP Request: Extend HTTP request node to log detailed timing information (#3116) @k-toumura
- HTTP Response: Fix sizing of HTTP Response header fields (#3164) @knolleary
- Join: Support for msg.restartTimeout (#3121) @magma1447
- Link Call: Add Link Call node (#3152) @knolleary
- Switch: Copy previous rule type when adding rule to switch node (#3170) @knolleary
- Delay node: add option to send intermediate messages on separate output (#3166) @knolleary
- Typo in http request set method translation (#3173) @mailsvb
#### 2.0.6: Maintenance Release
Editor
- Fix typo in ko editor.json Fixes #3119
- Change fade color when hovering an inactive tab (#3106) @bonanitech
- Ensure treeList row has suitable min-height when no content Fixes #3109
Runtime
- Update tar to latest (#3128) @aksswami
- Give passport verify callback the same arity as the original callback (#3117) @dschmidt
- Handle HTTPS Key and certificate as string or buffer (#3115) @bartbutenaers
#### 2.0.5: Maintenance Release
Editor
- Remove default ctrl-enter keybinding from monaco editor Fixes #3093
Runtime
- Update tar dependency
- Add support for maintenance streams in generate-publish-script
Nodes
- Fix regression in Join node when manual joining array with msg.parts present Fixes #3096
#### 2.0.4: Maintenance Release
Editor
- Fix tab fade CSS for when using themes (#3085) @bonanitech
- Handle just-copied-but-not-deployed node with credentials in editor Fixes #3090
Nodes
- Filter: Fix RBE node handling of default topi property Fixes #3087
- HTTP Request: Handle partially encoded url query strings in request node
- HTTP Request: Fix support for supplied CA certs (#3089) @hardillb
- HTTP Request: Ensure TLS Cert is used (#3092) @hardillb
- Inject: Fix inject now button unable to send empty props
- Inject: Inject now button success notification should use label with updated props
#### 2.0.3: Maintenance Release
Nodes
- HTML: Fix HTML parsing when body is included in the select tag Fixes #3079
- HTTP Request: Preserve case of user-provided http headers in request node Fixes #3081
- HTTP Request: Set decompress to false for HTTP Request to keep 1.x compatibility Fixes #3083
- HTTP Request: Add unit tests for HTTP Request encodeURI and error response
- HTTP Request: Do not throw HTTP errors in request node Fixes #3082
- HTTP Request: Ensure uri is properly encoded before passing to got module Fixes #3080
#### 2.0.2: Maintenance Release
Runtime
- Use file:// url with dynamic import
- Detect if agent-base has patched https.request and undo it Fixes #3072
Editor
- Fix tab fade css because Safari Fixes #3073
- Fix error closing library dialog with monaco
- Handle other error types in Manage Palette view
Nodes
- HTTP Request node - ignore invalid cookies rather than fail request Fixes #3075
- Fix msg.reset handling in Delay node Fixes #3074
#### 2.0.1: Maintenance Release
Nodes
- Function: Ensure default module export is exposed in Function node
#### 2.0.0: Milestone Release
**Migration from 1.x**
- Node-RED now requires Node.js 12.x or later.
@@ -8,16 +259,90 @@ Migration from 1.x
they should be fully backward compatible.
- RBE: Relabelled as 'filter' to make it more discoverable and made part of
the core palette, rather than as a separate module.
the core palette, rather than as a separate module.
- Tail: This node has been removed from the default palette. You can reinstall it
from node-red-node-tail
from node-red-node-tail
- HTTP Request: Reimplemented with a different underlying module. We have
tried to maintain 100% functional compatibility, but it is possible
some edge cases remain. In particular, if you are use http proxies in
your environment
tried to maintain 100% functional compatibility, but it is possible
some edge cases remain.
- JSON: The schema validation option no longer supports JSON-Schema draft-04
- HTML: Its underlying module has had a major version update. Should be fully
backward compatible.
backward compatible.
- `functionExternalModules` is now enabled by default for new installs.
If you have an existing settings file that contains this setting, you will
need to set it to `true` yourself.
The external modules will now get installed in your Node-RED user directory,
(`~/.node-red`) rather than in a subdirectory. This means all dependencies will
be listed in your top-level `package.json`. If you have existing external modules,
they will get reinstalled to the new location when you first run Node-RED 2.0.
Runtime
- Fix missing dependencies (#3052, #2057) @kazuhitoyokoi
- Ensure node.types is defined if node html file missing
- Fix reporting of type_already_registered error
- Move install location of external modules (#3064) @knolleary
Editor
- Update translations (#3063) @kazuhitoyokoi
- Add a slight fade to tab labels that overflow
- Show config node details when selected in outliner
- Fix layout of info outliner for subflow entries
Nodes
- Delay: let `msg.flush` specify how many messages to flush from node (#3059) @dceejay
- Function: external modules is now enabled by default (#3065) @knolleary
- Function: external modules now supports both ES6 and CJS modules (#3065) @knolleary
- WebSocket: add option for client node to send automatic pings (#3056) @knolleary
##### 2.0.0-beta.2: Beta Release
Runtime
- Add `node-red admin init` (via `node-red-admin@2.1.0`)
- Move to GH Actions rather than Travis for build (#3042) @knolleary
Editor
- Include hasUser=false config nodes when exporting whole flow (#3048)
- Emit nodes:change for any updated config node when node deleted/added
- Fix padding of compact notification Closes #3045
- Ensure any html in changelog is escaped before displaying
- Add support for Map/Set property types on Debug (#3040) @knolleary
- Add 'theme' to default settings file
- Add RED.view.annotations api (#3032) @knolleary
- Update monaco editor to V0.25.2 (#3031) @Steve-Mcl
- Lower tray zIndex when overlay tray being opened Fixes #3019
- Reduce z-Index of Function expand buttons to prevent overlap Part of #3019
- Ensure RED.clipboard.import displays the right library Fixes #3021
- Batch messages sent over comms to prevent flooding (#3025) @knolleary
- Allow RED.popover.panel to specify a closeButton to ignore click events on
- Use browser default language for initial page load
- Add css var for node font color
- Fix label padding of toggleButton
- Give sidebar open tab a bit more room for its label
- Various Monaco updates (#3015) @Steve-Mcl
- Log readOnly on startup (#3024) @sammachin
- Translation updates (#3020 #3022) @HiroyasuNishiyama @kazuhitoyokoi
Nodes
- HTTP Request: Fix proxy handling (#3044) @hardillb
- HTTP Request: Handle basic auth with @ in username (#3017) @hardillb
- Add Japanese translation for file-in node (#3037 #3039) @kazuhitoyokoi
- File In: Add option for file-in node to include all properties (default off) (#3035) @dceejay
- Exec: add windowsHide option to hide windows under Windows (#3026) @natcl
- Support loading external module sub path Fixes #3023
##### 2.0.0-beta.1: Beta Release
Runtime

View File

@@ -46,6 +46,14 @@ If you raise a pull-request without having signed the CLA, you will be prompted
to do so automatically.
### Code Branches
When raising a PR for a fix or a new feature, it is important to target the right branch.
- `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch.
- `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream.
- `dev` - this is the branch for new feature development targeting the next milestone release.
### Coding standards
Please ensure you follow the coding standards used through-out the existing

View File

@@ -162,7 +162,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
@@ -170,6 +170,7 @@ module.exports = function(grunt) {
"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",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
@@ -181,6 +182,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
@@ -198,7 +200,8 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js"
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
],
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
},
@@ -325,6 +328,12 @@ module.exports = function(grunt) {
],
tasks: ['jsonlint:keymaps','copy:build']
},
tours: {
files: [
'packages/node_modules/@node-red/editor-client/src/tours/**/*.js'
],
tasks: ['copy:build']
},
misc: {
files: [
'CHANGELOG.md'
@@ -422,6 +431,12 @@ module.exports = function(grunt) {
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/tours',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/'
}
]
}
@@ -568,7 +583,7 @@ module.exports = function(grunt) {
grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src;
var copyright = "/**\n"+
" * Copyright JS Foundation and other contributors, http://js.foundation\n"+
" * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+
" *\n"+
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+
" * you may not use this file except in compliance with the License.\n"+

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "2.0.0-beta.1",
"version": "2.2.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,10 +26,10 @@
}
],
"dependencies": {
"acorn": "8.3.0",
"acorn-walk": "8.1.0",
"ajv": "8.6.0",
"async-mutex": "0.3.1",
"acorn": "8.6.0",
"acorn-walk": "8.2.0",
"ajv": "8.8.2",
"async-mutex": "0.3.2",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
@@ -37,55 +37,54 @@
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.4.1",
"cookie-parser": "1.4.5",
"cookie-parser": "1.4.6",
"cors": "2.8.5",
"cronosjs": "1.7.1",
"denque": "1.5.0",
"denque": "2.0.1",
"express": "4.17.1",
"express-session": "1.17.2",
"form-data": "4.0.0",
"fs-extra": "10.0.0",
"fs.notify": "0.0.4",
"got": "11.8.2",
"got": "11.8.3",
"hash-sum": "2.0.0",
"hpagent": "0.1.1",
"hpagent": "0.1.2",
"https-proxy-agent": "5.0.0",
"i18next": "20.3.1",
"i18next": "21.5.4",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "3.14.0",
"js-yaml": "3.14.1",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.4",
"jsonata": "1.8.5",
"lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0",
"memorystore": "1.6.6",
"mime": "2.5.2",
"moment-timezone": "0.5.33",
"mqtt": "4.2.6",
"multer": "1.4.2",
"moment-timezone": "0.5.34",
"mqtt": "4.2.8",
"multer": "1.4.3",
"mustache": "4.2.0",
"node-red-admin": "^2.0.0",
"node-red-admin": "^2.2.1",
"nopt": "5.0.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"on-headers": "1.0.2",
"passport": "0.4.1",
"passport": "0.5.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"raw-body": "2.4.1",
"request": "2.88.0",
"raw-body": "2.4.2",
"semver": "7.3.5",
"tar": "6.1.0",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.13.9",
"uglify-js": "3.14.4",
"uuid": "8.3.2",
"ws": "7.4.6",
"ws": "7.5.1",
"xml2js": "0.4.23"
},
"optionalDependencies": {
"bcrypt": "5.0.1"
},
"devDependencies": {
"dompurify": "2.2.9",
"dompurify": "2.3.3",
"grunt": "1.4.1",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
@@ -94,7 +93,7 @@
"grunt-contrib-compress": "2.0.0",
"grunt-contrib-concat": "~1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-jshint": "3.0.0",
"grunt-contrib-jshint": "3.1.1",
"grunt-contrib-uglify": "5.0.1",
"grunt-contrib-watch": "~1.1.0",
"grunt-jsdoc": "2.4.1",
@@ -105,20 +104,20 @@
"grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1",
"i18next-http-backend": "1.2.6",
"i18next-http-backend": "1.3.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "2.0.7",
"marked": "3.0.7",
"minami": "1.2.3",
"mocha": "8.4.0",
"mocha": "9.1.3",
"node-red-node-test-helper": "^0.2.7",
"nodemon": "2.0.7",
"nodemon": "2.0.15",
"proxy": "^1.0.2",
"sass": "1.34.1",
"sass": "1.44.0",
"should": "13.2.3",
"sinon": "11.1.1",
"sinon": "11.1.2",
"stoppable": "^1.1.0",
"supertest": "6.1.3"
"supertest": "6.1.6"
},
"engines": {
"node": ">=12"

View File

@@ -141,7 +141,7 @@ function completeVerify(profile,done) {
Users.authenticate(profile).then(function(user) {
if (user) {
Tokens.create(user.username,"node-red-editor",user.permissions).then(function(tokens) {
log.audit({event: "auth.login",username:user.username,scope:user.permissions});
log.audit({event: "auth.login",user,username:user.username,scope:user.permissions});
user.tokens = tokens;
done(null,user);
});
@@ -173,31 +173,38 @@ function genericStrategy(adminApp,strategy) {
adminApp.use(passport.session());
var options = strategy.options;
var verify = function() {
var originalDone = arguments[arguments.length-1];
if (options.verify) {
var args = Array.from(arguments);
args[args.length-1] = function(err,profile) {
if (err) {
return originalDone(err);
} else {
return completeVerify(profile,originalDone);
}
};
passport.use(new strategy.strategy(options,
function() {
var originalDone = arguments[arguments.length-1];
if (options.verify) {
var args = Array.from(arguments);
args[args.length-1] = function(err,profile) {
if (err) {
return originalDone(err);
} else {
return completeVerify(profile,originalDone);
}
};
options.verify.apply(null,args);
} else {
var profile = arguments[arguments.length - 2];
return completeVerify(profile,originalDone);
}
options.verify.apply(null,args);
} else {
var profile = arguments[arguments.length - 2];
return completeVerify(profile,originalDone);
}
));
};
// Give our callback the same arity as the original one from options
if (options.verify) {
Object.defineProperty(verify, "length", { value: options.verify.length })
}
passport.use(new strategy.strategy(options, verify));
adminApp.get('/auth/strategy',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
passport.authenticate(strategy.name, {session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot
}),
completeGenerateStrategyAuth,
handleStrategyError
);
var callbackMethodFunc = adminApp.get;
@@ -205,8 +212,13 @@ function genericStrategy(adminApp,strategy) {
callbackMethodFunc = adminApp.post;
}
callbackMethodFunc.call(adminApp,'/auth/strategy/callback',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
passport.authenticate(strategy.name, {
session:false,
failureMessage: true,
failureRedirect: settings.httpAdminRoot
}),
completeGenerateStrategyAuth,
handleStrategyError
);
}
@@ -216,6 +228,13 @@ function completeGenerateStrategyAuth(req,res) {
// Successful authentication, redirect home.
res.redirect(settings.httpAdminRoot + '?access_token='+tokens.accessToken);
}
function handleStrategyError(err, req, res, next) {
if (res.headersSent) {
return next(err)
}
log.audit({event: "auth.login.fail.oauth",error:err.toString()});
res.redirect(settings.httpAdminRoot + '?session_message='+err.toString());
}
module.exports = {
init: init,

View File

@@ -93,7 +93,7 @@ var passwordTokenExchange = function(client, username, password, scope, done) {
return logEntry.user !== username;
});
Tokens.create(username,client.id,scope).then(function(tokens) {
log.audit({event: "auth.login",username:username,client:client.id,scope:scope});
log.audit({event: "auth.login",user,username:username,client:client.id,scope:scope});
done(null,tokens.accessToken,null,{expires_in:tokens.expires_in});
});
} else {
@@ -146,7 +146,7 @@ function authenticateUserToken(req) {
} else {
reject();
}
});
}).catch(reject);
} else {
reject();
}
@@ -163,6 +163,9 @@ TokensStrategy.prototype.authenticate = function(req) {
authenticateUserToken(req).then(user => {
this.success(user,{scope:user.permissions});
}).catch(err => {
if (err) {
log.trace("token authentication failure: "+err.stack?err.stack:err)
}
this.fail(401);
});
}

View File

@@ -158,25 +158,31 @@ function CommsConnection(ws, user) {
}
CommsConnection.prototype.send = function(topic,data) {
var self = this;
if (topic && data) {
this.stack.push({topic:topic,data:data});
}
this._queueSend();
}
CommsConnection.prototype._queueSend = function() {
var self = this;
if (!this._xmitTimer) {
this._xmitTimer = setTimeout(function() {
try {
self.ws.send(JSON.stringify(self.stack));
self.ws.send(JSON.stringify(self.stack.splice(0,50)));
self.lastSentTime = Date.now();
} catch(err) {
removeActiveConnection(self);
log.warn(log._("comms.error-send",{message:err.toString()}));
}
delete self._xmitTimer;
self.stack = [];
if (self.stack.length > 0) {
self._queueSend();
}
},50);
}
}
CommsConnection.prototype.subscribe = function(topic) {
runtimeAPI.comms.subscribe({
user: this.user,

View File

@@ -48,9 +48,10 @@ module.exports = {
var prevLang = i18n.i.language;
// Trigger a load from disk of the language if it is not the default
i18n.i.changeLanguage(lang, function(){
var catalog = loadResource(lang, namespace);
res.json(catalog||{});
i18n.i.changeLanguage(prevLang, function() {
var catalog = loadResource(lang, namespace);
res.json(catalog||{});
});
});
i18n.i.changeLanguage(prevLang);
}
}

View File

@@ -18,14 +18,6 @@ var apiUtils = require("../util");
var express = require("express");
var runtimeAPI;
function getUsername(userObj) {
var username = '__default';
if ( userObj && userObj.name ) {
username = userObj.name;
}
return username;
}
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;

View File

@@ -24,15 +24,19 @@ var defaultContext = {
page: {
title: "Node-RED",
favicon: "favicon.ico",
tabicon: "red/images/node-red-icon-black.svg"
tabicon: {
icon: "red/images/node-red-icon-black.svg",
colour: "#8f0000"
},
version: require(path.join(__dirname,"../../package.json")).version
},
header: {
title: "Node-RED",
image: "red/images/node-red.svg"
},
asset: {
red: (process.env.NODE_ENV == "development")? "red/red.js":"red/red.min.js",
main: (process.env.NODE_ENV == "development")? "red/main.js":"red/main.min.js",
red: "red/red.min.js",
main: "red/main.min.js",
vendorMonaco: ""
}
};
@@ -74,7 +78,7 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
let fullPath = array[i];
if (baseDirectory) {
fullPath = path.resolve(baseDirectory,array[i]);
if (fullPath.indexOf(baseDirectory) !== 0) {
if (fullPath.indexOf(path.resolve(baseDirectory)) !== 0) {
continue;
}
}
@@ -91,6 +95,10 @@ module.exports = {
init: function(settings, _runtimeAPI) {
runtimeAPI = _runtimeAPI;
themeContext = clone(defaultContext);
if (process.env.NODE_ENV == "development") {
themeContext.asset.red = "red/red.js";
themeContext.asset.main = "red/main.js";
}
themeSettings = null;
theme = settings.editorTheme || {};
themeContext.asset.vendorMonaco = ((theme.codeEditor || {}).lib === "monaco") ? "vendor/monaco/monaco-bootstrap.js" : "";
@@ -123,9 +131,13 @@ module.exports = {
}
if (theme.page.tabicon) {
url = serveFile(themeApp,"/tabicon/",theme.page.tabicon)
let icon = theme.page.tabicon.icon || theme.page.tabicon
url = serveFile(themeApp,"/tabicon/", icon)
if (url) {
themeContext.page.tabicon = url;
themeContext.page.tabicon.icon = url;
}
if (theme.page.tabicon.colour) {
themeContext.page.tabicon.colour = theme.page.tabicon.colour
}
}
@@ -216,6 +228,11 @@ module.exports = {
if (theme.theme) {
themeSettings.theme = theme.theme;
}
if (theme.hasOwnProperty("tours")) {
themeSettings.tours = theme.tours;
}
return themeApp;
},
context: async function() {
@@ -244,7 +261,10 @@ module.exports = {
)
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
theme.page = theme.page || {_:{}}
theme.page._.scripts = cssFiles.concat(theme.page._.scripts || [])
theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || [])
}
if(theme.codeEditor) {
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
}
}
activeThemeInitialised = true;

View File

@@ -91,7 +91,16 @@ module.exports = {
},
editor: async function(req,res) {
res.send(Mustache.render(editorTemplate,await theme.context()));
let sessionMessages;
if (req.session && req.session.messages) {
sessionMessages = JSON.stringify(req.session.messages);
delete req.session.messages
}
res.send(Mustache.render(editorTemplate,{
sessionMessages,
...await theme.context()
}));
},
editorResources: express.static(path.join(editorClientDir,'public'))
};

View File

@@ -90,6 +90,8 @@ function init(settings,_server,storage,runtimeAPI) {
auth.getToken,
auth.errorHandler
);
} else if (settings.adminAuth.tokens) {
adminApp.use(passport.initialize());
}
adminApp.post("/auth/revoke",auth.needsPermission(""),auth.revoke,apiUtil.errorHandler);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "2.0.0-beta.1",
"version": "2.2.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "2.0.0-beta.1",
"@node-red/editor-client": "2.0.0-beta.1",
"@node-red/util": "2.2.0-beta.1",
"@node-red/editor-client": "2.2.0-beta.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"clone": "2.1.2",
@@ -26,13 +26,13 @@
"express": "4.17.1",
"memorystore": "1.6.6",
"mime": "2.5.2",
"multer": "1.4.2",
"multer": "1.4.3",
"mustache": "4.2.0",
"oauth2orize": "1.11.0",
"oauth2orize": "1.11.1",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.4.1",
"ws": "7.4.6"
"passport": "0.5.0",
"ws": "7.5.1"
},
"optionalDependencies": {
"bcrypt": "5.0.1"

View File

@@ -53,8 +53,17 @@
"confirmDelete": "Confirm delete",
"delete": "Are you sure you want to delete '__label__'?",
"dropFlowHere": "Drop the flow here",
"addFlow": "Add Flow",
"listFlows": "List Flows",
"addFlow": "Add flow",
"addFlowToRight": "Add flow to the right",
"hideFlow": "Hide flow",
"hideOtherFlows": "Hide other flows",
"showAllFlows": "Show all flows",
"hideAllFlows": "Hide all flows",
"hiddenFlows": "List __count__ hidden flow",
"hiddenFlows_plural": "List __count__ hidden flows",
"showLastHiddenFlow": "Show last hidden flow",
"listFlows": "List flows",
"listSubflows": "List subflows",
"status": "Status",
"enabled": "Enabled",
"disabled":"Disabled",
@@ -83,6 +92,7 @@
"palette": {
"show": "Show palette"
},
"edit": "Edit",
"settings": "Settings",
"userSettings": "User Settings",
"nodes": "Nodes",
@@ -105,6 +115,7 @@
"editPalette":"Manage palette",
"other": "Other",
"showTips": "Show tips",
"showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website",
"projects": "Projects",
"projects-new": "New",
@@ -116,7 +127,20 @@
"groupSelection": "Group selection",
"ungroupSelection": "Ungroup selection",
"groupMergeSelection": "Merge selection",
"groupRemoveSelection": "Remove from group"
"groupRemoveSelection": "Remove from group",
"arrange":"Arrange",
"alignLeft":"Align to left",
"alignCenter":"Align to center",
"alignRight":"Align to right",
"alignTop":"Align to top",
"alignMiddle":"Align to middle",
"alignBottom":"Align to bottom",
"distributeHorizontally":"Distribute horizontally",
"distributeVertically":"Distribute vertically",
"moveToBack":"Move to back",
"moveToFront":"Move to front",
"moveBackwards":"Move backwards",
"moveForwards":"Move forwards"
}
},
"actions": {
@@ -450,8 +474,9 @@
"unassigned": "Unassigned",
"global": "global",
"workspace": "workspace",
"selectAll": "Select all nodes",
"selectAllConnected": "Select all connected nodes",
"selectAll": "Select all",
"selectNone": "Select none",
"selectAllConnected": "Select connected",
"addRemoveNode": "Add/remove node from selection",
"editSelected": "Edit selected node",
"deleteSelected": "Delete selected nodes or link",
@@ -464,7 +489,10 @@
"copyNode": "Copy selected nodes",
"cutNode": "Cut selected nodes",
"pasteNode": "Paste nodes",
"undoChange": "Undo the last change performed",
"copyGroupStyle": "Copy group style",
"pasteGroupStyle": "Paste group style",
"undoChange": "Undo",
"redoChange": "Redo",
"searchBox": "Open search box",
"managePalette": "Manage palette",
"actionList":"Action list"
@@ -519,7 +547,8 @@
"nodeEnabled_plural": "Nodes enabled:",
"nodeDisabled": "Node disabled:",
"nodeDisabled_plural": "Nodes disabled:",
"nodeUpgraded": "Node module __module__ upgraded to version __version__"
"nodeUpgraded": "Node module __module__ upgraded to version __version__",
"unknownNodeRegistered": "Error loading node: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "Manage palette",
@@ -642,7 +671,8 @@
"unusedConfigNodes": "Unused configuration nodes",
"invalidNodes": "Invalid nodes",
"uknownNodes": "Unknown nodes",
"unusedSubflows": "Unused subflows"
"unusedSubflows": "Unused subflows",
"hiddenFlows": "Hidden flows"
}
},
"help": {
@@ -864,6 +894,8 @@
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "No matches found",
"addNode": "add a node..."
},
@@ -1108,6 +1140,11 @@
"preview": "UI Preview",
"defaultValue": "Default value"
},
"tourGuide": {
"takeATour": "Take a tour",
"start": "Start",
"next": "Next"
},
"languages" : {
"de": "German",
"en-US": "English",

View File

@@ -200,8 +200,8 @@
"desc": "Returns a copy of the `string` with extra padding, if necessary, so that its total number of characters is at least the absolute value of the `width` parameter.\n\nIf `width` is a positive number, then the string is padded to the right; if negative, it is padded to the left.\n\nThe optional `char` argument specifies the padding character(s) to use. If not specified, it defaults to the space character."
},
"$fromMillis": {
"args": "number",
"desc": "Convert a number representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a timestamp string in the ISO 8601 format."
"args": "number, [, picture [, timezone]]",
"desc": "Convert the `number` representing milliseconds since the Unix Epoch (1 January, 1970 UTC) to a formatted string representation of the timestamp as specified by the picture string.\n\nIf the optional `picture` parameter is omitted, then the timestamp is formatted in the ISO 8601 format.\n\nIf the optional `picture` string is supplied, then the timestamp is formatted occording to the representation specified in that string. The behaviour of this function is consistent with the two-argument version of the XPath/XQuery function `format-dateTime` as defined in the XPath F&O 3.1 specification. The picture string parameter defines how the timestamp is formatted and has the same syntax as `format-dateTime`.\n\nIf the optional `timezone` string is supplied, then the formatted timestamp will be in that timezone. The `timezone` string should be in the format '±HHMM', where ± is either the plus or minus sign and HHMM is the offset in hours and minutes from UTC. Positive offset for timezones east of UTC, negative offset for timezones west of UTC."
},
"$formatNumber": {
"args": "number, picture [, options]",

View File

@@ -54,7 +54,16 @@
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
"addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加",
"hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示",
"hideAllFlows": "全てのフローを非表示",
"hiddenFlows": "__count__ 個の非表示のフロー一覧",
"hiddenFlows_plural": "__count__ 個の非表示のフロー一覧",
"showLastHiddenFlow": "最後に非表示にしたフローを表示",
"listFlows": "フロー一覧",
"listSubflows": "サブフロー一覧",
"status": "状態",
"enabled": "有効",
"disabled": "無効",
@@ -83,6 +92,7 @@
"palette": {
"show": "パレットを表示"
},
"edit": "編集",
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
@@ -105,17 +115,32 @@
"editPalette": "パレットの管理",
"other": "その他",
"showTips": "ヒントを表示",
"showWelcomeTours": "新バージョンのガイドツアーを表示",
"help": "Node-REDウェブサイト",
"projects": "プロジェクト",
"projects-new": "新規",
"projects-open": "開く",
"projects-settings": "設定",
"showNodeLabelDefault": "追加したノードのラベルを表示",
"codeEditor": "コードエディタ",
"groups": "グループ",
"groupSelection": "選択部分をグループ化",
"ungroupSelection": "選択部分をグループ解除",
"groupMergeSelection": "選択部分をマージ",
"groupRemoveSelection": "グループから削除"
"groupRemoveSelection": "グループから削除",
"arrange": "配置",
"alignLeft": "左揃え",
"alignCenter": "左右中央揃え",
"alignRight": "右揃え",
"alignTop": "上揃え",
"alignMiddle": "上下中央揃え",
"alignBottom": "下揃え",
"distributeHorizontally": "左右に整列",
"distributeVertically": "上下に整列",
"moveToBack": "最背面へ移動",
"moveToFront": "最前面へ移動",
"moveBackwards": "背面へ移動",
"moveForwards": "前面へ移動"
}
},
"actions": {
@@ -450,7 +475,8 @@
"global": "グローバル",
"workspace": "ワークスペース",
"selectAll": "全てのノードを選択",
"selectAllConnected": "接続された全てのノードを選択",
"selectNone": "選択を外す",
"selectAllConnected": "接続されたノードを選択",
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
@@ -460,10 +486,13 @@
"moveNode": "選択したノードを移動(移動量大)",
"toggleSidebar": "サイドバーの表示/非表示",
"togglePalette": "パレットの表示/非表示",
"copyNode": "選択したノードをコピー",
"cutNode": "選択したノードを切り取り",
"copyNode": "ノードをコピー",
"cutNode": "ノードを切り取り",
"pasteNode": "ノードを貼り付け",
"copyGroupStyle": "グループ様式をコピー",
"pasteGroupStyle": "グループ様式を貼り付け",
"undoChange": "変更操作を戻す",
"redoChange": "変更操作をやり直し",
"searchBox": "ノードを検索",
"managePalette": "パレットの管理",
"actionList": "動作一覧"
@@ -518,7 +547,8 @@
"nodeEnabled_plural": "ノードを有効化しました:",
"nodeDisabled": "ノードを無効化しました:",
"nodeDisabled_plural": "ノードを無効化しました:",
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました"
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました",
"unknownNodeRegistered": "ノードの読み込みエラー: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "パレットの管理",
@@ -641,7 +671,8 @@
"unusedConfigNodes": "未使用の設定ノード",
"invalidNodes": "不正なノード",
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー"
"unusedSubflows": "未使用のサブフロー",
"hiddenFlows": "非表示のフロー"
}
},
"help": {
@@ -885,6 +916,9 @@
"eval": "表現評価エラー:\n __message__"
}
},
"monaco": {
"setTheme": "テーマを設定:"
},
"jsEditor": {
"title": "JavaScriptエディタ"
},
@@ -1104,6 +1138,11 @@
"preview": "UIプレビュー",
"defaultValue": "デフォルト値"
},
"tourGuide": {
"takeATour": "ツアーを開始",
"start": "開始",
"next": "次へ"
},
"languages": {
"de": "ドイツ語",
"en-US": "英語",

View File

@@ -52,8 +52,8 @@
"desc": "文字列 `str` からパターン `pattern` を検索し、置換文字列 `replacement` に置き換えます。\n\n任意の引数 `limit` には、置換回数の上限値を指定します。"
},
"$now": {
"args": "",
"desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。"
"args": "$[picture [, timezone]]",
"desc": "ISO 8601互換形式の時刻を生成し、文字列として返します。pictureおよびtimezoneパラメータが指定されている場合、現在時刻を`$fromMillis()`関数の説明に従ってフォーマットします。"
},
"$base64encode": {
"args": "string",
@@ -200,8 +200,8 @@
"desc": "文字数が引数 `width` の絶対値以上となるよう、必要に応じて追加文字を付け足した `string` のコピーを返します。\n\n`width` が正の値の場合、文字列の右側に追加文字を付け足します。もし負の値の場合、文字列の左側に追加文字を付け足します。\n\n任意の引数 `char` には、本関数で用いる追加文字を指定します。もし追加文字を指定しない場合は、既定値として空白文字を使用します。"
},
"$fromMillis": {
"args": "number",
"desc": "Unixエポック(1 January, 1970 UTC)からの経過ミリ秒を表す数値を、ISO 8601形式のタイムスタンプの文字列に変換します。"
"args": "number, [, picture [, timezone]]",
"desc": "Unixエポック(1 January, 1970 UTC)からの経過ミリ秒を表す数値を、`picture`の指定に従ってタイムスタンプの文字列に変換します。\n\n`picture`パラメータが指定されない場合、ISO 8601形式に変換します。\n\n`picture`を指定すると、指定した文字列に従って変換を行います。この変換はXPath F&O 3.1仕様におけるXPath/XQueryの2引数形式`format-dateTime`と同様です。`picture`パラメータはタイムスタンプの変換形式を定義し、その書式は`format-dateTime`と同じです。\n\n`timezone`を指定すると、指定タイムゾーンで変換します。`timezone`は'±HHMM'という形式で指定します。ここで、±は+もしくは-記号を表し、HHMMはUTCからの差分時間と分を表します。正の差分はUTCの東、負の差分は西のタイムゾーンとなります。"
},
"$formatNumber": {
"args": "number, picture [, options]",

View File

@@ -56,7 +56,7 @@
"displayConfig": "설정노드 보기",
"import": "가져오기",
"export": "내보내기",
"search": "플로우 색",
"search": "플로우 색",
"searchInput": "플로우 검색",
"subflows": "보조 플로우",
"createSubflow": "보조 플로우 생성",

View File

@@ -225,7 +225,7 @@
"compact": "紧凑",
"formatted": "已格式化",
"copy": "导出到剪贴板",
"export": "到处到库",
"export": "导出到库",
"exportAs": "导出为",
"overwrite": "替换",
"exists": "<p><b>\"__file__\"</b>已存在</p><p>是否要替换它?</p>"

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "2.0.0-beta.1",
"version": "2.2.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -14,5 +14,5 @@
"name": "Dave Conway-Jones"
}
],
"main": "./lib/index.js"
"main": "./index.js"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" height="512" width="512"><g transform="translate(0 -540.36)"><path fill="#8f0000" color="#000" d="M0 540.36h512v392H0z"/><rect ry="0" height="108.23" width="500.23" stroke="#fff" y="938.25" x="5.89" stroke-width="11.77" fill="#fff"/><path style="text-decoration-color:#000;isolation:auto;mix-blend-mode:normal;solid-color:#000;block-progression:tb;text-decoration-line:none;white-space:normal;text-indent:0;text-transform:none;text-decoration-style:solid" d="M122.88 305.82a4.46 4.46 0 0 0-4.38 4.42v.78c-2.23.1-4.04.54-5.33 1.43a10.5 10.5 0 0 0-3.18 3.87c-.71 1.3-1.3 2.41-2.15 3.2-.72.66-1.8 1.12-3.45 1.32a4.37 4.37 0 0 0-4.3-3.95H82.91a4.43 4.43 0 0 0-4.42 4.35v4.24a4.43 4.43 0 0 0 4.42 4.36h17.16a4.4 4.4 0 0 0 4.4-4.36v-1.6c9.72.14 12.46 2.6 15.59 5.33 3 2.62 6.66 5.38 15.43 5.5v.73a4.49 4.49 0 0 0 4.46 4.38h17.09c2.38 0 4.45-2 4.45-4.38v-4.24a4.49 4.49 0 0 0-4.45-4.38h-17.1c-2.38 0-4.45 2-4.45 4.38v.58c-8.1-.06-10.48-2.15-13.5-4.79-2.5-2.19-5.64-4.58-11.94-5.58 1.17-1.18 1.88-2.52 2.51-3.66.68-1.23 1.29-2.2 2.27-2.88.76-.52 1.98-.84 3.66-.94v.55c0 2.39 2 4.34 4.38 4.34h17.24a4.39 4.39 0 0 0 4.38-4.34v-4.24c0-2.38-2-4.42-4.38-4.42zm0 3h17.24c.8 0 1.38.62 1.38 1.42v4.24c0 .81-.57 1.34-1.38 1.34h-17.24c-.8 0-1.38-.53-1.38-1.34v-4.24c0-.8.57-1.42 1.38-1.42zm-39.96 11.02h17.16c.81 0 1.42.6 1.42 1.4v4.24c0 .81-.61 1.41-1.42 1.41H82.92c-.8 0-1.42-.6-1.42-1.4v-4.25c0-.8.61-1.4 1.42-1.4zm57.04 9.98h17.09c.8 0 1.45.57 1.45 1.38v4.17c0 .8-.65 1.45-1.45 1.45h-17.1c-.8 0-1.45-.65-1.45-1.45v-4.17c0-.8.65-1.38 1.46-1.38z" fill="#fff" color="#000" transform="matrix(4 0 0 4 -162 -450.91)"/><g fill="#8f0000"><path d="M91 954.34v8.45l-8 1.45v60.07H69.03l-28.53-47.1-.5.05v37.2l8 1.44v8.41H19v-8.4l7.45-1.45v-50.22L19 962.79v-8.46h21.48l28.37 47.1.15-.04v-37.15l-7-1.45v-8.45h29zM95 997.83q0-11.63 6.49-19.03 6.53-7.45 18.02-7.45 11.53 0 18.02 7.4 6.54 7.4 6.54 19.08v1q0 11.74-6.54 19.14-6.49 7.35-17.93 7.35-11.58 0-18.11-7.35-6.5-7.4-6.5-19.13v-1.01zm14.03 1q0 7.12 2.5 11.45 2.5 4.28 8.08 4.28 5.43 0 7.93-4.33 2.54-4.33 2.54-11.4v-1q0-6.92-2.54-11.3-2.55-4.37-8.03-4.37t-7.98 4.37-2.5 11.3v1zM184.48 1017.96a17.15 17.15 0 0 1-5.82 5.48 15.17 15.17 0 0 1-7.59 1.88c-6.4 0-11.4-2.35-14.95-7.03-3.52-4.67-5.13-10.86-5.13-18.55v-1c0-8.21 1.62-14.83 5.18-19.86 3.56-5.03 8.56-7.54 15-7.54 2.6 0 4.93.57 7.01 1.73a17.94 17.94 0 0 1 5.81 4.8v-18.64l-8-1.45v-8.46h22v65.13l6 1.44v8.43h-18.45l-1.06-6.36zm-19.49-18.22c0 4.55.63 8.14 2.14 10.77 1.54 2.6 4.04 3.9 7.5 3.9 2.05 0 3.83-.43 5.33-1.26 1.5-.83 3.07-2.03 4.03-3.6v-22.06a11.27 11.27 0 0 0-4.03-3.85 9.62 9.62 0 0 0-5.24-1.4c-3.43 0-5.92 1.53-7.5 4.57s-2.23 7.02-2.23 11.92v1.01zM233.7 1025.28c-7.5 0-13.5-2.4-17.98-7.21-4.48-4.81-6.73-10.91-6.73-18.32v-1.92c0-7.72 2.12-14.08 6.35-19.08 4.26-5 9.96-7.46 17.1-7.43 7.03 0 12.47 2.1 16.35 6.33a23.46 23.46 0 0 1 6.2 17.15v7.52h-31.43l-.1.41c.26 3.43 1.4 6.25 3.41 8.46 2.05 2.21 4.83 3.32 8.32 3.32 3.1 0 5.69-.3 7.74-.91 2.05-.64 4.29-1.64 6.73-2.98l3.8 8.65a27.59 27.59 0 0 1-8.37 4.28 35.28 35.28 0 0 1-11.4 1.73zm-1.25-43.16c-2.6 0-4.65.99-6.15 2.98s-2.44 4.6-2.8 7.83l.15.4H241v-1.41c0-2.98-.84-5.35-2.25-7.11-1.37-1.8-3.47-2.7-6.3-2.7zM291.99 1000.32h-27v-11h27zM331.88 954.34c7.95 0 14.18 1.82 18.7 5.47 4.52 3.63 6.4 8.64 6.4 15.05 0 3.52-.57 6.58-2.46 9.18-1.89 2.6-4.66 4.7-8.31 6.3 4.13 1.21 7.1 3.25 8.89 6.1a19.02 19.02 0 0 1 2.89 10.52v3.56c0 1.54.15 2.74.76 3.6.6.84 1.62 1.34 3.03 1.5l1.2.24v8.46h-6.73c-4.58 0-7.8-1.24-9.66-3.7s-2.6-5.66-2.6-9.57v-3.99c0-3.4-1.1-6.05-2.93-7.98-1.8-1.95-4.34-2.98-7.64-3.07H322v18.45l7 1.44v8.42h-29v-8.42l8-1.44v-50.22l-8-1.44v-8.46h31.89zm-9.95 30.85h9.71c3.91 0 6.84-.83 8.8-2.5s2.93-4.07 2.93-7.2c0-3.15-.98-5.65-2.93-7.5-1.92-1.9-4.78-2.84-8.56-2.84h-9.9v20.04zM412.99 993.32h-23v20h22.21l.63-8h10.2v18.99H368v-8.42l8-1.44v-50.22l-8-1.44v-8.47h54.95v19h-10.3l-.63-8H390v17h23v11zM462.48 954.36c8.55 0 15.6 2.71 21.14 8.19 5.55 5.45 8.36 12.42 8.36 20.98v11.58c0 8.59-2.81 15.63-8.36 21.08-5.54 5.41-12.59 8.12-21.14 8.12h-31.5v-8.41l7-1.52v-50.22l-7-1.37v-8.43l7.46-.08 24.04.08zm-10.5 10.76v48.4l9.77.02c5.03.02 8.98-1.7 11.83-5.1 2.85-3.39 4.4-7.8 4.4-13.28v-11.68c0-5.42-1.55-9.84-4.4-13.24-2.85-3.4-6.8-5.1-11.83-5.1l-9.77-.02z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -558,11 +558,22 @@ RED.history = (function() {
} else if (ev.t == "reorder") {
inverseEv = {
t: 'reorder',
order: RED.nodes.getWorkspaceOrder(),
dirty: RED.nodes.dirty()
};
if (ev.order) {
RED.workspaces.order(ev.order);
if (ev.workspaces) {
inverseEv.workspaces = {
from: ev.workspaces.to,
to: ev.workspaces.from
}
RED.workspaces.order(ev.workspaces.from);
}
if (ev.nodes) {
inverseEv.nodes = {
z: ev.nodes.z,
from: ev.nodes.to,
to: ev.nodes.from
}
RED.nodes.setNodeOrder(ev.nodes.z,ev.nodes.from);
}
} else if (ev.t == "createGroup") {
inverseEv = {
@@ -658,6 +669,8 @@ RED.history = (function() {
push: function(ev) {
undoHistory.push(ev);
redoHistory = [];
RED.menu.setDisabled("menu-item-edit-undo", false);
RED.menu.setDisabled("menu-item-edit-redo", true);
},
pop: function() {
var ev = undoHistory.pop();
@@ -665,13 +678,24 @@ RED.history = (function() {
if (rev) {
redoHistory.push(rev);
}
RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0);
RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0);
},
peek: function() {
return undoHistory[undoHistory.length-1];
},
replace: function(ev) {
if (undoHistory.length === 0) {
RED.history.push(ev);
} else {
undoHistory[undoHistory.length-1] = ev;
}
},
clear: function() {
undoHistory = [];
redoHistory = [];
RED.menu.setDisabled("menu-item-edit-undo", true);
RED.menu.setDisabled("menu-item-edit-redo", true);
},
redo: function() {
var ev = redoHistory.pop();
@@ -681,6 +705,8 @@ RED.history = (function() {
undoHistory.push(uev);
}
}
RED.menu.setDisabled("menu-item-edit-undo", undoHistory.length === 0);
RED.menu.setDisabled("menu-item-edit-redo", redoHistory.length === 0);
}
}

View File

@@ -25,8 +25,9 @@ RED.i18n = (function() {
return {
init: function(options, done) {
apiRootUrl = options.apiRootUrl||"";
var preferredLanguage = localStorage.getItem("editor-language");
var preferredLanguage = localStorage.getItem("editor-language") || detectLanguage();
var opts = {
compatibilityJSON: 'v3',
backend: {
loadPath: apiRootUrl+'locales/__ns__?lng=__lng__',
},
@@ -37,6 +38,8 @@ RED.i18n = (function() {
defaultNS: "editor",
fallbackLng: ['en-US'],
returnObjects: true,
keySeparator: ".",
nsSeparator: ":",
interpolation: {
unescapeSuffix: 'HTML',
escapeValue: false,

View File

@@ -25,7 +25,9 @@
"ctrl-alt-o": "core:open-project",
"ctrl-g v": "core:show-version-control-tab",
"ctrl-shift-l": "core:show-event-log",
"ctrl-shift-p":"core:show-action-list"
"ctrl-shift-p":"core:show-action-list",
"alt-w": "core:hide-flow",
"alt-shift-w": "core:show-last-hidden-flow"
},
"red-ui-sidebar-node-config": {
"backspace": "core:delete-config-selection",
@@ -36,7 +38,9 @@
},
"red-ui-workspace": {
"backspace": "core:delete-selection",
"ctrl-backspace": "core:delete-selection-and-reconnect",
"delete": "core:delete-selection",
"ctrl-delete": "core:delete-selection-and-reconnect",
"enter": "core:edit-selected-node",
"ctrl-enter": "core:go-to-selection",
"ctrl-c": "core:copy-selection-to-internal-clipboard",
@@ -77,6 +81,15 @@
"right": "core:go-to-nearest-node-on-right",
"left": "core:go-to-nearest-node-on-left",
"up": "core:go-to-nearest-node-above",
"down": "core:go-to-nearest-node-below"
"down": "core:go-to-nearest-node-below",
"alt-a g": "core:align-selection-to-grid",
"alt-a l": "core:align-selection-to-left",
"alt-a r": "core:align-selection-to-right",
"alt-a t": "core:align-selection-to-top",
"alt-a b": "core:align-selection-to-bottom",
"alt-a m": "core:align-selection-to-middle",
"alt-a c": "core:align-selection-to-center",
"alt-a h": "core:distribute-selection-horizontally",
"alt-a v": "core:distribute-selection-vertically"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -201,6 +201,7 @@ var RED = (function() {
RED.projects.refresh(function(activeProject) {
loadFlows(function() {
RED.sidebar.info.refresh()
var showProjectWelcome = false;
if (!activeProject) {
// Projects enabled but no active project
RED.menu.setDisabled('menu-item-projects-open',true);
@@ -208,10 +209,10 @@ var RED = (function() {
if (activeProject === false) {
// User previously decline the migration to projects.
} else { // null/undefined
RED.projects.showStartup();
showProjectWelcome = true;
}
}
completeLoad();
completeLoad(showProjectWelcome);
});
});
} else {
@@ -251,6 +252,9 @@ var RED = (function() {
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6),true);
}
if (RED.workspaces.active() === 0 && RED.workspaces.count() > 0) {
RED.workspaces.show(RED.nodes.getWorkspaceOrder()[0])
}
} catch(err) {
console.warn(err);
RED.notify(
@@ -267,7 +271,7 @@ var RED = (function() {
});
}
function completeLoad() {
function completeLoad(showProjectWelcome) {
var persistentNotifications = {};
RED.comms.subscribe("notification/#",function(topic,msg) {
var parts = topic.split("/");
@@ -471,22 +475,33 @@ var RED = (function() {
var typeList;
var info;
if (topic == "notification/node/added") {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() {
$.get('nodes/'+id, function(data) {
appendNodeConfig(data);
RED.settings.refreshSettings(function(err, data) {
var addedTypes = [];
msg.forEach(function(m) {
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadNodeCatalog(id, function() {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+id,
success: function(data) {
appendNodeConfig(data);
}
});
});
});
});
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
loadIconList();
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
loadIconList();
})
} else if (topic == "notification/node/removed") {
for (i=0;i<msg.length;i++) {
m = msg[i];
@@ -499,18 +514,29 @@ var RED = (function() {
loadIconList();
} else if (topic == "notification/node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
$.get('nodes/'+msg.id, function(data) {
appendNodeConfig(data);
RED.settings.refreshSettings(function(err, data) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
});
}
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'nodes/'+msg.id,
success: function(data) {
appendNodeConfig(data);
typeList = "<ul><li>"+msg.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
}
});
}
});
}
} else if (topic == "notification/node/disabled") {
if (msg.types) {
@@ -535,17 +561,24 @@ var RED = (function() {
setTimeout(function() {
loader.end();
checkFirstRun(function() {
if (showProjectWelcome) {
RED.projects.showStartup();
}
});
},100);
}
function showAbout() {
$.get('red/about', function(data) {
var aboutHeader = '<div style="text-align:center;">'+
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data));
});
function checkFirstRun(done) {
if (RED.settings.theme("tours") === false) {
done();
return;
}
if (!RED.settings.get("editor.view.view-show-welcome-tours", true)) {
done();
return;
}
RED.actions.invoke("core:show-welcome-tour", RED.settings.get("editor.tours.welcome"), done);
}
function buildMainMenu() {
@@ -557,6 +590,22 @@ var RED = (function() {
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
]});
}
menuOptions.push({id:"menu-item-edit-menu", label:RED._("menu.label.edit"), options: [
{id: "menu-item-edit-undo", label:RED._("keyboard.undoChange"), disabled: true, onselect: "core:undo"},
{id: "menu-item-edit-redo", label:RED._("keyboard.redoChange"), disabled: true, onselect: "core:redo"},
null,
{id: "menu-item-edit-cut", label:RED._("keyboard.cutNode"), onselect: "core:cut-selection-to-internal-clipboard"},
{id: "menu-item-edit-copy", label:RED._("keyboard.copyNode"), onselect: "core:copy-selection-to-internal-clipboard"},
{id: "menu-item-edit-paste", label:RED._("keyboard.pasteNode"), disabled: true, onselect: "core:paste-from-internal-clipboard"},
null,
{id: "menu-item-edit-copy-group-style", label:RED._("keyboard.copyGroupStyle"), onselect: "core:copy-group-style"},
{id: "menu-item-edit-paste-group-style", label:RED._("keyboard.pasteGroupStyle"), disabled: true, onselect: "core:paste-group-style"},
null,
{id: "menu-item-edit-select-all", label:RED._("keyboard.selectAll"), onselect: "core:select-all-nodes"},
{id: "menu-item-edit-select-connected", label:RED._("keyboard.selectAllConnected"), onselect: "core:select-connected-nodes"},
{id: "menu-item-edit-select-none", label:RED._("keyboard.selectNone"), onselect: "core:select-none"}
]});
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
{id:"menu-item-palette",label:RED._("menu.label.palette.show"),toggle:true,onselect:"core:toggle-palette", selected: true},
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
@@ -564,6 +613,25 @@ var RED = (function() {
{id:"menu-item-action-list",label:RED._("keyboard.actionList"),onselect:"core:show-action-list"},
null
]});
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"},
null,
{id: "menu-item-view-tools-align-top", label:RED._("menu.label.alignTop"), disabled: true, onselect: "core:align-selection-to-top"},
{id: "menu-item-view-tools-align-middle", label:RED._("menu.label.alignMiddle"), disabled: true, onselect: "core:align-selection-to-middle"},
{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"}
]});
menuOptions.push(null);
if (RED.settings.theme("menu.menu-item-import-library", true)) {
menuOptions.push({id: "menu-item-import", label: RED._("menu.label.import"), onselect: "core:show-import-dialog"});
@@ -624,7 +692,6 @@ var RED = (function() {
RED.user.init();
RED.notifications.init();
RED.library.init();
RED.keyboard.init();
RED.palette.init();
RED.eventLog.init();
@@ -653,16 +720,13 @@ var RED = (function() {
RED.deploy.init(RED.settings.theme("deployButton",null));
buildMainMenu();
RED.keyboard.init(buildMainMenu);
RED.nodes.init();
RED.comms.connect();
$("#red-ui-main-container").show();
RED.actions.add("core:show-about", showAbout);
loadPluginList();
}

View File

@@ -19,7 +19,6 @@ RED.settings = (function () {
var loadedSettings = {};
var userSettings = {};
var settingsDirty = false;
var pendingSave;
var hasLocalStorage = function () {
@@ -126,7 +125,7 @@ RED.settings = (function () {
load(done);
}
var load = function(done) {
var refreshSettings = function(done) {
$.ajax({
headers: {
"Accept": "application/json"
@@ -136,6 +135,23 @@ RED.settings = (function () {
url: 'settings',
success: function (data) {
setProperties(data);
done(null, data);
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = "";
}
RED.user.login(function() { refreshSettings(done); });
} else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
}
}
});
}
var load = function(done) {
refreshSettings(function(err, data) {
if (!err) {
if (!RED.settings.user || RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
@@ -148,18 +164,8 @@ RED.settings = (function () {
console.log("D3",d3.version);
console.groupEnd();
loadUserSettings(done);
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = "";
}
RED.user.login(function() { load(done); });
} else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
}
}
});
})
};
function loadUserSettings(done) {
@@ -220,14 +226,28 @@ RED.settings = (function () {
return defaultValue;
}
}
function getLocal(key) {
return localStorage.getItem(key)
}
function setLocal(key, value) {
localStorage.setItem(key, value);
}
function removeLocal(key) {
localStorage.removeItem(key)
}
return {
init: init,
load: load,
loadUserSettings: loadUserSettings,
refreshSettings: refreshSettings,
set: set,
get: get,
remove: remove,
theme: theme
theme: theme,
setLocal: setLocal,
getLocal: getLocal,
removeLocal: removeLocal
}
})();

View File

@@ -4,6 +4,12 @@ RED.actions = (function() {
}
function addAction(name,handler) {
if (typeof handler !== 'function') {
throw new Error("Action handler not a function");
}
if (actions[name]) {
throw new Error("Cannot override existing action");
}
actions[name] = handler;
}
function removeAction(name) {
@@ -12,9 +18,11 @@ RED.actions = (function() {
function getAction(name) {
return actions[name];
}
function invokeAction(name,args) {
function invokeAction() {
var args = Array.prototype.slice.call(arguments);
var name = args.shift();
if (actions.hasOwnProperty(name)) {
actions[name](args);
actions[name].apply(null, args);
}
}
function listActions() {

View File

@@ -71,6 +71,7 @@ RED.clipboard = (function() {
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-download
@@ -81,6 +82,7 @@ RED.clipboard = (function() {
var data = $("#red-ui-clipboard-dialog-export-text").val();
downloadData("flows.json", data);
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-export
@@ -95,6 +97,7 @@ RED.clipboard = (function() {
$( this ).dialog( "close" );
copyText(flowData);
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
RED.view.focus();
} else {
var flowToExport = $("#red-ui-clipboard-dialog-export-text").val();
var selectedPath = activeLibraries[activeTab].getSelected();
@@ -110,6 +113,7 @@ RED.clipboard = (function() {
contentType: "application/json; charset=utf-8"
}).done(function() {
$(dialog).dialog( "close" );
RED.view.focus();
RED.notify(RED._("library.exportedToLibrary"),"success");
}).fail(function(xhr,textStatus,err) {
if (xhr.status === 401) {
@@ -171,6 +175,7 @@ RED.clipboard = (function() {
}
}
$( this ).dialog( "close" );
RED.view.focus();
}
},
{ // red-ui-clipboard-dialog-import-conflict
@@ -203,6 +208,7 @@ RED.clipboard = (function() {
// console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
$( this ).dialog( "close" );
RED.view.focus();
}
}
],
@@ -452,7 +458,7 @@ RED.clipboard = (function() {
var libraries = RED.settings.libraries || [];
libraries.forEach(function(lib) {
var tabId = "red-ui-clipboard-dialog-import-tab-library-"+lib.id
var tabId = "red-ui-clipboard-dialog-import-tab-"+lib.id
tabs.addTab({
id: tabId,
label: RED._(lib.label||lib.id)
@@ -498,6 +504,13 @@ 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) {
$("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
$("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
} else {
$("#red-ui-clipboard-dialog-import-opt-current").removeClass('disabled').addClass("selected");
$("#red-ui-clipboard-dialog-import-opt-new").removeClass("selected");
}
$("#red-ui-clipboard-dialog-import-opt > a").on("click", function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
@@ -611,9 +624,6 @@ RED.clipboard = (function() {
activeLibraries[tabId] = browser;
})
$("#red-ui-clipboard-dialog-tab-library-name").on("keyup", validateExportFilename);
$("#red-ui-clipboard-dialog-tab-library-name").on('paste',function() { setTimeout(validateExportFilename,10)});
$("#red-ui-clipboard-dialog-export").button("enable");
@@ -636,7 +646,6 @@ RED.clipboard = (function() {
label: RED._("editor.types.json")
});
var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: []
})
@@ -701,6 +710,13 @@ RED.clipboard = (function() {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.groups(activeWorkspace);
nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
RED.nodes.eachConfig(function(n) {
if (n.z === RED.workspaces.active() && n._def.hasUsers === false) {
// Grab any config nodes scoped to this flow that don't
// require any flow-nodes to use them
nodes.push(n);
}
});
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
@@ -731,16 +747,22 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-import-conflict").hide();
var selection = RED.workspaces.selection();
if (selection.length > 0) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
if (RED.workspaces.active() === 0) {
$("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected');
$("#red-ui-clipboard-dialog-export-rng-flow").addClass('disabled').removeClass('selected');
$("#red-ui-clipboard-dialog-export-rng-full").trigger("click");
} else {
selection = RED.view.selection();
if (selection.nodes) {
var selection = RED.workspaces.selection();
if (selection.length > 0) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
} else {
$("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected');
$("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
selection = RED.view.selection();
if (selection.nodes) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
} else {
$("#red-ui-clipboard-dialog-export-rng-selected").addClass('disabled').removeClass('selected');
$("#red-ui-clipboard-dialog-export-rng-flow").trigger("click");
}
}
}
if (format === "red-ui-clipboard-dialog-export-fmt-full") {

View File

@@ -0,0 +1,115 @@
(function($) {
/**
* Attach to an <input type="text"> to provide auto-complete
*
* $("#node-red-text").autoComplete({
* search: function(value) { return ['a','b','c'] }
* })
*
* options:
*
* search : function(value, [done])
* A function that is passed the current contents of the input whenever
* it changes.
* The function must either return auto-complete options, or pass them
* to the optional 'done' parameter.
* If the function signature includes 'done', it must be used
*
* The auto-complete options should be an array of objects in the form:
* {
* value: String : the value to insert if selected
* label: String|DOM Element : the label to display in the dropdown.
* }
*
*/
$.widget( "nodered.autoComplete", {
_create: function() {
var that = this;
this.completionMenuShown = false;
this.options.search = this.options.search || function() { return [] }
this.element.addClass("red-ui-autoComplete")
this.element.on("keydown.red-ui-autoComplete", function(evt) {
if ((evt.keyCode === 13 || evt.keyCode === 9) && that.completionMenuShown) {
var opts = that.menu.options();
that.element.val(opts[0].value);
that.menu.hide();
evt.preventDefault();
}
})
this.element.on("keyup.red-ui-autoComplete", function(evt) {
if (evt.keyCode === 13 || evt.keyCode === 9 || evt.keyCode === 27) {
// ENTER / TAB / ESCAPE
return
}
if (evt.keyCode === 8 || evt.keyCode === 46) {
// Delete/Backspace
if (!that.completionMenuShown) {
return;
}
}
that._updateCompletions(this.value);
});
},
_showCompletionMenu: function(completions) {
if (this.completionMenuShown) {
return;
}
this.menu = RED.popover.menu({
tabSelect: true,
width: 300,
maxHeight: 200,
class: "red-ui-autoComplete-container",
options: completions,
onselect: (opt) => { this.element.val(opt.value); this.element.focus(); this.element.trigger("change") },
onclose: () => { this.completionMenuShown = false; delete this.menu; this.element.focus()}
});
this.menu.show({
target: this.element
})
this.completionMenuShown = true;
},
_updateCompletions: function(val) {
var that = this;
if (val.trim() === "") {
if (this.completionMenuShown) {
this.menu.hide();
}
return;
}
function displayResults(completions,requestId) {
if (requestId && requestId !== that.pendingRequest) {
// This request has been superseded
return
}
if (!completions || completions.length === 0) {
if (that.completionMenuShown) {
that.menu.hide();
}
return
}
if (that.completionMenuShown) {
that.menu.options(completions);
} else {
that._showCompletionMenu(completions);
}
}
if (this.options.search.length === 2) {
var requestId = 1+Math.floor(Math.random()*10000);
this.pendingRequest = requestId;
this.options.search(val,function(completions) { displayResults(completions,requestId);})
} else {
displayResults(this.options.search(val))
}
},
_destroy: function() {
this.element.removeClass("red-ui-autoComplete")
this.element.off("keydown.red-ui-autoComplete")
this.element.off("keyup.red-ui-autoComplete")
if (this.completionMenuShown) {
this.menu.hide();
}
}
});
})(jQuery);

View File

@@ -82,12 +82,19 @@ RED.menu = (function() {
linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+opt.label+'</span>'+
'<span class="red-ui-menu-sublabel">'+opt.sublabel+'</span></span>'
} else {
linkContent += '<span class="red-ui-menu-label">'+opt.label+'</span>'
linkContent += '<span class="red-ui-menu-label"><span>'+opt.label+'</span></span>'
}
linkContent += '</a>';
var link = $(linkContent).appendTo(item);
opt.link = link;
if (typeof opt.onselect === 'string') {
var 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"));
}
}
menuItems[opt.id] = opt;
@@ -276,6 +283,22 @@ RED.menu = (function() {
}
}
function refreshShortcuts() {
for (var id in menuItems) {
if (menuItems.hasOwnProperty(id)) {
var opt = menuItems[id];
if (typeof opt.onselect === "string" && opt.shortcutSpan) {
opt.shortcutSpan.remove();
delete opt.shortcutSpan;
var 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(opt.link.find(".red-ui-menu-label"));
}
}
}
}
}
return {
init: createMenu,
setSelected: setSelected,
@@ -284,6 +307,7 @@ RED.menu = (function() {
setDisabled: setDisabled,
addItem: addItem,
removeItem: removeItem,
setAction: setAction
setAction: setAction,
refreshShortcuts: refreshShortcuts
}
})();

View File

@@ -13,24 +13,138 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/*
* RED.popover.create(options) - create a popover callout box
* RED.popover.tooltip(target,content, action) - add a tooltip to an element
* RED.popover.menu(options) - create a dropdown menu
* RED.popover.panel(content) - create a dropdown container element
*/
/*
* RED.popover.create(options)
*
* options
* - target : DOM element - the element to target with the popover
* - direction : string - position of the popover relative to target
* 'top', 'right'(default), 'bottom', 'left', 'inset-[top,right,bottom,left]'
* - trigger : string - what triggers the popover to be displayed
* 'hover' - display when hovering the target
* 'click' - display when target is clicked
* 'modal' - hmm not sure, need to find where we use that mode
* - content : string|function - contents of the popover. If a string, handled
* as raw HTML, so take care.
* If a function, can return a String to be added
* as text (not HTML), or a DOM element to append
* - delay : object - sets show/hide delays after mouseover/out events
* { show: 750, hide: 50 }
* - autoClose : number - delay before closing the popover in some cases
* if trigger is click - delay after mouseout
* else if trigger not hover/modal - delay after showing
* - width : number - width of popover, default 'auto'
* - maxWidth : number - max width of popover, default 'auto'
* - size : string - scale of popover. 'default', 'small'
* - offset : number - px offset from target
* - tooltip : boolean - if true, clicking on popover closes it
* - class : string - optional css class to apply to popover
* - interactive : if trigger is 'hover' and this is set to true, allow the mouse
* to move over the popover without hiding it.
*
* Returns the popover object with the following properties/functions:
* properties:
* - element : DOM element - the popover dom element
* functions:
* - setContent(content) - change the popover content. This only works if the
* popover is not currently displayed. It does not
* change the content of a visible popover.
* - open(instant) - show the popover. If 'instant' is true, don't fade in
* - close(instant) - hide the popover. If 'instant' is true, don't fade out
* - move(options) - move the popover. The options parameter can take many
* of the options detailed above including:
* target,direction,content,width,offset
* Other settings probably won't work because we haven't needed to change them
*/
/*
* RED.popover.tooltip(target,content, action)
*
* - target : DOM element - the element to apply the tooltip to
* - content : string - the text of the tooltip
* - action : string - *optional* the name of an Action this tooltip is tied to
* For example, it 'target' is a button that triggers a particular action.
* The tooltip will include the keyboard shortcut for the action
* if one is defined
*
*/
/*
* RED.popover.menu(options)
*
* options
* - options : array - list of menu options - see below for format
* - width : number - width of the menu. Default: 'auto'
* - class : string - class to apply to the menu container
* - maxHeight : number - maximum height of menu before scrolling items. Default: none
* - onselect : function(item) - called when a menu item is selected, if that item doesn't
* have its own onselect function
* - onclose : function(cancelled) - called when the menu is closed
* - disposeOnClose : boolean - by default, the menu is discarded when it closes
* and mustbe rebuilt to redisplay. Setting this to 'false'
* keeps the menu on the DOM so it can be shown again.
*
* Menu Options array:
* [
* label : string|DOM element - the label of the item. Can be custom DOM element
* onselect : function - called when the item is selected
* ]
*
* Returns the menu object with the following functions:
*
* - options([menuItems]) - if menuItems is undefined, returns the current items.
* otherwise, sets the current menu items
* - show(opts) - shows the menu. `opts` is an object of options. See RED.popover.panel.show(opts)
* for the full list of options. In most scenarios, this just needs:
* - target : DOM element - the element to display the menu below
* - hide(cancelled) - hide the menu
*/
/*
* RED.popover.panel(content)
* Create a UI panel that can be displayed relative to any target element.
* Handles auto-closing when mouse clicks outside the panel
*
* - 'content' - DOM element to display in the panel
*
* Returns the panel object with the following functions:
*
* properties:
* - container : DOM element - the panel element
*
* functions:
* - show(opts) - show the panel.
* opts:
* - onclose : function - called when the panel closes
* - closeButton : DOM element - if the panel is closeable by a click of a button,
* by providing a reference to it here, we can
* handle the events properly to hide the panel
* - target : DOM element - the element to display the panel relative to
* - align : string - should the panel align to the left or right edge of target
* default: 'right'
* - offset : Array - px offset to apply from the target. [width, height]
* - dispose : boolean - whether the panel should be removed from DOM when hidden
* default: true
* - hide(dispose) - hide the panel.
*/
RED.popover = (function() {
var deltaSizes = {
"default": {
top: 10,
topTop: 30,
leftRight: 17,
leftLeft: 25,
leftBottom: 8,
leftTop: 11
x: 12,
y: 12
},
"small": {
top: 6,
topTop: 20,
leftRight: 8,
leftLeft: 26,
leftBottom: 8,
leftTop: 9
x:8,
y:8
}
}
function createPopover(options) {
@@ -41,7 +155,9 @@ RED.popover = (function() {
var delay = options.delay || { show: 750, hide: 50 };
var autoClose = options.autoClose;
var width = options.width||"auto";
var maxWidth = options.maxWidth;
var size = options.size||"default";
var popupOffset = options.offset || 0;
if (!deltaSizes[size]) {
throw new Error("Invalid RED.popover size value:",size);
}
@@ -49,6 +165,8 @@ RED.popover = (function() {
var timer = null;
var active;
var div;
var contentDiv;
var currentStyle;
var openPopup = function(instant) {
if (active) {
@@ -58,6 +176,10 @@ RED.popover = (function() {
return;
}
div = $('<div class="red-ui-popover"></div>');
if (options.class) {
div.addClass(options.class);
}
contentDiv = $('<div class="red-ui-popover-content">').appendTo(div);
if (size !== "default") {
div.addClass("red-ui-popover-size-"+size);
}
@@ -67,71 +189,23 @@ RED.popover = (function() {
return;
}
if (typeof result === 'string') {
div.text(result);
contentDiv.text(result);
} else {
div.append(result);
contentDiv.append(result);
}
} else {
div.html(content);
}
if (width !== "auto") {
div.width(width);
contentDiv.html(content);
}
div.appendTo("body");
var targetPos = target.offset();
var targetWidth = target.outerWidth();
var targetHeight = target.outerHeight();
var divHeight = div.height();
var divWidth = div.width();
var paddingRight = 10;
movePopup({target,direction,width,maxWidth});
var viewportTop = $(window).scrollTop();
var viewportLeft = $(window).scrollLeft();
var viewportBottom = viewportTop + $(window).height();
var viewportRight = viewportLeft + $(window).width();
var top = 0;
var left = 0;
var d = direction;
if (d === 'right') {
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
} else if (d === 'left') {
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
} else if (d === 'bottom') {
top = targetPos.top+targetHeight+deltaSizes[size].top;
left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom;
if (left < 0) {
d = "right";
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
} else if (left+divWidth+paddingRight > viewportRight) {
d = "left";
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
if (top+divHeight+targetHeight/2 + 5 > viewportBottom) {
top -= (top+divHeight+targetHeight/2 - viewportBottom + 5)
}
} else if (top+divHeight > viewportBottom) {
d = 'top';
top = targetPos.top-deltaSizes[size].topTop-divHeight;
left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop;
}
} else if (d === 'top') {
top = targetPos.top-deltaSizes[size].topTop-divHeight;
left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop;
if (top < 0) {
d = 'bottom';
top = targetPos.top+targetHeight+deltaSizes[size].top;
left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom;
}
}
div.addClass('red-ui-popover-'+d).css({top: top, left: left});
if (existingPopover) {
existingPopover.close(true);
}
target.data("red-ui-popover",res)
if (options.trigger !== 'manual') {
target.data("red-ui-popover",res)
}
if (options.tooltip) {
div.on("mousedown", function(evt) {
closePopup(true);
@@ -161,6 +235,104 @@ RED.popover = (function() {
}
}
}
var movePopup = function(options) {
target = options.target || target;
direction = options.direction || direction || "right";
popupOffset = options.offset || popupOffset;
var transition = options.transition;
var width = options.width||"auto";
div.width(width);
if (options.maxWidth) {
div.css("max-width",options.maxWidth)
} else {
div.css("max-width", 'auto');
}
var targetPos = target[0].getBoundingClientRect();
var targetHeight = targetPos.height;
var targetWidth = targetPos.width;
var divHeight = div.outerHeight();
var divWidth = div.outerWidth();
var paddingRight = 10;
var viewportTop = $(window).scrollTop();
var viewportLeft = $(window).scrollLeft();
var viewportBottom = viewportTop + $(window).height();
var viewportRight = viewportLeft + $(window).width();
var top = 0;
var left = 0;
if (direction === 'right') {
top = targetPos.top+targetHeight/2-divHeight/2;
left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset;
} else if (direction === 'left') {
top = targetPos.top+targetHeight/2-divHeight/2;
left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset;
} else if (direction === 'bottom') {
top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset;
left = targetPos.left+targetWidth/2-divWidth/2;
if (left < 0) {
direction = "right";
top = targetPos.top+targetHeight/2-divHeight/2;
left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset;
} else if (left+divWidth+paddingRight > viewportRight) {
direction = "left";
top = targetPos.top+targetHeight/2-divHeight/2;
left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset;
if (top+divHeight+targetHeight/2 + 5 > viewportBottom) {
top -= (top+divHeight+targetHeight/2 - viewportBottom + 5)
}
} else if (top+divHeight > viewportBottom) {
direction = 'top';
top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset;
left = targetPos.left+targetWidth/2-divWidth/2;
}
} else if (direction === 'top') {
top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset;
left = targetPos.left+targetWidth/2-divWidth/2;
if (top < 0) {
direction = 'bottom';
top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset;
left = targetPos.left+targetWidth/2-divWidth/2;
}
} else if (/inset/.test(direction)) {
top = targetPos.top + targetHeight/2 - divHeight/2;
left = targetPos.left + targetWidth/2 - divWidth/2;
if (/bottom/.test(direction)) {
top = targetPos.top + targetHeight - divHeight-popupOffset;
}
if (/top/.test(direction)) {
top = targetPos.top+popupOffset;
}
if (/left/.test(direction)) {
left = targetPos.left+popupOffset;
}
if (/right/.test(direction)) {
left = targetPos.left + targetWidth - divWidth-popupOffset;
}
}
if (currentStyle) {
div.removeClass(currentStyle);
}
if (transition) {
div.css({
"transition": "0.6s ease",
"transition-property": "top,left,right,bottom"
})
}
currentStyle = 'red-ui-popover-'+direction;
div.addClass(currentStyle).css({top: top, left: left});
if (transition) {
setTimeout(function() {
div.css({
"transition": "none"
});
},600);
}
}
var closePopup = function(instant) {
$(document).off('mousedown.red-ui-popover');
if (!active) {
@@ -236,8 +408,10 @@ RED.popover = (function() {
},autoClose);
}
var res = {
get element() { return div },
setContent: function(_content) {
content = _content;
return res;
},
open: function (instant) {
@@ -249,6 +423,10 @@ RED.popover = (function() {
active = false;
closePopup(instant);
return res;
},
move: function(options) {
movePopup(options);
return
}
}
return res;
@@ -258,18 +436,17 @@ RED.popover = (function() {
return {
create: createPopover,
tooltip: function(target,content, action) {
var label = content;
if (action) {
label = function() {
var label = content;
var label = function() {
var label = content;
if (action) {
var shortcut = RED.keyboard.getShortcut(action);
if (shortcut && shortcut.key) {
label = $('<span>'+content+' <span class="red-ui-popover-key">'+RED.keyboard.formatKey(shortcut.key, true)+'</span></span>');
}
return label;
}
return label;
}
return RED.popover.create({
var popover = RED.popover.create({
tooltip: true,
target:target,
trigger: "hover",
@@ -278,6 +455,14 @@ RED.popover = (function() {
content: label,
delay: { show: 750, hide: 50 }
});
popover.setContent = function(newContent) {
content = newContent;
}
popover.setAction = function(newAction) {
action = newAction;
}
return popover;
},
menu: function(options) {
var list = $('<ul class="red-ui-menu"></ul>');
@@ -286,20 +471,47 @@ RED.popover = (function() {
}
var menuOptions = options.options || [];
var first;
menuOptions.forEach(function(opt) {
var item = $('<li>').appendTo(list);
var link = $('<a href="#"></a>').text(opt.label).appendTo(item);
link.on("click", function(evt) {
evt.preventDefault();
if (opt.onselect) {
opt.onselect();
}
menu.hide();
})
if (!first) { first = link}
})
var container = RED.popover.panel(list);
if (options.width) {
container.container.width(options.width);
}
if (options.class) {
container.container.addClass(options.class);
}
if (options.maxHeight) {
container.container.css({
"max-height": options.maxHeight,
"overflow-y": 'auto'
})
}
var menu = {
options: function(opts) {
if (opts === undefined) {
return menuOptions
}
menuOptions = opts || [];
list.empty();
menuOptions.forEach(function(opt) {
var item = $('<li>').appendTo(list);
var link = $('<a href="#"></a>').appendTo(item);
if (typeof opt.label == "string") {
link.text(opt.label)
} else if (opt.label){
opt.label.appendTo(link);
}
link.on("click", function(evt) {
evt.preventDefault();
if (opt.onselect) {
opt.onselect();
} else if (options.onselect) {
options.onselect(opt);
}
menu.hide();
})
if (!first) { first = link}
})
},
show: function(opts) {
$(document).on("keydown.red-ui-menu", function(evt) {
var currentItem = list.find(":focus").parent();
@@ -308,11 +520,9 @@ RED.popover = (function() {
// DOWN
if (currentItem.length > 0) {
if (currentItem.index() === menuOptions.length-1) {
console.log("WARP TO TOP")
// Wrap to top of list
list.children().first().children().first().focus();
} else {
console.log("GO DOWN ONE")
currentItem.next().children().first().focus();
}
} else {
@@ -323,11 +533,9 @@ RED.popover = (function() {
// UP
if (currentItem.length > 0) {
if (currentItem.index() === 0) {
console.log("WARP TO BOTTOM")
// Wrap to bottom of list
list.children().last().children().first().focus();
} else {
console.log("GO UP ONE")
currentItem.prev().children().first().focus();
}
} else {
@@ -337,6 +545,11 @@ RED.popover = (function() {
// ESCAPE
evt.preventDefault();
menu.hide(true);
} else if (evt.keyCode === 9 && options.tabSelect) {
// TAB - with tabSelect enabled
evt.preventDefault();
currentItem.find("a").trigger("click");
}
evt.stopPropagation();
})
@@ -356,6 +569,7 @@ RED.popover = (function() {
}
}
}
menu.options(menuOptions);
return menu;
},
panel: function(content) {
@@ -363,7 +577,6 @@ RED.popover = (function() {
panel.css({ display: "none" });
panel.appendTo(document.body);
content.appendTo(panel);
var closeCallback;
function hide(dispose) {
$(document).off("mousedown.red-ui-popover-panel-close");
@@ -378,22 +591,23 @@ RED.popover = (function() {
}
function show(options) {
var closeCallback = options.onclose;
var closeButton = options.closeButton;
var target = options.target;
var align = options.align || "right";
var offset = options.offset || [0,0];
var pos = target.offset();
var targetWidth = target.width();
var targetHeight = target.height();
var targetHeight = target.outerHeight();
var panelHeight = panel.height();
var panelWidth = panel.width();
var top = (targetHeight+pos.top) + offset[1];
if (top+panelHeight > $(window).height()) {
if (top+panelHeight-$(document).scrollTop() > $(window).height()) {
top -= (top+panelHeight)-$(window).height() + 5;
}
if (top < 0) {
panelHeight.height(panelHeight+top)
panel.height(panelHeight+top)
top = 0;
}
if (align === "right") {
@@ -420,7 +634,8 @@ RED.popover = (function() {
});
$(document).on("mousedown.red-ui-popover-panel-close", function(event) {
if(!$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) {
var hitCloseButton = closeButton && $(event.target).closest(closeButton).length;
if(!hitCloseButton && !$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) {
if (closeCallback) {
closeCallback();
}

View File

@@ -38,6 +38,7 @@ RED.tabs = (function() {
if (options.vertical) {
wrapper.addClass("red-ui-tabs-vertical");
}
if (options.addButton) {
wrapper.addClass("red-ui-tabs-add");
var addButton = $('<div class="red-ui-tab-button red-ui-tabs-add"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper);
@@ -75,6 +76,8 @@ RED.tabs = (function() {
});
}
if (options.searchButton) {
// This is soft-deprecated as we don't use this in the core anymore
// We no use the `menu` option to provide a drop-down list of actions
wrapper.addClass("red-ui-tabs-search");
var searchButton = $('<div class="red-ui-tab-button red-ui-tabs-search"><a href="#"><i class="fa fa-list-ul"></i></a></div>').appendTo(wrapper);
searchButton.find('a').on("click", function(evt) {
@@ -94,6 +97,52 @@ RED.tabs = (function() {
}
}
if (options.menu) {
wrapper.addClass("red-ui-tabs-menu");
var menuButton = $('<div class="red-ui-tab-button red-ui-tabs-menu"><a href="#"><i class="fa fa-caret-down"></i></a></div>').appendTo(wrapper);
var menuButtonLink = menuButton.find('a')
var menuOpen = false;
var menu;
menuButtonLink.on("click", function(evt) {
evt.stopPropagation();
evt.preventDefault();
if (menuOpen) {
menu.remove();
menuOpen = false;
return;
}
menuOpen = true;
var menuOptions = [];
if (typeof options.searchButton === 'function') {
menuOptions = options.menu()
} else if (Array.isArray(options.menu)) {
menuOptions = options.menu;
} else if (typeof options.menu === 'function') {
menuOptions = options.menu();
}
menu = RED.menu.init({options: menuOptions});
menu.attr("id",options.id+"-menu");
menu.css({
position: "absolute"
})
menu.appendTo("body");
var elementPos = menuButton.offset();
menu.css({
top: (elementPos.top+menuButton.height()-2)+"px",
left: (elementPos.left - menu.width() + menuButton.width())+"px"
})
$(".red-ui-menu.red-ui-menu-dropdown").hide();
$(document).on("click.red-ui-tabmenu", function(evt) {
$(document).off("click.red-ui-tabmenu");
menuOpen = false;
menu.remove();
});
menu.show();
})
}
var scrollLeft;
var scrollRight;
@@ -117,9 +166,9 @@ RED.tabs = (function() {
}
})
scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a");
scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();});
scrollLeft.on('mousedown',function(evt) {scrollEventHandler(evt, evt.shiftKey?('-='+scrollContainer.scrollLeft()):'-=150') }).on('click',function(evt){ evt.preventDefault();});
scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a");
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();});
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,evt.shiftKey?('+='+(scrollContainer[0].scrollWidth - scrollContainer.width()-scrollContainer.scrollLeft())):'+=150') }).on('click',function(evt){ evt.preventDefault();});
}
if (options.collapsible) {
@@ -337,6 +386,12 @@ RED.tabs = (function() {
if (link.length === 0) {
return;
}
if (link.parent().hasClass("hide-tab")) {
link.parent().removeClass("hide-tab").removeClass("hide");
if (options.onshow) {
options.onshow(tabs[link.attr('href').slice(1)])
}
}
if (!link.parent().hasClass("active")) {
ul.children().removeClass("active");
ul.children().css({"transition": "width 100ms"});
@@ -362,13 +417,13 @@ RED.tabs = (function() {
}
}
function activatePreviousTab() {
var previous = ul.find("li.active").prev();
var previous = findPreviousVisibleTab();
if (previous.length > 0) {
activateTab(previous.find("a"));
}
}
function activateNextTab() {
var next = ul.find("li.active").next();
var next = findNextVisibleTab();
if (next.length > 0) {
activateTab(next.find("a"));
}
@@ -378,7 +433,9 @@ RED.tabs = (function() {
if (options.vertical) {
return;
}
var tabs = ul.find("li.red-ui-tab");
var allTabs = ul.find("li.red-ui-tab");
var tabs = allTabs.filter(":not(.hide-tab)");
var hiddenTabs = allTabs.filter(".hide-tab");
var width = wrapper.width();
var tabCount = tabs.length;
var tabWidth;
@@ -388,7 +445,7 @@ RED.tabs = (function() {
var visibleCount = collapsedButtonsRow.children(":visible").length;
tabWidth = width - collapsedButtonsRow.width()-10;
var maxTabWidth = 198;
var minTabWidth = 80;
var minTabWidth = 120;
if (tabWidth <= minTabWidth || (tabWidth < maxTabWidth && visibleCount > 5)) {
// The tab is too small. Hide the next button to make room
// Start at the end of the button row, -1 for the menu button
@@ -446,6 +503,7 @@ RED.tabs = (function() {
// }
tabs.css({width:currentTabWidth});
hiddenTabs.css({width:"0px"});
if (tabWidth < 50) {
// ul.find(".red-ui-tab-close").hide();
ul.find(".red-ui-tab-icon").hide();
@@ -486,24 +544,104 @@ RED.tabs = (function() {
}
var li = ul.find("a[href='#"+id+"']").parent();
if (li.hasClass("active")) {
var tab = li.prev();
var tab = findPreviousVisibleTab(li);
if (tab.length === 0) {
tab = li.next();
tab = findNextVisibleTab(li);
}
if (tab.length > 0) {
activateTab(tab.find("a"));
} else {
if (options.onchange) {
options.onchange(null);
}
}
activateTab(tab.find("a"));
}
li.remove();
if (tabs[id].pinned) {
pinnedTabsCount--;
li.one("transitionend", function(evt) {
li.remove();
if (tabs[id].pinned) {
pinnedTabsCount--;
}
if (options.onremove) {
options.onremove(tabs[id]);
}
delete tabs[id];
updateTabWidths();
if (collapsibleMenu) {
collapsibleMenu.remove();
collapsibleMenu = null;
}
})
li.addClass("hide-tab");
li.width(0);
}
function findPreviousVisibleTab(li) {
if (!li) {
li = ul.find("li.active");
}
if (options.onremove) {
options.onremove(tabs[id]);
var previous = li.prev();
while(previous.length > 0 && previous.hasClass("hide-tab")) {
previous = previous.prev();
}
delete tabs[id];
updateTabWidths();
if (collapsibleMenu) {
collapsibleMenu.remove();
collapsibleMenu = null;
return previous;
}
function findNextVisibleTab(li) {
if (!li) {
li = ul.find("li.active");
}
var next = li.next();
while(next.length > 0 && next.hasClass("hide-tab")) {
next = next.next();
}
return next;
}
function showTab(id) {
if (tabs[id]) {
var li = ul.find("a[href='#"+id+"']").parent();
if (li.hasClass("hide-tab")) {
li.removeClass("hide-tab").removeClass("hide");
if (ul.find("li.red-ui-tab:not(.hide-tab)").length === 1) {
activateTab(li.find("a"))
}
updateTabWidths();
if (options.onshow) {
options.onshow(tabs[id])
}
}
}
}
function hideTab(id) {
if (tabs[id]) {
var li = ul.find("a[href='#"+id+"']").parent();
if (!li.hasClass("hide-tab")) {
if (li.hasClass("active")) {
var tab = findPreviousVisibleTab(li);
if (tab.length === 0) {
tab = findNextVisibleTab(li);
}
if (tab.length > 0) {
activateTab(tab.find("a"));
} else {
if (options.onchange) {
options.onchange(null);
}
}
}
li.removeClass("active");
li.one("transitionend", function(evt) {
li.addClass("hide");
updateTabWidths();
if (options.onhide) {
options.onhide(tabs[id])
}
setTimeout(function() {
updateScroll()
},200)
})
li.addClass("hide-tab");
li.css({width:0})
}
}
}
@@ -663,6 +801,7 @@ RED.tabs = (function() {
link.on("click", function(evt) { evt.preventDefault(); })
link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); })
$('<span class="red-ui-tabs-fade"></span>').appendTo(li);
if (tab.closeable) {
li.addClass("red-ui-tabs-closeable")
@@ -672,6 +811,18 @@ RED.tabs = (function() {
event.preventDefault();
removeTab(tab.id);
});
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow"));
}
if (tab.hideable) {
li.addClass("red-ui-tabs-closeable")
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close 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);
@@ -679,10 +830,13 @@ RED.tabs = (function() {
$('<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);
}
// link.attr("title",tab.label);
RED.popover.tooltip(link,function() { return tab.label})
if (options.onadd) {
options.onadd(tab);
}
link.attr("title",tab.label);
if (ul.find("li.red-ui-tab").length == 1) {
activateTab(link);
}
@@ -783,22 +937,37 @@ RED.tabs = (function() {
previousTab: activatePreviousTab,
resize: updateTabWidths,
count: function() {
return ul.find("li.red-ui-tab").length;
return ul.find("li.red-ui-tab:not(.hide)").length;
},
activeIndex: function() {
return ul.find("li.active").index()
return ul.find("li.active").index()
},
contains: function(id) {
return ul.find("a[href='#"+id+"']").length > 0;
},
showTab: showTab,
hideTab: hideTab,
renameTab: function(id,label) {
tabs[id].label = label;
var tab = ul.find("a[href='#"+id+"']");
tab.attr("title",label);
tab.find("span.red-ui-text-bidi-aware").text(label).attr('dir', RED.text.bidi.resolveBaseTextDir(label));
updateTabWidths();
},
listTabs: function() {
return $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));
},
selection: getSelection,
clearSelection: function() {
if (options.onselect) {
var selection = ul.find("li.red-ui-tab.selected");
if (selection.length > 0) {
selection.removeClass("selected");
selectionChanged();
}
}
},
order: function(order) {
preferredOrder = order;
var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));

View File

@@ -48,7 +48,7 @@
});
this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"></button>');
if (enabledLabel || disabledLabel) {
this.buttonLabel = $("<span>").appendTo(this.button);
this.buttonLabel = $("<span>").appendTo(this.button).css("margin-left", "5px");
}
if (this.options.class) {

View File

@@ -24,6 +24,9 @@
* - rootSortable: boolean - if 'sortable' is set, then setting this to
* false, prevents items being sorted to the
* top level of the tree
* - autoSelect: boolean - default true - triggers item selection when navigating
* list by keyboard. If the list has checkboxed items
* you probably want to set this to false
*
* methods:
* - data(items) - clears existing items and replaces with new data
@@ -41,6 +44,7 @@
* sublabel: 'Local', // a sub-label for the item
* icon: 'fa fa-rocket', // (optional) icon for the item
* checkbox: true/false, // (optional) if present, display checkbox accordingly
* radio: 'group-name', // (optional) if present, display radio box - using group-name to set radio group
* selected: true/false, // (optional) whether the item is selected or not
* children: [] | function(done,item) // (optional) an array of child items, or a function
* // that will call the `done` callback with an array
@@ -49,6 +53,7 @@
* deferBuild: true/false, // don't build any ui elements for the item's children
* until it is expanded by the user.
* element: // custom dom element to use for the item - ignored if `label` is set
* collapsible: true/false, // prevent a parent item from being collapsed. default true.
* }
* ]
*
@@ -89,77 +94,99 @@
$.widget( "nodered.treeList", {
_create: function() {
var that = this;
var autoSelect = true;
if (that.options.autoSelect === false) {
autoSelect = false;
}
this.element.addClass('red-ui-treeList');
this.element.attr("tabIndex",0);
var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element);
this.element.on('keydown', function(evt) {
var selected = that._topList.find(".selected").parent().data('data');
if (!selected && (evt.keyCode === 40 || evt.keyCode === 38)) {
that.select(that._data[0]);
var focussed = that._topList.find(".focus").parent().data('data');
if (!focussed && (evt.keyCode === 40 || evt.keyCode === 38)) {
if (that._data[0]) {
if (autoSelect) {
that.select(that._data[0]);
} else {
that._topList.find(".focus").removeClass("focus")
}
that._data[0].treeList.label.addClass('focus')
}
return;
}
var target;
switch(evt.keyCode) {
case 32: // SPACE
case 13: // ENTER
if (evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
return
}
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
if (focussed.checkbox) {
focussed.treeList.checkbox.trigger("click");
} else if (focussed.radio) {
focussed.treeList.radio.trigger("click");
} else if (focussed.children) {
if (focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.collapse()
} else {
selected.treeList.expand()
focussed.treeList.expand()
}
} else {
that._trigger("confirm",null,selected)
that._trigger("confirm",null,focussed)
}
break;
case 37: // LEFT
evt.preventDefault();
evt.stopPropagation();
if (selected.children&& selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
} else if (selected.parent) {
target = selected.parent;
if (focussed.children&& focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.collapse()
} else if (focussed.parent) {
target = focussed.parent;
}
break;
case 38: // UP
evt.preventDefault();
evt.stopPropagation();
target = that._getPreviousSibling(selected);
target = that._getPreviousSibling(focussed);
if (target) {
target = that._getLastDescendant(target);
}
if (!target && selected.parent) {
target = selected.parent;
if (!target && focussed.parent) {
target = focussed.parent;
}
break;
case 39: // RIGHT
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (!selected.treeList.container.hasClass("expanded")) {
selected.treeList.expand()
if (focussed.children) {
if (!focussed.treeList.container.hasClass("expanded")) {
focussed.treeList.expand()
}
}
break
case 40: //DOWN
evt.preventDefault();
evt.stopPropagation();
if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) {
target = selected.children[0];
if (focussed.children && Array.isArray(focussed.children) && focussed.children.length > 0 && focussed.treeList.container.hasClass("expanded")) {
target = focussed.children[0];
} else {
target = that._getNextSibling(selected);
while (!target && selected.parent) {
selected = selected.parent;
target = that._getNextSibling(selected);
target = that._getNextSibling(focussed);
while (!target && focussed.parent) {
focussed = focussed.parent;
target = that._getNextSibling(focussed);
}
}
break
}
if (target) {
that.select(target);
if (autoSelect) {
that.select(target);
} else {
that._topList.find(".focus").removeClass("focus")
}
target.treeList.label.addClass('focus')
}
});
this._data = [];
@@ -313,7 +340,7 @@
if (child.depth !== parent.depth+1) {
child.depth = parent.depth+1;
// var labelPaddingWidth = ((child.gutter ? child.gutter[0].offsetWidth + 2 : 0) + (child.depth * 20));
var labelPaddingWidth = ((child.gutter?child.gutter.width()+2:0)+(child.depth*20));
var labelPaddingWidth = (((child.gutter&&!child.gutter.hasClass("red-ui-treeList-gutter-float"))?child.gutter.width()+2:0)+(child.depth*20));
child.treeList.labelPadding.width(labelPaddingWidth+'px');
if (child.element) {
$(child.element).css({
@@ -462,6 +489,9 @@
container.addClass("expanded");
}
item.treeList.collapse = function() {
if (item.collapsible === false) {
return
}
if (!item.children) {
return;
}
@@ -532,10 +562,12 @@
}).appendTo(label)
}
var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(depth*20);
// var labelPaddingWidth = (item.gutter ? item.gutter[0].offsetWidth + 2 : 0) + (depth * 20)
var labelPaddingWidth = ((item.gutter&&!item.gutter.hasClass("red-ui-treeList-gutter-float"))?item.gutter.width()+2:0)+(depth*20);
item.treeList.labelPadding = $('<span>').css({
display: "inline-block",
"flex-shrink": 0,
width: labelPaddingWidth+'px'
}).appendTo(label);
@@ -581,7 +613,7 @@
// Already a parent because we've got the angle-right icon
return;
}
$('<i class="fa fa-angle-right" />').appendTo(treeListIcon);
$('<i class="fa fa-angle-right" />').toggleClass("hide",item.collapsible === false).appendTo(treeListIcon);
treeListIcon.on("click.red-ui-treeList-expand", function(e) {
e.stopPropagation();
e.preventDefault();
@@ -632,6 +664,46 @@
label.on("click", function(e) {
e.stopPropagation();
cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
})
}
item.treeList.select = function(v) {
if (v !== item.selected) {
cb.trigger("click");
}
}
item.treeList.checkbox = cb;
selectWrapper.appendTo(label)
} else if (item.radio) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>');
var cb = $('<input class="red-ui-treeList-radio" type="radio">').prop('name', item.radio).prop('checked',item.selected).appendTo(selectWrapper);
cb.on('click', function(e) {
e.stopPropagation();
});
cb.on('change', function(e) {
item.selected = this.checked;
that._selected.forEach(function(selectedItem) {
if (selectedItem.radio === item.radio) {
selectedItem.treeList.label.removeClass("selected");
selectedItem.selected = false;
that._selected.delete(selectedItem);
}
})
if (item.selected) {
that._selected.add(item);
} else {
that._selected.delete(item);
}
label.toggleClass("selected",this.checked);
that._trigger("select",e,item);
})
if (!item.children) {
label.on("click", function(e) {
e.stopPropagation();
cb.trigger("click");
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
})
}
item.treeList.select = function(v) {
@@ -640,6 +712,7 @@
}
}
selectWrapper.appendTo(label)
item.treeList.radio = cb;
} else {
label.on("click", function(e) {
if (!that.options.multi) {
@@ -647,10 +720,14 @@
}
label.addClass("selected");
that._selected.add(item);
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
that._trigger("select",e,item)
})
label.on("dblclick", function(e) {
that._topList.find(".focus").removeClass("focus")
label.addClass('focus')
if (!item.children) {
that._trigger("confirm",e,item);
}
@@ -798,6 +875,9 @@
if (item.treeList.label) {
item.treeList.label.addClass("selected");
}
that._topList.find(".focus").removeClass("focus");
if (triggerEvent !== false) {
this._trigger("select",null,item)
}
@@ -805,6 +885,9 @@
clearSelection: function() {
this._selected.forEach(function(item) {
item.selected = false;
if (item.treeList.checkbox) {
item.treeList.checkbox.prop('checked',false)
}
if (item.treeList.label) {
item.treeList.label.removeClass("selected")
}

View File

@@ -53,8 +53,88 @@
}
return icon;
}
var autoComplete = function(options) {
return function(val) {
var matches = [];
options.forEach(opt => {
let v = opt.value;
var i = v.toLowerCase().indexOf(val.toLowerCase());
if (i > -1) {
var pre = v.substring(0,i);
var matchedVal = v.substring(i,i+val.length);
var post = v.substring(i+val.length)
var el = $('<div/>',{style:"white-space:nowrap; overflow: hidden; flex-grow:1"});
$('<span/>').text(pre).appendTo(el);
$('<span/>',{style:"font-weight: bold"}).text(matchedVal).appendTo(el);
$('<span/>').text(post).appendTo(el);
var element = $('<div>',{style: "display: flex"});
el.appendTo(element);
if (opt.source) {
$('<div>').css({
"font-size": "0.8em"
}).text(opt.source.join(",")).appendTo(element);
}
matches.push({
value: v,
label: element,
i:i
})
}
})
matches.sort(function(A,B){return A.i-B.i})
return matches;
}
}
// This is a hand-generated list of completions for the core nodes (based on the node help html).
var msgCompletions = [
{ value: "payload" },
{ value: "req", source: ["http in"]},
{ value: "req.body", source: ["http in"]},
{ value: "req.headers", source: ["http in"]},
{ value: "req.query", source: ["http in"]},
{ value: "req.params", source: ["http in"]},
{ value: "req.cookies", source: ["http in"]},
{ value: "req.files", source: ["http in"]},
{ value: "complete", source: ["join"] },
{ value: "contentType", source: ["mqtt"] },
{ value: "cookies", source: ["http in","http request"] },
{ value: "correlationData", source: ["mqtt"] },
{ value: "delay", source: ["delay","trigger"] },
{ value: "encoding", source: ["file"] },
{ value: "error", source: ["catch"] },
{ value: "filename", source: ["file","file in"] },
{ value: "flush", source: ["delay"] },
{ value: "followRedirects", source: ["http request"] },
{ value: "headers", source: ["http in"," http request"] },
{ value: "kill", source: ["exec"] },
{ value: "messageExpiryInterval", source: ["mqtt"] },
{ value: "method", source: ["http-request"] },
{ value: "options", source: ["xml"] },
{ value: "parts", source: ["split","join"] },
{ value: "pid", source: ["exec"] },
{ value: "qos", source: ["mqtt"] },
{ value: "rate", source: ["delay"] },
{ value: "rejectUnauthorized", source: ["http request"] },
{ value: "requestTimeout", source: ["http request"] },
{ value: "reset", source: ["delay","trigger","join","rbe"] },
{ value: "responseTopic", source: ["mqtt"] },
{ value: "restartTimeout", source: ["join"] },
{ value: "retain", source: ["mqtt"] },
{ value: "select", source: ["html"] },
{ value: "statusCode", source: ["http in"] },
{ value: "template", source: ["template"] },
{ value: "toFront", source: ["delay"] },
{ value: "topic", source: ["inject","mqtt","rbe"] },
{ value: "url", source: ["http request"] },
{ value: "userProperties", source: ["mqtt"] }
]
var allOptions = {
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression},
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
flow: {value:"flow",label:"flow.",hasValue:true,
options:[],
validate:RED.utils.validatePropertyExpression,
@@ -265,6 +345,47 @@
}
}
};
// For a type with options, check value is a valid selection
// If !opt.multiple, returns the valid option object
// if opt.multiple, returns an array of valid option objects
// If not valid, returns null;
function isOptionValueValid(opt, currentVal) {
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
if (typeof op === "string" && op === currentVal) {
return {value:currentVal}
} else if (op.value === currentVal) {
return op;
}
}
} else {
// Check to see if value is a valid csv of
// options.
var currentValues = {};
var selected = [];
currentVal.split(",").forEach(function(v) {
if (v) {
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
var val = typeof op === "string" ? op : op.value;
if (currentValues.hasOwnProperty(val)) {
delete currentValues[val];
selected.push(typeof op === "string" ? {value:op} : op.value)
}
}
if (!$.isEmptyObject(currentValues)) {
return null;
}
return selected
}
}
var nlsd = false;
$.widget( "nodered.typedInput", {
@@ -298,7 +419,8 @@
}
nlsd = true;
var that = this;
this.identifier = this.element.attr('id') || "TypedInput-"+Math.floor(Math.random()*100);
if (this.options.debug) { console.log(this.identifier,"Create",{defaultType:this.options.default, value:this.element.val()}) }
this.disarmClick = false;
this.input = $('<input class="red-ui-typedInput-input" type="text"></input>');
this.input.insertAfter(this.element);
@@ -328,6 +450,8 @@
});
this.defaultInputType = this.input.attr('type');
// Used to remember selections per-type to restore them when switching between types
this.oldValues = {};
this.uiSelect.addClass("red-ui-typedInput-container");
@@ -380,6 +504,9 @@
that.element.trigger('paste',evt);
});
this.input.on('keydown', function(evt) {
if (that.typeMap[that.propertyType].autoComplete) {
return
}
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
evt.stopPropagation();
}
@@ -407,9 +534,9 @@
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="red-ui-typedInput-icon fa fa-caret-down"></i></span></button>').appendTo(this.uiSelect);
this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
RED.popover.tooltip(this.optionSelectLabel,function() {
return that.optionValue;
});
// RED.popover.tooltip(this.optionSelectLabel,function() {
// return that.optionValue;
// });
this.optionSelectTrigger.on("click", function(event) {
event.preventDefault();
event.stopPropagation();
@@ -428,7 +555,9 @@
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect);
this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton);
this.type(this.options.default||this.typeList[0].value);
this.type(this.typeField.val() || this.options.default||this.typeList[0].value);
this.typeChanged = !!this.options.default;
}catch(err) {
console.log(err.stack);
}
@@ -579,7 +708,7 @@
var height = relativeTo.height();
var menuHeight = menu.height();
var top = (height+pos.top);
if (top+menuHeight > $(window).height()) {
if (top+menuHeight-$(document).scrollTop() > $(window).height()) {
top -= (top+menuHeight)-$(window).height()+5;
}
if (top < 0) {
@@ -676,6 +805,7 @@
var that = this;
var currentType = this.type();
this.typeMap = {};
var firstCall = (this.typeList === undefined);
this.typeList = types.map(function(opt) {
var result;
if (typeof opt === 'string') {
@@ -688,8 +818,10 @@
});
if (this.typeList.length < 2) {
this.selectTrigger.attr("tabindex", -1)
this.selectTrigger.on("mousedown.red-ui-typedInput-focus-block", function(evt) { evt.preventDefault(); })
} else {
this.selectTrigger.attr("tabindex", 0)
this.selectTrigger.off("mousedown.red-ui-typedInput-focus-block")
}
this.selectTrigger.toggleClass("disabled", this.typeList.length === 1);
this.selectTrigger.find(".fa-caret-down").toggle(this.typeList.length > 1)
@@ -698,10 +830,19 @@
}
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
this.type(this.typeList[0].value);
if (!firstCall) {
this.type(this.typeList[0].value);
}
} else {
this.propertyType = null;
this.type(currentType);
if (!firstCall) {
this.type(currentType);
}
}
if (this.typeList.length === 1 && !this.typeList[0].icon && (!this.typeList[0].label || this.typeList[0].showLabel === false)) {
this.selectTrigger.hide()
} else {
this.selectTrigger.show()
}
},
width: function(desiredWidth) {
@@ -712,7 +853,10 @@
},
value: function(value) {
var that = this;
var opt = this.typeMap[this.propertyType];
// If the default type has been set to an invalid type, then on first
// creation, the current propertyType will not exist. Default to an
// empty object on the assumption the corrent type will be set shortly
var opt = this.typeMap[this.propertyType] || {};
if (!arguments.length) {
var v = this.input.val();
if (opt.export) {
@@ -720,27 +864,38 @@
}
return v;
} else {
if (this.options.debug) { console.log(this.identifier,"----- SET VALUE ------",value) }
var selectedOption = [];
var valueToCheck = value;
if (opt.options) {
var checkValues = [value];
if (opt.hasValue && opt.parse) {
var parts = opt.parse(value);
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
value = parts.value;
valueToCheck = parts.option || parts.value;
}
var checkValues = [valueToCheck];
if (opt.multiple) {
selectedOption = [];
checkValues = value.split(",");
checkValues = valueToCheck.split(",");
}
checkValues.forEach(function(value) {
checkValues.forEach(function(valueToCheck) {
for (var i=0;i<opt.options.length;i++) {
var op = opt.options[i];
if (typeof op === "string") {
if (op === value || op === ""+value) {
if (op === valueToCheck || op === ""+valueToCheck) {
selectedOption.push(that.activeOptions[op]);
break;
}
} else if (op.value === value) {
} else if (op.value === valueToCheck) {
selectedOption.push(op);
break;
}
}
})
if (this.options.debug) { console.log(this.identifier,"set value to",value) }
this.input.val(value);
if (!opt.multiple) {
if (selectedOption.length === 0) {
@@ -765,9 +920,64 @@
return this.propertyType;
} else {
var that = this;
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
var previousValue = null;
var opt = this.typeMap[type];
if (opt && this.propertyType !== type) {
// If previousType is !null, then this is a change of the type, rather than the initialisation
var previousType = this.typeMap[this.propertyType];
previousValue = this.input.val();
if (previousType && this.typeChanged) {
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
if (previousType.options && opt.hasValue !== true) {
this.oldValues[previousType.value] = previousValue;
} else if (previousType.hasValue === false) {
this.oldValues[previousType.value] = previousValue;
} else {
this.oldValues["_"] = previousValue;
}
if ((opt.options && opt.hasValue !== true) || opt.hasValue === false) {
if (this.oldValues.hasOwnProperty(opt.value)) {
if (this.options.debug) { console.log(this.identifier,"restored previous (1)",this.oldValues[opt.value]) }
this.input.val(this.oldValues[opt.value]);
} else if (opt.options) {
// No old value for the option type.
// It is possible code has called 'value' then 'type'
// to set the selected option. This is what the Inject/Switch/Change
// nodes did before 2.1.
// So we need to be careful to not reset the value if it is a valid option.
var validOptions = isOptionValueValid(opt,previousValue);
if (this.options.debug) { console.log(this.identifier,{previousValue,opt,validOptions}) }
if ((previousValue || previousValue === '') && validOptions) {
if (this.options.debug) { console.log(this.identifier,"restored previous (2)") }
this.input.val(previousValue);
} else {
if (typeof opt.default === "string") {
if (this.options.debug) { console.log(this.identifier,"restored previous (3)",opt.default) }
this.input.val(opt.default);
} else if (Array.isArray(opt.default)) {
if (this.options.debug) { console.log(this.identifier,"restored previous (4)",opt.default.join(",")) }
this.input.val(opt.default.join(","))
} else {
if (this.options.debug) { console.log(this.identifier,"restored previous (5)") }
this.input.val("");
}
}
} else {
if (this.options.debug) { console.log(this.identifier,"restored default/blank",opt.default||"") }
this.input.val(opt.default||"")
}
} else {
if (this.options.debug) { console.log(this.identifier,"restored old/default/blank") }
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
}
if (previousType.autoComplete) {
this.input.autoComplete("destroy");
}
}
this.propertyType = type;
this.typeChanged = true;
if (this.typeField) {
this.typeField.val(type);
}
@@ -836,22 +1046,12 @@
var op;
if (!opt.hasValue) {
var validValue = false;
var currentVal = this.input.val();
// Check the value is valid for the available options
var validValues = isOptionValueValid(opt,this.input.val());
if (!opt.multiple) {
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
if (typeof op === "string" && op === currentVal) {
that._updateOptionSelectLabel({value:currentVal});
validValue = true;
break;
} else if (op.value === currentVal) {
that._updateOptionSelectLabel(op);
validValue = true;
break;
}
}
if (!validValue) {
if (validValues) {
that._updateOptionSelectLabel(validValues)
} else {
op = opt.options[0];
if (typeof op === "string") {
this.value(op);
@@ -862,27 +1062,19 @@
}
}
} else {
// Check to see if value is a valid csv of
// options.
var currentValues = {};
currentVal.split(",").forEach(function(v) {
if (v) {
currentValues[v] = true;
}
});
for (var i=0;i<opt.options.length;i++) {
op = opt.options[i];
delete currentValues[op.value||op];
}
if (!$.isEmptyObject(currentValues)) {
// Invalid, set to default/empty
this.value((opt.default||[]).join(","));
if (!validValues) {
validValues = (opt.default || []).map(function(v) {
return typeof v === "string"?v:v.value
});
this.value(validValues.join(","));
}
that._updateOptionSelectLabel(validValues);
}
} else {
var selectedOption = this.optionValue||opt.options[0];
if (opt.parse) {
var parts = opt.parse(this.input.val(),selectedOption);
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
var parts = opt.parse(this.input.val(),selectedOptionObj);
if (parts.option) {
selectedOption = parts.option;
if (!this.activeOptions.hasOwnProperty(selectedOption)) {
@@ -906,6 +1098,7 @@
this._updateOptionSelectLabel(this.activeOptions[selectedOption]);
}
} else if (selectedOption) {
if (this.options.debug) { console.log(this.identifier,"HERE",{optionValue:selectedOption.value}) }
this.optionValue = selectedOption.value;
this._updateOptionSelectLabel(selectedOption);
} else {
@@ -938,8 +1131,6 @@
this.input.attr('type',this.defaultInputType)
}
if (opt.hasValue === false) {
this.oldValue = this.input.val();
this.input.val("");
this.elementDiv.hide();
this.valueLabelContainer.hide();
} else if (opt.valueLabel) {
@@ -952,12 +1143,13 @@
this.elementDiv.hide();
opt.valueLabel.call(this,this.valueLabelContainer,this.input.val());
} else {
if (this.oldValue !== undefined) {
this.input.val(this.oldValue);
delete this.oldValue;
}
this.valueLabelContainer.hide();
this.elementDiv.show();
if (opt.autoComplete) {
this.input.autoComplete({
search: opt.autoComplete
})
}
}
if (this.optionExpandButton) {
if (opt.expand) {
@@ -1042,6 +1234,9 @@
},
disabled: function() {
return this.uiSelect.attr("disabled") === "disabled";
},
focus: function() {
this.input.focus();
}
});
})(jQuery);

View File

@@ -333,6 +333,16 @@ RED.deploy = (function() {
var unknownNodes = [];
var invalidNodes = [];
RED.nodes.eachConfig(function(node) {
if (!node.valid && !node.d) {
invalidNodes.push(getNodeInfo(node));
}
if (node.type === "unknown") {
if (unknownNodes.indexOf(node.name) == -1) {
unknownNodes.push(node.name);
}
}
});
RED.nodes.eachNode(function(node) {
if (!node.valid && !node.d) {
invalidNodes.push(getNodeInfo(node));

View File

@@ -554,6 +554,8 @@ RED.diff = (function() {
color: "#DDAA99",
defaults:{name:{value:""}}
}
} else if (node.type === "group") {
def = RED.group.def;
} else {
def = {};
}
@@ -763,16 +765,15 @@ RED.diff = (function() {
}
}
if (node.hasOwnProperty('x')) {
if (localNode) {
if (localNode.x !== node.x || localNode.y !== node.y) {
if (localNode.x !== node.x || localNode.y !== node.y || localNode.w !== node.w || localNode.h !== node.h ) {
localChanged = true;
localChanges++;
}
}
if (remoteNode) {
if (remoteNode.x !== node.x || remoteNode.y !== node.y) {
if (remoteNode.x !== node.x || remoteNode.y !== node.y|| remoteNode.w !== node.w || remoteNode.h !== node.h) {
remoteChanged = true;
remoteChanges++;
}
@@ -790,7 +791,12 @@ RED.diff = (function() {
localCell.addClass("red-ui-diff-status-"+(localChanged?"changed":"unchanged"));
$('<span class="red-ui-diff-status">'+(localChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(localCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(localCell);
propertyElements['local.position'] = RED.utils.createObjectElement({x:localNode.x,y:localNode.y},
var localPosition = {x:localNode.x,y:localNode.y};
if (localNode.hasOwnProperty('w')) {
localPosition.w = localNode.w;
localPosition.h = localNode.h;
}
propertyElements['local.position'] = RED.utils.createObjectElement(localPosition,
{
path: "position",
exposeApi: true,
@@ -811,7 +817,12 @@ RED.diff = (function() {
if (remoteNode) {
$('<span class="red-ui-diff-status">'+(remoteChanged?'<i class="fa fa-square"></i>':'')+'</span>').appendTo(remoteCell);
element = $('<span class="red-ui-diff-list-element"></span>').appendTo(remoteCell);
propertyElements['remote.position'] = RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y},
var remotePosition = {x:remoteNode.x,y:remoteNode.y};
if (remoteNode.hasOwnProperty('w')) {
remotePosition.w = remoteNode.w;
remotePosition.h = remoteNode.h;
}
propertyElements['remote.position'] = RED.utils.createObjectElement(remotePosition,
{
path: "position",
exposeApi: true,
@@ -883,11 +894,11 @@ RED.diff = (function() {
}
}
}
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
var properties = Object.keys(node).filter(function(p) { return p!='inputLabels'&&p!='outputLabels'&&p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='w'&&p!=='h'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
if (def.defaults) {
properties = properties.concat(Object.keys(def.defaults));
}
if (node.type !== 'tab') {
if (node.type !== 'tab' && node.type !== "group") {
properties = properties.concat(['inputLabels','outputLabels']);
}
if ( ((localNode && localNode.hasOwnProperty('icon')) || (remoteNode && remoteNode.hasOwnProperty('icon'))) &&
@@ -1376,6 +1387,7 @@ RED.diff = (function() {
function mergeDiff(diff) {
//console.log(diff);
var selectedTab = RED.workspaces.active();
var appliedDiff = applyDiff(diff);
var newConfig = appliedDiff.config;
@@ -1426,6 +1438,7 @@ RED.diff = (function() {
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.workspaces.show(selectedTab, true);
RED.sidebar.config.refresh();
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,11 +25,14 @@
function .selection.getRange();
property .session //the editor object
function .session.insert = function(position, text)
function .setReadOnly(readOnly)
property .renderer = {};
function .renderer.updateFull()
function setMode(mode, cb)
function getRange();
function replace(range, text)
function selectAll
function clearSelection
function getSelectedText()
function destroy()
function resize()
@@ -51,6 +54,7 @@ RED.editor.codeEditor.monaco = (function() {
var initialised = false;
const type = "monaco";
const monacoThemes = ["vs","vs-dark","hc-black"]; //TODO: consider setting hc-black autmatically based on acessability?
let userSelectedTheme;
//TODO: get from externalModules.js For now this is enough for feature parity with ACE (and then some).
const knownModules = {
@@ -120,7 +124,7 @@ RED.editor.codeEditor.monaco = (function() {
const def = modulesCache[libPath];
if( def ) {
if(!preloadOnly) {
loadedLibs.JS[libModule] = monaco.languages.typescript.javascriptDefaults.addExtraLib(def, `file://types/${libPackage}/${libModule}/index.d.ts`);
loadedLibs.JS[libModule] = monaco.languages.typescript.javascriptDefaults.addExtraLib(def, "file://types/" + libPackage + "/" + libModule + "/index.d.ts");
}
if(cb) {
setTimeout(function() {
@@ -133,7 +137,7 @@ RED.editor.codeEditor.monaco = (function() {
.done(function(data) {
modulesCache[libPath] = data;
if(!preloadOnly) {
loadedLibs.JS[libModule] = monaco.languages.typescript.javascriptDefaults.addExtraLib(data, `file://types/${libPackage}/${libModule}/index.d.ts`);
loadedLibs.JS[libModule] = monaco.languages.typescript.javascriptDefaults.addExtraLib(data, "file://types/" + libPackage + "/" + libModule + "/index.d.ts");
}
if(cb) { cb(null, _module) }
})
@@ -150,22 +154,9 @@ RED.editor.codeEditor.monaco = (function() {
function init(options) {
//Handles "Uncaught (in promise) Canceled: Canceled"
//@see https://github.com/microsoft/monaco-editor/issues/2382
//This is fixed in commit microsoft/vscode@49cad9a however it is not yet present monaco-editor
//Remove the below addEventListener once monaco-editor V0.23.1 or greater is published
window.addEventListener('unhandledrejection', function(evt) {
if(evt && evt.reason && evt.reason.stack) {
if (evt.reason.name === 'Canceled' && evt.reason.stack.indexOf('vendor/monaco/dist') >= 0) {
evt.preventDefault();
evt.stopImmediatePropagation();
}
}
});
//Handles orphaned models
//Handles orphaned models
//ensure loaded models that are not explicitly destroyed by a call to .destroy() are disposed
RED.events.on("editor:close",function() {
RED.events.on("editor:close",function() {
let models = window.monaco ? monaco.editor.getModels() : null;
if(models && models.length) {
console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().")
@@ -191,19 +182,35 @@ RED.editor.codeEditor.monaco = (function() {
var editorSettings = RED.editor.codeEditor.settings || {};
var editorOptions = editorSettings.options || {};
if (editorOptions.theme) {
if (!monacoThemes.includes(editorOptions.theme)) {
var customTheme = 'vendor/monaco/dist/theme/' + editorOptions.theme + '.json';
$.get(customTheme, function (theme) {
monacoThemes.push(editorOptions.theme);//add to list of loaded themes
if ((theme.rules && Array.isArray(theme.rules)) || theme.colors) {
monaco.editor.defineTheme(editorOptions.theme, theme);
monaco.editor.setTheme(editorOptions.theme);
//if editorOptions.theme is an object (set in theme.js context()), use the plugin theme name as the monaco theme name
//if editorOptions.theme is a string, it should be the name of a pre-set theme, load that
try {
const addTheme = function (themeThemeName, theme) {
if ((theme.rules && Array.isArray(theme.rules)) || theme.colors) {
monacoThemes.push(themeThemeName); //add to list of loaded themes
monaco.editor.defineTheme(themeThemeName, theme);
monaco.editor.setTheme(themeThemeName);
userSelectedTheme = themeThemeName;
}
};
if (editorOptions.theme) {
if (typeof editorOptions.theme == "object" && RED.settings.editorTheme.theme) {
let themeThemeName = editorOptions.theme.name || RED.settings.editorTheme.theme;
addTheme(themeThemeName, editorOptions.theme);
} else if (typeof editorOptions.theme == "string") {
let themeThemeName = editorOptions.theme;
if (!monacoThemes.includes(themeThemeName)) {
$.get('vendor/monaco/dist/theme/' + themeThemeName + '.json', function (theme) {
addTheme(themeThemeName, theme);
});
}
});
}
}
} catch (error) {
console.warn(error);
}
//Helper function to simplify snippet setup
function createMonacoCompletionItem(label, insertText, documentation, range, kind) {
if (Array.isArray(documentation)) { documentation = documentation.join("\n"); }
@@ -563,7 +570,7 @@ RED.editor.codeEditor.monaco = (function() {
createMonacoCompletionItem("node.send", 'node.send(${1:msg});','async send a msg to the next node',range),
createMonacoCompletionItem("node.send (multiple)", 'var ${1:msg1} = {payload:${2:1}};\nvar ${3:msg2} = {payload:${4:2}};\nnode.send([[${1:msg1}, ${3:msg2}]]);','send 1 or more messages out of 1 output',range),
createMonacoCompletionItem("node.send (multiple outputs)", 'var ${1:msg1} = {payload:${2:1}};\nvar ${3:msg2} = {payload:${4:2}};\nnode.send([${1:msg1}, ${3:msg2}]);','send more than 1 message out of multiple outputs',range),
createMonacoCompletionItem("node.status", 'node.status({fill:"${1:red}",shape:"${2:ring}",text:"${3:error}"});','Set the status icon and text underneath the function node',range),
createMonacoCompletionItem("node.status", 'node.status({fill:"${1|red,green,yellow,blue,grey|}",shape:"${2|ring,dot|}",text:"${3:message}"});','Set the status icon and text underneath the function node',range),
createMonacoCompletionItem("get (node context)", 'context.get("${1:name}");','Get a value from node context',range),
createMonacoCompletionItem("set (node context)", 'context.set("${1:name}", ${1:value});','Set a value in node context',range),
createMonacoCompletionItem("get (flow context)", 'flow.get("${1:name}");','Get a value from flow context',range),
@@ -645,6 +652,7 @@ RED.editor.codeEditor.monaco = (function() {
strictPropertyInitialization: true,
strictFunctionTypes: true,
strictBindCallApply: true,
useDefineForClassFields: true,//permit class static fields with private name to have initializer
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
typeRoots: ["types"],
@@ -662,10 +670,13 @@ RED.editor.codeEditor.monaco = (function() {
noSyntaxValidation: false,
diagnosticCodesToIgnore: [
1108, //return not inside function
1375, //'await' expressions are only allowed at the top level of a file when that file is a module
1378, //Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher
//2304, //Cannot find name - this one is heavy handed and prevents user seeing stupid errors. Would provide better ACE feature parity (i.e. no need for declaration of vars) but misses lots of errors. Lets be bold & leave it out!
2307, //Cannot find module 'xxx' or its corresponding type declarations
2322, //Type 'unknown' is not assignable to type 'string'
2339, //property does not exist on
2345, //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions'
7043, //i forget what this one is,
80001, //Convert to ES6 module
80004, //JSDoc types may be moved to TypeScript types.
@@ -750,6 +761,19 @@ RED.editor.codeEditor.monaco = (function() {
var editorOptions = $.extend({}, editorSettings.options, options);
editorOptions.language = convertAceModeToMonacoLang(options.mode);
if(userSelectedTheme) {
editorOptions.theme = userSelectedTheme;//use user selected theme for this session
}
//by default, set javascript editors to text mode.
//when element becomes visible, it will be (re) set to javascript mode
//this is to ensure multiple editors sharing the model dont present its
//consts & lets to each other
if(editorOptions.language == "javascript") {
editorOptions._language = editorOptions.language;
editorOptions.language = "text"
}
//apply defaults
if (!editorOptions.minimap) {
editorOptions.minimap = {
@@ -881,7 +905,8 @@ RED.editor.codeEditor.monaco = (function() {
if(!extraModuleLibs || extraModuleLibs.length == 0) {
loadedLibs.JS[id] = monaco.languages.typescript.javascriptDefaults.addExtraLib(" ", file);
} else {
var loadList = [...extraModuleLibs];
var loadList = [];
Array.prototype.push.apply(loadList, extraModuleLibs);//Use this instead of spread operator to prevent IE syntax error
var loadExtraModules = {};
for (let index = 0; index < extraModuleLibs.length; index++) {
const lib = extraModuleLibs[index];
@@ -919,6 +944,15 @@ RED.editor.codeEditor.monaco = (function() {
/*********** Create the monaco editor ***************/
var ed = monaco.editor.create(el, editorOptions);
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
try {
ed._standaloneKeybindingService.addDynamicKeybinding(
'-editor.action.insertLineAfter', // command ID prefixed by '-'
null, // keybinding
() => {} // need to pass an empty handler
);
} catch (error) { }
ed.nodered = {
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
}
@@ -948,7 +982,9 @@ RED.editor.codeEditor.monaco = (function() {
var oldSelections = ed.getSelections();
var oldPosition = ed.getPosition();
oldValue = oldModel.getValue() || "";
if(!oldModel.isDisposed()) { oldModel.dispose(); }
try {
if(!oldModel.isDisposed()) { oldModel.dispose(); }
} catch (error) { }
ed.setModel(null);
newModel = monaco.editor.createModel((oldValue || ""), mode);
ed.setModel(newModel);
@@ -963,7 +999,7 @@ RED.editor.codeEditor.monaco = (function() {
if (cb && typeof cb == "function") {
cb();
}
if(resize) {
if(resize) {
this.resize(); //cause a call to layout()
}
}
@@ -990,6 +1026,10 @@ RED.editor.codeEditor.monaco = (function() {
_executeEdits(this,[op]);
}
ed.setReadOnly = function setReadOnly(readOnly) {
ed.updateOptions({ readOnly: readOnly })
}
ed.session.replace = function replace(range, text) {
var op = { range: range, text: text, forceMoveMarkers: true };
_executeEdits(this,[op]);
@@ -1006,11 +1046,20 @@ RED.editor.codeEditor.monaco = (function() {
}
}
ed.selectAll = function selectAll() {
const range = ed.getModel().getFullModelRange();
ed.setSelection(range);
}
ed.clearSelection = function clearSelection() {
ed.setPosition({column:1,lineNumber:1});
}
ed.getSelectedText = function getSelectedText() {
return ed.getModel().getValueInRange(ed.getSelection());
}
ed.insertSnippet = function editer_insertSnippet(s) {
ed.insertSnippet = function insertSnippet(s) {
//https://github.com/microsoft/monaco-editor/issues/1112#issuecomment-429580604
//no great way of triggering snippets!
let contribution = ed.getContribution("snippetController2");
@@ -1141,7 +1190,10 @@ RED.editor.codeEditor.monaco = (function() {
return { row: p.lineNumber-1, column: p.column-1 };
}
ed.setTheme = monaco.editor.setTheme;
ed.setTheme = function(theme) {
monaco.editor.setTheme(theme);
userSelectedTheme = theme;//remember users choice for this session
}
ed.on = function (name, cb) {
switch (name) {
@@ -1201,33 +1253,37 @@ RED.editor.codeEditor.monaco = (function() {
}
ed._mode = editorOptions.language;
//as models are signleton, consts and let are avialable to other javascript instances
//as models are signleton, consts and let are avialable to other javascript instances
//so when not focused, set editor mode to text temporarily to avoid multiple defs
if(editorOptions._language) {
ed._mode = editorOptions._language;
ed._tempMode = editorOptions.language;
}
ed.onDidBlurEditorWidget(function() {
if(isVisible(el) == false) {
onVisibilityChange(false);
onVisibilityChange(false, 0, el);
}
});
ed.onDidFocusEditorWidget(function() {
onVisibilityChange(true, 10);
onVisibilityChange(true, 10, el);
});
function visibilityWatcher(element, callback) {
try {
var options = {
root: $(element).closest("div.red-ui-tray-content")[0] || document,
attributes: true,
childList: true,
attributes: true,
childList: true,
};
var observer = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
callback(entry.intersectionRatio > 0, 5, entry);
callback(entry.intersectionRatio > 0, 5, entry.target);
});
}, options);
observer.observe(element);
observer.observe(element);
} catch (e1) {
//browser not supporting IntersectionObserver? then fall back to polling!
try {
@@ -1235,7 +1291,7 @@ RED.editor.codeEditor.monaco = (function() {
watchTimer = setInterval(function() {
let elVisible = isVisible(el);
if(elVisible != elVisibleMem) {
callback(elVisible, 100, element);
callback(elVisible, 5, element);
}
elVisibleMem = elVisible;
}, 100);
@@ -1246,31 +1302,23 @@ RED.editor.codeEditor.monaco = (function() {
function onVisibilityChange(visible, delay, element) {
if(visible) {
if(ed._mode == "javascript" && ed._tempMode == "text") {
ed._tempMode = "";
ed._tempMode = "";
setTimeout(function() {
if(el.parentElement) { //ensure el is still in DOM
if(element.parentElement) { //ensure el is still in DOM
ed.setMode('javascript', undefined, false);
}
}, delay);
}, delay || 50);
}
} else if(ed._mode == "javascript") {
if(el.parentElement) { //ensure el is still in DOM
} else if(ed._mode == "javascript" && ed._tempMode != "text") {
if(element.parentElement) { //ensure el is still in DOM
ed.setMode('text', undefined, false);
ed._tempMode = "text";
ed._tempMode = "text";
}
}
}
visibilityWatcher(el, onVisibilityChange);
//by default, set javascript editors to text mode.
//when elemt becomes visible, it will be (re) set to javascript mode
//this is to ensure multiple editors sharing the model dont presnet its
//consts & lets to each other
if(ed._mode == "javascript") {
ed.setMode('text', undefined, false);
ed._tempMode = "text";
}
if (editorOptions.language === 'markdown') {
$(el).addClass("red-ui-editor-text-container-toolbar");
@@ -1340,4 +1388,4 @@ RED.editor.codeEditor.monaco = (function() {
*/
create: create
}
})();
})();

View File

@@ -1,4 +1,4 @@
RED.colorPicker = (function() {
RED.editor.colorPicker = RED.colorPicker = (function() {
function create(options) {
var color = options.value;

View File

@@ -0,0 +1,616 @@
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'];
/**
* Create env var edit interface
* @param container - container
* @param node - subflow node
*/
function buildPropertiesList(envContainer, node) {
var isTemplateNode = (node.type === "subflow");
envContainer
.css({
'min-height':'150px',
'min-width':'450px'
})
.editableList({
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
addItem: function(container, i, opt) {
// If this is an instance node, these are properties unique to
// this instance - ie opt.parent will not be defined.
if (isTemplateNode) {
container.addClass("red-ui-editor-subflow-env-editable")
}
var envRow = $('<div/>').appendTo(container);
var nameField = null;
var valueField = null;
nameField = $('<input/>', {
class: "node-input-env-name",
type: "text",
placeholder: RED._("common.label.name")
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
valueField = $('<input/>',{
style: "width:100%",
class: "node-input-env-value",
type: "text",
}).attr("autocomplete","disable").appendTo(envRow)
valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
valueField.typedInput('type', opt.type);
if (opt.type === "cred") {
if (opt.value) {
valueField.typedInput('value', opt.value);
} else if (node.credentials && node.credentials[opt.name]) {
valueField.typedInput('value', node.credentials[opt.name]);
} else if (node.credentials && node.credentials['has_'+opt.name]) {
valueField.typedInput('value', "__PWRD__");
} else {
valueField.typedInput('value', "");
}
} else {
valueField.typedInput('value', opt.value);
}
opt.nameField = nameField;
opt.valueField = valueField;
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
actionButton.on("click", function(evt) {
evt.preventDefault();
removeTip.close();
container.parent().addClass("red-ui-editableList-item-deleting")
container.fadeOut(300, function() {
envContainer.editableList('removeItem',opt);
});
});
if (isTemplateNode) {
// Add the UI customisation row
// if `opt.ui` does not exist, then apply defaults. If these
// defaults do not change then they will get stripped off
// before saving.
if (opt.type === 'cred') {
opt.ui = opt.ui || {
icon: "",
type: "cred"
}
opt.ui.type = "cred";
} else {
opt.ui = opt.ui || {
icon: "",
type: "input",
opts: {types:DEFAULT_ENV_TYPE_LIST}
}
}
opt.ui.label = opt.ui.label || {};
opt.ui.type = opt.ui.type || "input";
var uiRow = $('<div/>').appendTo(container).hide();
// save current info for reverting on cancel
// var copy = $.extend(true, {}, ui);
$('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) {
evt.preventDefault();
if ($(this).hasClass('expanded')) {
uiRow.slideUp();
$(this).removeClass('expanded');
} else {
uiRow.slideDown();
$(this).addClass('expanded');
}
});
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
nameField.trigger('change');
}
},
sortable: ".red-ui-editableList-item-handle",
removable: false
});
var parentEnv = {};
var envList = [];
if (/^subflow:/.test(node.type)) {
var subflowDef = RED.nodes.subflow(node.type.substring(8));
if (subflowDef.env) {
subflowDef.env.forEach(function(env) {
var item = {
name:env.name,
parent: {
type: env.type,
value: env.value,
ui: env.ui
}
}
envList.push(item);
parentEnv[env.name] = item;
})
}
}
if (node.env) {
for (var i = 0; i < node.env.length; i++) {
var env = node.env[i];
if (parentEnv.hasOwnProperty(env.name)) {
parentEnv[env.name].type = env.type;
parentEnv[env.name].value = env.value;
} else {
envList.push({
name: env.name,
type: env.type,
value: env.value,
ui: env.ui
});
}
}
}
envList.forEach(function(env) {
if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') {
return;
}
if (!isTemplateNode && env.parent) {
return;
}
envContainer.editableList('addItem', JSON.parse(JSON.stringify(env)));
});
}
/**
* Create UI edit interface for environment variable
* @param container - container
* @param env - env var definition
* @param nameField - name field of env var
* @param valueField - value field of env var
*/
function buildEnvEditRow(container, ui, nameField, valueField) {
container.addClass("red-ui-editor-subflow-env-ui-row")
var topRow = $('<div></div>').appendTo(container);
$('<div></div>').appendTo(topRow);
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
$('<div>').text(RED._("editor.label")).appendTo(topRow);
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
var row = $('<div></div>').appendTo(container);
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
var typeOptions = {
'input': {types:DEFAULT_ENV_TYPE_LIST},
'select': {opts:[]},
'spinner': {},
'cred': {}
};
if (ui.opts) {
typeOptions[ui.type] = ui.opts;
} else {
// Pick up the default values if not otherwise provided
ui.opts = typeOptions[ui.type];
}
var iconCell = $('<div></div>').appendTo(row);
var iconButton = $('<a href="#"></a>').appendTo(iconCell);
iconButton.on("click", function(evt) {
evt.preventDefault();
var icon = ui.icon || "";
var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) {
iconButton.empty();
var path = newIcon || "";
var newPath = RED.utils.separateIconPath(path);
if (newPath) {
$('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
}
ui.icon = path;
});
})
if (ui.icon) {
var newPath = RED.utils.separateIconPath(ui.icon);
$('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
}
var labelCell = $('<div></div>').appendTo(row);
var label = ui.label && ui.label[currentLocale] || "";
var labelInput = $('<input type="text">').val(label).appendTo(labelCell);
ui.labelField = labelInput;
labelInput.on('change', function(evt) {
ui.label = ui.label || {};
var val = $(this).val().trim();
if (val === "") {
delete ui.label[currentLocale];
} else {
ui.label[currentLocale] = val;
}
})
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell);
RED.popover.tooltip(labelIcon,function() {
var langs = Object.keys(ui.label);
var content = $("<div>");
if (langs.indexOf(currentLocale) === -1) {
langs.push(currentLocale);
langs.sort();
}
langs.forEach(function(l) {
var row = $('<div>').appendTo(content);
$('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row);
$('<span>').text(ui.label[l]||"").appendTo(row);
});
return content;
})
nameField.on('change',function(evt) {
labelInput.attr("placeholder",$(this).val())
});
var inputCell = $('<div></div>').appendTo(row);
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
if (ui.type === "input") {
inputCellInput.val(ui.opts.types.join(","));
}
var checkbox;
var selectBox;
inputCellInput.typedInput({
types: [
{
value:"input",
label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
{value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"},
{value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"},
{value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"},
{value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"},
{value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"},
{value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"},
{value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"}
],
default: DEFAULT_ENV_TYPE_LIST,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
if (value.length) {
value.forEach(function(v) {
if (!/^fa /.test(v.icon)) {
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
} else {
var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
$("<i>",{class: v.icon}).appendTo(s);
}
})
} else {
$('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input);
}
}
},
{
value: "cred",
label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({
"border-top-right-radius": "4px",
"border-bottom-right-radius": "4px"
}).appendTo(container);
$('<div class="placeholder-input">').html("&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;").appendTo(innerContainer);
}
},
{
value:"select",
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false,
valueLabel: function(container,value) {
container.css("padding","0");
selectBox = $('<select></select>').appendTo(container);
if (ui.opts && Array.isArray(ui.opts.opts)) {
ui.opts.opts.forEach(function(o) {
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
$('<option>').val(o.v).text(label).appendTo(selectBox);
})
}
selectBox.on('change', function(evt) {
var v = selectBox.val();
// var parts = v.split(":");
// var t = parts.shift();
// v = parts.join(":");
//
// valueField.typedInput("type",'str')
valueField.typedInput("value",v)
});
selectBox.val(valueField.typedInput("value"));
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
},
expand: {
icon: "fa-caret-down",
minWidth: 400,
content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
var optList = $('<ol>').appendTo(content).editableList({
header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"),
addItem: function(row,index,itemData) {
var labelDiv = $('<div>').appendTo(row);
var label = lookupLabel(itemData.l, "", currentLocale);
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
itemData.label.on('keydown', function(evt) {
if (evt.keyCode === 13) {
itemData.input.focus();
evt.preventDefault();
}
});
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
RED.popover.tooltip(labelIcon,function() {
return currentLocale;
})
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
// Problem using a TI here:
// - this is in a popout panel
// - clicking the expand button in the TI will close the parent edit tray
// and open the type editor.
// - but it leaves the popout panel over the top.
// - there is no way to get back to the popout panel after closing the type editor
//.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST});
itemData.input.on('keydown', function(evt) {
if (evt.keyCode === 13) {
// Enter or Tab
var index = optList.editableList('indexOf',itemData);
var length = optList.editableList('length');
if (index + 1 === length) {
var newItem = {};
optList.editableList('addItem',newItem);
setTimeout(function() {
if (newItem.label) {
newItem.label.focus();
}
},100)
} else {
var nextItem = optList.editableList('getItemAt',index+1);
if (nextItem.label) {
nextItem.label.focus()
}
}
evt.preventDefault();
}
});
},
sortable: true,
removable: true,
height: 160
})
if (ui.opts.opts.length > 0) {
ui.opts.opts.forEach(function(o) {
optList.editableList('addItem',$.extend(true,{},o))
})
} else {
optList.editableList('addItem',{})
}
return {
onclose: function() {
var items = optList.editableList('items');
var vals = [];
items.each(function (i,el) {
var data = el.data('data');
var l = data.label.val().trim();
var v = data.input.val();
// var t = data.input.typedInput('type');
// var v = data.input.typedInput('value');
if (l.length > 0) {
data.l = data.l || {};
data.l[currentLocale] = l;
}
data.v = v;
if (l.length > 0 || v.length > 0) {
var val = {l:data.l,v:data.v};
// if (t !== 'str') {
// val.t = t;
// }
vals.push(val);
}
});
ui.opts.opts = vals;
inputCellInput.typedInput('value',Date.now())
}
}
}
}
},
{
value:"checkbox",
label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false,
valueLabel: function(container,value) {
container.css("padding",0);
checkbox = $('<input type="checkbox">').appendTo(container);
checkbox.on('change', function(evt) {
valueField.typedInput('value',$(this).prop('checked')?"true":"false");
})
checkbox.prop('checked',valueField.typedInput('value')==="true");
}
},
{
value:"spinner",
label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
var min = ui.opts && ui.opts.min;
var max = ui.opts && ui.opts.max;
var label = "";
if (min !== undefined && max !== undefined) {
label = Math.min(min,max)+" - "+Math.max(min,max);
} else if (min !== undefined) {
label = "> "+min;
} else if (max !== undefined) {
label = "< "+max;
}
$('<span>').css("margin-left","15px").text(label).appendTo(input);
},
expand: {
icon: "fa-caret-down",
content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
content.css("padding","8px 5px")
var min = ui.opts.min;
var max = ui.opts.max;
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
minInput.val(min);
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
maxInput.val(max);
$('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content);
$('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content);
return {
onclose: function() {
var min = minInput.val().trim();
var max = maxInput.val().trim();
if (min !== "") {
ui.opts.min = parseInt(min);
} else {
delete ui.opts.min;
}
if (max !== "") {
ui.opts.max = parseInt(max);
} else {
delete ui.opts.max;
}
inputCellInput.typedInput('value',Date.now())
}
}
}
}
},
{
value:"none",
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
},
{
value:"hide",
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
}
],
default: 'none'
}).on("typedinputtypechange", function(evt,type) {
ui.type = $(this).typedInput("type");
ui.opts = typeOptions[ui.type];
if (ui.type === 'input') {
// In the case of 'input' type, the typedInput uses the multiple-option
// mode. Its value needs to be set to a comma-separately list of the
// selected options.
inputCellInput.typedInput('value',ui.opts.types.join(","))
} else {
// No other type cares about `value`, but doing this will
// force a refresh of the label now that `ui.opts` has
// been updated.
inputCellInput.typedInput('value',Date.now())
}
switch (ui.type) {
case 'input':
valueField.typedInput('types',ui.opts.types);
break;
case 'select':
valueField.typedInput('types',['str']);
break;
case 'checkbox':
valueField.typedInput('types',['bool']);
break;
case 'spinner':
valueField.typedInput('types',['num']);
break;
case 'cred':
valueField.typedInput('types',['cred']);
break;
default:
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
}
if (ui.type === 'checkbox') {
valueField.typedInput('type','bool');
} else if (ui.type === 'spinner') {
valueField.typedInput('type','num');
}
if (ui.type !== 'checkbox') {
checkbox = null;
}
}).on("change", function(evt,type) {
if (ui.type === 'input') {
var types = inputCellInput.typedInput('value');
ui.opts.types = (types === "") ? ["str"] : types.split(",");
valueField.typedInput('types',ui.opts.types);
}
});
valueField.on("change", function(evt) {
if (checkbox) {
checkbox.prop('checked',$(this).typedInput('value')==="true")
}
})
// Set the input to the right type. This will trigger the 'typedinputtypechange'
// event handler (just above ^^) to update the value if needed
inputCellInput.typedInput('type',ui.type)
}
function setLocale(l, list) {
currentLocale = l;
if (list) {
var items = list.editableList("items");
items.each(function (i, item) {
var entry = $(this).data('data');
var labelField = entry.ui.labelField;
labelField.val(lookupLabel(entry.ui.label, "", currentLocale));
if (labelField.timeout) {
clearTimeout(labelField.timeout);
delete labelField.timeout;
}
labelField.addClass("input-updated");
labelField.timeout = setTimeout(function() {
delete labelField.timeout
labelField.removeClass("input-updated");
},3000);
});
}
}
/**
* Lookup text for specific locale
* @param labels - dict of labels
* @param defaultLabel - fallback label if not found
* @param locale - target locale
* @returns {string} text for specified locale
*/
function lookupLabel(labels, defaultLabel, locale) {
if (labels) {
if (labels[locale]) {
return labels[locale];
}
if (locale) {
var lang = locale.substring(0, 2);
if (labels[lang]) {
return labels[lang];
}
}
}
return defaultLabel;
}
return {
create: buildPropertiesList,
setLocale: setLocale,
lookupLabel: lookupLabel,
DEFAULT_ENV_TYPE_LIST: DEFAULT_ENV_TYPE_LIST,
DEFAULT_ENV_TYPE_LIST_INC_CRED: DEFAULT_ENV_TYPE_LIST_INC_CRED
}
})();

View File

@@ -0,0 +1,99 @@
RED.editor.iconPicker = (function() {
function showIconPicker(container, backgroundColor, iconPath, faOnly, done) {
var picker = $('<div class="red-ui-icon-picker">');
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(picker);
searchInput = $('<input type="text">').attr("placeholder",RED._("editor.searchIcons")).appendTo(searchDiv).searchBox({
delay: 50,
change: function() {
var searchTerm = $(this).val().trim();
if (searchTerm === "") {
iconList.find(".red-ui-icon-list-module").show();
iconList.find(".red-ui-icon-list-icon").show();
} else {
iconList.find(".red-ui-icon-list-module").hide();
iconList.find(".red-ui-icon-list-icon").each(function(i,n) {
if ($(n).data('icon').indexOf(searchTerm) === -1) {
$(n).hide();
} else {
$(n).show();
}
});
}
}
});
var row = $('<div>').appendTo(picker);
var iconList = $('<div class="red-ui-icon-list">').appendTo(picker);
var metaRow = $('<div class="red-ui-icon-meta"></div>').appendTo(picker);
var summary = $('<span>').appendTo(metaRow);
var resetButton = $('<button type="button" class="red-ui-button red-ui-button-small">'+RED._("editor.useDefault")+'</button>').appendTo(metaRow).on("click", function(e) {
e.preventDefault();
iconPanel.hide();
done(null);
});
if (!backgroundColor && faOnly) {
iconList.addClass("red-ui-icon-list-dark");
}
setTimeout(function() {
var iconSets = RED.nodes.getIconSets();
Object.keys(iconSets).forEach(function(moduleName) {
if (faOnly && (moduleName !== "font-awesome")) {
return;
}
var icons = iconSets[moduleName];
if (icons.length > 0) {
// selectIconModule.append($("<option></option>").val(moduleName).text(moduleName));
var header = $('<div class="red-ui-icon-list-module"></div>').text(moduleName).appendTo(iconList);
$('<i class="fa fa-cube"></i>').prependTo(header);
icons.forEach(function(icon) {
var iconDiv = $('<div>',{class:"red-ui-icon-list-icon"}).appendTo(iconList);
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconDiv);
var icon_url = RED.settings.apiRootUrl+"icons/"+moduleName+"/"+icon;
iconDiv.data('icon',icon_url);
if (backgroundColor) {
nodeDiv.css({
'backgroundColor': backgroundColor
});
var borderColor = RED.utils.getDarkerColor(backgroundColor);
if (borderColor !== backgroundColor) {
nodeDiv.css('border-color',borderColor)
}
}
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
if (iconPath.module === moduleName && iconPath.file === icon) {
iconDiv.addClass("selected");
}
iconDiv.on("mouseover", function() {
summary.text(icon);
})
iconDiv.on("mouseout", function() {
summary.html("&nbsp;");
})
iconDiv.on("click", function() {
iconPanel.hide();
done(moduleName+"/"+icon);
})
})
}
});
setTimeout(function() {
spinner.remove();
},50);
},300);
var spinner = RED.utils.addSpinnerOverlay(iconList,true);
var iconPanel = RED.popover.panel(picker);
iconPanel.show({
target: container
})
picker.slideDown(100);
searchInput.trigger("focus");
}
return {
show: showIconPicker
}
})();

View File

@@ -81,7 +81,8 @@
clearTimeout: true,
setInterval: true,
clearInterval: true
}
},
extraLibs: options.extraLibs
});
if (options.cursor) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);

View File

@@ -0,0 +1,517 @@
;(function() {
RED.editor.registerEditPane("editor-tab-appearance", function(node) {
return {
label: RED._("editor-tab.appearance"),
name: RED._("editor-tab.appearance"),
iconClass: "fa fa-object-group",
create: function(container) {
this.content = container;
buildAppearanceForm(this.content,node);
if (node.type === 'subflow') {
this.defaultIcon = "node-red/subflow.svg";
} else {
var iconPath = RED.utils.getDefaultNodeIcon(node._def,node);
this.defaultIcon = iconPath.module+"/"+iconPath.file;
if (node.icon && node.icon !== this.defaultIcon) {
this.isDefaultIcon = false;
} else {
this.isDefaultIcon = true;
}
}
},
resize: function(size) {
},
close: function() {
},
show: function() {
refreshLabelForm(this.content, node);
},
apply: function(editState) {
if (updateLabels(node, editState.changes, editState.outputMap)) {
editState.changed = true;
}
if (!node._def.defaults || !node._def.defaults.hasOwnProperty("icon")) {
var icon = $("#red-ui-editor-node-icon").val()||""
if (!this.isDefaultIcon) {
if (icon !== node.icon) {
editState.changes.icon = node.icon;
node.icon = icon;
editState.changed = true;
}
} else {
if (icon !== "" && icon !== this.defaultIcon) {
editState.changes.icon = node.icon;
node.icon = icon;
editState.changed = true;
} else {
var iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
var currentDefaultIcon = iconPath.module+"/"+iconPath.file;
if (this.defaultIcon !== currentDefaultIcon) {
editState.changes.icon = node.icon;
node.icon = currentDefaultIcon;
editState.changed = true;
}
}
}
}
if (node.type === "subflow") {
var newCategory = $("#subflow-appearance-input-category").val().trim();
if (newCategory === "_custom_") {
newCategory = $("#subflow-appearance-input-custom-category").val().trim();
if (newCategory === "") {
newCategory = node.category;
}
}
if (newCategory === 'subflows') {
newCategory = '';
}
if (newCategory != node.category) {
editState.changes['category'] = node.category;
node.category = newCategory;
editState.changed = true;
}
var oldColor = node.color;
var newColor = $("#red-ui-editor-node-color").val();
if (oldColor !== newColor) {
editState.changes.color = node.color;
node.color = newColor;
editState.changed = true;
RED.utils.clearNodeColorCache();
if (node.type === "subflow") {
var nodeDefinition = RED.nodes.getType(
"subflow:" + node.id
);
nodeDefinition["color"] = newColor;
}
}
}
var showLabel = node._def.hasOwnProperty("showLabel")?node._def.showLabel:true;
if (!$("#node-input-show-label").prop('checked')) {
// Not checked - hide label
if (showLabel) {
// Default to show label
if (node.l !== false) {
editState.changes.l = node.l
editState.changed = true;
}
node.l = false;
} else {
// Node has showLabel:false (eg link nodes)
if (node.hasOwnProperty('l') && node.l) {
editState.changes.l = node.l
editState.changed = true;
}
delete node.l;
}
} else {
// Checked - show label
if (showLabel) {
// Default to show label
if (node.hasOwnProperty('l') && !node.l) {
editState.changes.l = node.l
editState.changed = true;
}
delete node.l;
} else {
if (!node.l) {
editState.changes.l = node.l
editState.changed = true;
}
node.l = true;
}
}
}
}
});
function buildAppearanceForm(container,node) {
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
var i,row;
if (node.type === "subflow") {
var categoryRow = $("<div/>", {
class: "form-row"
}).appendTo(dialogForm);
$("<label/>", {
for: "subflow-appearance-input-category",
"data-i18n": "editor:subflow.category"
}).appendTo(categoryRow);
var categorySelector = $("<select/>", {
id: "subflow-appearance-input-category"
}).css({
width: "250px"
}).appendTo(categoryRow);
$("<input/>", {
type: "text",
id: "subflow-appearance-input-custom-category"
}).css({
display: "none",
"margin-left": "10px",
width: "calc(100% - 250px)"
}).appendTo(categoryRow);
var categories = RED.palette.getCategories();
categories.sort(function(A,B) {
return A.label.localeCompare(B.label);
})
categories.forEach(function(cat) {
categorySelector.append($("<option/>").val(cat.id).text(cat.label));
})
categorySelector.append($("<option/>").attr('disabled',true).text("---"));
categorySelector.append($("<option/>").val("_custom_").text(RED._("palette.addCategory")));
$("#subflow-appearance-input-category").on("change", function() {
var val = $(this).val();
if (val === "_custom_") {
$("#subflow-appearance-input-category").width(120);
$("#subflow-appearance-input-custom-category").show();
} else {
$("#subflow-appearance-input-category").width(250);
$("#subflow-appearance-input-custom-category").hide();
}
})
$("#subflow-appearance-input-category").val(node.category||"subflows");
var userCount = 0;
var subflowType = "subflow:"+node.id;
// RED.nodes.eachNode(function(n) {
// if (n.type === subflowType) {
// userCount++;
// }
// });
$("#red-ui-editor-subflow-user-count")
.text(RED._("subflow.subflowInstances", {count:node.instances.length})).show();
}
$('<div class="form-row">'+
'<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+
'<span style="margin-right: 2px;"/>'+
'<input type="checkbox" id="node-input-show-label"/>'+
'</div>').appendTo(dialogForm);
$("#node-input-show-label").toggleButton({
enabledLabel: RED._("editor.show"),
disabledLabel: RED._("editor.hide")
})
if (!node.hasOwnProperty("l")) {
// Show label unless def.showLabel set to false
node.l = node._def.hasOwnProperty("showLabel")?node._def.showLabel:true;
}
$("#node-input-show-label").prop("checked",node.l).trigger("change");
if (node.type === "subflow") {
// subflow template can select its color
var color = node.color ? node.color : "#DDAA99";
var colorRow = $("<div/>", {
class: "form-row"
}).appendTo(dialogForm);
$("<label/>").text(RED._("editor.color")).appendTo(colorRow);
var recommendedColors = [
"#DDAA99",
"#3FADB5", "#87A980", "#A6BBCF",
"#AAAA66", "#C0C0C0", "#C0DEED",
"#C7E9C0", "#D7D7A0", "#D8BFD8",
"#DAC4B4", "#DEB887", "#DEBD5C",
"#E2D96E", "#E6E0F8", "#E7E7AE",
"#E9967A", "#F3B567", "#FDD0A2",
"#FDF0C2", "#FFAAAA", "#FFCC66",
"#FFF0F0", "#FFFFFF"
]
RED.editor.colorPicker.create({
id: "red-ui-editor-node-color",
value: color,
palette: recommendedColors,
sortPalette: function (a, b) {return a.l - b.l;}
}).appendTo(colorRow);
$("#red-ui-editor-node-color").on('change', function(ev) {
// Horribly out of scope...
var colour = $(this).val();
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
})
}
// If a node has icon property in defaults, the icon of the node cannot be modified. (e.g, ui_button node in dashboard)
if ((!node._def.defaults || !node._def.defaults.hasOwnProperty("icon"))) {
var iconRow = $('<div class="form-row"></div>').appendTo(dialogForm);
$('<label data-i18n="editor.settingIcon">').appendTo(iconRow);
var iconButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(iconRow);
$('<i class="fa fa-caret-down"></i>').appendTo(iconButton);
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(iconButton);
var colour = RED.utils.getNodeColor(node.type, node._def);
var icon_url = RED.utils.getNodeIcon(node._def,node);
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
iconButton.on("click", function(e) {
e.preventDefault();
var iconPath;
var icon = $("#red-ui-editor-node-icon").val()||"";
if (icon) {
iconPath = RED.utils.separateIconPath(icon);
} else {
iconPath = RED.utils.getDefaultNodeIcon(node._def, node);
}
var backgroundColor = RED.utils.getNodeColor(node.type, node._def);
if (node.type === "subflow") {
backgroundColor = $("#red-ui-editor-node-color").val();
}
RED.editor.iconPicker.show(iconButton,backgroundColor,iconPath,false,function(newIcon) {
$("#red-ui-editor-node-icon").val(newIcon||"");
var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon});
RED.utils.createIconElement(icon_url, iconContainer, true);
});
});
RED.popover.tooltip(iconButton, function() {
return $("#red-ui-editor-node-icon").val() || RED._("editor.default");
})
$('<input type="hidden" id="red-ui-editor-node-icon">').val(node.icon).appendTo(iconRow);
}
$('<div class="form-row"><span data-i18n="editor.portLabels"></span></div>').appendTo(dialogForm);
var inputCount = node.inputs || node._def.inputs || 0;
var outputCount = node.outputs || node._def.outputs || 0;
if (node.type === 'subflow') {
inputCount = node.in.length;
outputCount = node.out.length;
}
var inputLabels = node.inputLabels || [];
var outputLabels = node.outputLabels || [];
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelInputs"></span><div id="red-ui-editor-node-label-form-inputs"></div></div>').appendTo(dialogForm);
var inputsDiv = $("#red-ui-editor-node-label-form-inputs");
if (inputCount > 0) {
for (i=0;i<inputCount;i++) {
buildLabelRow("input",i,inputLabels[i],inputPlaceholder).appendTo(inputsDiv);
}
} else {
buildLabelRow().appendTo(inputsDiv);
}
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelOutputs"></span><div id="red-ui-editor-node-label-form-outputs"></div></div>').appendTo(dialogForm);
var outputsDiv = $("#red-ui-editor-node-label-form-outputs");
if (outputCount > 0) {
for (i=0;i<outputCount;i++) {
buildLabelRow("output",i,outputLabels[i],outputPlaceholder).appendTo(outputsDiv);
}
} else {
buildLabelRow().appendTo(outputsDiv);
}
}
function refreshLabelForm(container,node) {
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
var inputsDiv = $("#red-ui-editor-node-label-form-inputs");
var outputsDiv = $("#red-ui-editor-node-label-form-outputs");
var inputCount;
var formInputs = $("#node-input-inputs").val();
if (formInputs === undefined) {
if (node.type === 'subflow') {
inputCount = node.in.length;
} else {
inputCount = node.inputs || node._def.inputs || 0;
}
} else {
inputCount = Math.min(1,Math.max(0,parseInt(formInputs)));
if (isNaN(inputCount)) {
inputCount = 0;
}
}
var children = inputsDiv.children();
var childCount = children.length;
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
childCount--;
}
if (childCount < inputCount) {
if (childCount === 0) {
// remove the 'none' placeholder
$(children[0]).remove();
}
for (i = childCount;i<inputCount;i++) {
buildLabelRow("input",i,"",inputPlaceholder).appendTo(inputsDiv);
}
} else if (childCount > inputCount) {
for (i=inputCount;i<childCount;i++) {
$(children[i]).remove();
}
if (inputCount === 0) {
buildLabelRow().appendTo(inputsDiv);
}
}
var outputCount;
var i;
var formOutputs = $("#node-input-outputs").val();
if (formOutputs === undefined) {
if (node.type === 'subflow') {
outputCount = node.out.length;
} else {
inputCount = node.outputs || node._def.outputs || 0;
}
} else if (isNaN(formOutputs)) {
var outputMap = JSON.parse(formOutputs);
var keys = Object.keys(outputMap);
children = outputsDiv.children();
childCount = children.length;
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
childCount--;
}
outputCount = 0;
var rows = [];
keys.forEach(function(p) {
var row = $("#red-ui-editor-node-label-form-output-"+p).parent();
if (row.length === 0 && outputMap[p] !== -1) {
if (childCount === 0) {
$(children[0]).remove();
childCount = -1;
}
row = buildLabelRow("output",p,"",outputPlaceholder);
} else {
row.detach();
}
if (outputMap[p] !== -1) {
outputCount++;
rows.push({i:parseInt(outputMap[p]),r:row});
}
});
rows.sort(function(A,B) {
return A.i-B.i;
})
rows.forEach(function(r,i) {
r.r.find("label").text((i+1)+".");
r.r.appendTo(outputsDiv);
})
if (rows.length === 0) {
buildLabelRow("output",i,"").appendTo(outputsDiv);
} else {
}
} else {
outputCount = Math.max(0,parseInt(formOutputs));
}
children = outputsDiv.children();
childCount = children.length;
if (childCount === 1 && $(children[0]).hasClass('red-ui-editor-node-label-form-none')) {
childCount--;
}
if (childCount < outputCount) {
if (childCount === 0) {
// remove the 'none' placeholder
$(children[0]).remove();
}
for (i = childCount;i<outputCount;i++) {
buildLabelRow("output",i,"").appendTo(outputsDiv);
}
} else if (childCount > outputCount) {
for (i=outputCount;i<childCount;i++) {
$(children[i]).remove();
}
if (outputCount === 0) {
buildLabelRow().appendTo(outputsDiv);
}
}
}
function buildLabelRow(type, index, value, placeHolder) {
var result = $('<div>',{class:"red-ui-editor-node-label-form-row"});
if (type === undefined) {
$('<span>').text(RED._("editor.noDefaultLabel")).appendTo(result);
result.addClass("red-ui-editor-node-label-form-none");
} else {
result.addClass("");
var id = "red-ui-editor-node-label-form-"+type+"-"+index;
$('<label>',{for:id}).text((index+1)+".").appendTo(result);
var input = $('<input>',{type:"text",id:id, placeholder: placeHolder}).val(value).appendTo(result);
var clear = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').appendTo(result);
clear.on("click", function(evt) {
evt.preventDefault();
input.val("");
})
}
return result;
}
function updateLabels(node, changes, outputMap) {
var inputLabels = $("#red-ui-editor-node-label-form-inputs").children().find("input");
var outputLabels = $("#red-ui-editor-node-label-form-outputs").children().find("input");
var hasNonBlankLabel = false;
var changed = false;
var newValue = inputLabels.map(function() {
var v = $(this).val();
hasNonBlankLabel = hasNonBlankLabel || v!== "";
return v;
}).toArray().slice(0,node.inputs);
if ((node.inputLabels === undefined && hasNonBlankLabel) ||
(node.inputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.inputLabels))) {
changes.inputLabels = node.inputLabels;
node.inputLabels = newValue;
changed = true;
}
hasNonBlankLabel = false;
newValue = new Array(node.outputs);
outputLabels.each(function() {
var index = $(this).attr('id').substring("red-ui-editor-node-label-form-output-".length);
if (outputMap && outputMap.hasOwnProperty(index)) {
index = parseInt(outputMap[index]);
if (index === -1) {
return;
}
}
var v = $(this).val();
hasNonBlankLabel = hasNonBlankLabel || v!== "";
newValue[index] = v;
});
if ((node.outputLabels === undefined && hasNonBlankLabel) ||
(node.outputLabels !== undefined && JSON.stringify(newValue) !== JSON.stringify(node.outputLabels))) {
changes.outputLabels = node.outputLabels;
node.outputLabels = newValue;
changed = true;
}
return changed;
}
})();

View File

@@ -0,0 +1,70 @@
;(function() {
RED.editor.registerEditPane("editor-tab-description", function(node) {
return {
label: RED._("editor-tab.description"),
name: RED._("editor-tab.description"),
iconClass: "fa fa-file-text-o",
create: function(container) {
this.editor = buildDescriptionForm(container,node);
RED.e = this.editor;
},
resize: function(size) {
this.editor.resize();
},
close: function() {
this.editor.destroy();
this.editor = null;
},
show: function() {
this.editor.focus();
},
apply: function(editState) {
var oldInfo = node.info;
var newInfo = this.editor.getValue();
if (!!oldInfo) {
// Has existing info property
if (newInfo.trim() === "") {
// New value is blank - remove the property
editState.changed = true;
editState.changes.info = oldInfo;
delete node.info;
} else if (newInfo !== oldInfo) {
// New value is different
editState.changed = true;
editState.changes.info = oldInfo;
node.info = newInfo;
}
} else {
// No existing info
if (newInfo.trim() !== "") {
// New value is not blank
editState.changed = true;
editState.changes.info = undefined;
node.info = newInfo;
}
}
}
}
});
function buildDescriptionForm(container,node) {
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
var toolbarRow = $('<div></div>').appendTo(dialogForm);
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: 100%"></div>').appendTo(dialogForm);
var editorId = "node-info-input-info-editor-"+Math.floor(1000*Math.random());
$('<div style="height: 100%" class="node-text-editor" id="'+editorId+'" ></div>').appendTo(row);
var nodeInfoEditor = RED.editor.createEditor({
id: editorId,
mode: 'ace/mode/markdown',
value: ""
});
if (node.info) {
nodeInfoEditor.getSession().setValue(node.info, -1);
}
node.infoEditor = nodeInfoEditor;
return nodeInfoEditor;
}
})();

View File

@@ -0,0 +1,75 @@
;(function() {
RED.editor.registerEditPane("editor-tab-envProperties", function(node) {
return {
label: RED._("editor-tab.envProperties"),
name: RED._("editor-tab.envProperties"),
iconClass: "fa fa-list",
create: function(container) {
var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
this.list = $('<ol></ol>').appendTo(listContainer);
RED.editor.envVarList.create(this.list, node);
},
resize: function(size) {
this.list.editableList('height',size.height);
},
close: function() {
},
apply: function(editState) {
var old_env = node.env;
var new_env = [];
if (/^subflow:/.test(node.type)) {
new_env = RED.subflow.exportSubflowInstanceEnv(node);
}
// Get the values from the Properties table tab
var items = this.list.editableList('items');
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")
}
if (item.name.trim() !== "") {
new_env.push(item);
}
}
});
if (new_env && new_env.length > 0) {
new_env.forEach(function(prop) {
if (prop.type === "cred") {
node.credentials = node.credentials || {_:{}};
node.credentials[prop.name] = prop.value;
node.credentials['has_'+prop.name] = (prop.value !== "");
if (prop.value !== '__PWRD__') {
editState.changed = true;
}
delete prop.value;
}
});
}
if (!old_env && new_env.length === 0) {
delete node.env;
} else if (!isSameObj(old_env, new_env)) {
editState.changes.env = node.env;
if (new_env.length === 0) {
delete node.env;
} else {
node.env = new_env;
}
editState.changed = true;
}
}
}
});
function isSameObj(env0, env1) {
return (JSON.stringify(env0) === JSON.stringify(env1));
}
})();

View File

@@ -0,0 +1,60 @@
;(function() {
RED.editor.registerEditPane("editor-tab-flow-properties", function(node) {
return {
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
iconClass: "fa fa-cog",
create: function(container) {
var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(container);
$('<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>').appendTo(dialogForm);
var row = $('<div class="form-row node-text-editor-row">'+
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
'<div style="min-height:150px;" class="node-text-editor" id="node-input-info"></div>'+
'</div>').appendTo(dialogForm);
this.tabflowEditor = RED.editor.createEditor({
id: 'node-input-info',
mode: 'ace/mode/markdown',
value: ""
});
$('<input type="text" style="display: none;" />').prependTo(dialogForm);
dialogForm.on("submit", function(e) { e.preventDefault();});
$("#node-input-name").val(node.label);
RED.text.bidi.prepareInput($("#node-input-name"));
this.tabflowEditor.getSession().setValue(node.info || "", -1);
},
resize: function(size) {
$("#node-input-info").css("height", (size.height-70)+"px");
this.tabflowEditor.resize();
},
close: function() {
this.tabflowEditor.destroy();
},
apply: function(editState) {
var label = $( "#node-input-name" ).val();
if (node.label != label) {
editState.changes.label = node.label;
editState.changed = true;
node.label = label;
}
var info = this.tabflowEditor.getValue();
if (node.info !== info) {
editState.changes.info = node.info;
editState.changed = true;
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,181 @@
;(function() {
RED.editor.registerEditPane("editor-tab-properties", function(node) {
return {
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
iconClass: "fa fa-cog",
create: function(container) {
var nodeType = node.type;
if (node.type === "subflow") {
nodeType = "subflow-template";
} else if (node.type.substring(0,8) == "subflow:") {
nodeType = "subflow";
}
var i18nNamespace;
if (node._def.set.module === "node-red") {
i18nNamespace = "node-red";
} else {
i18nNamespace = node._def.set.id;
}
var formStyle = "dialog-form";
this.inputClass = "node-input";
if (node._def.category === "config" && nodeType !== "group") {
this.inputClass = "node-config-input";
formStyle = "node-config-dialog-edit-form";
}
RED.editor.buildEditForm(container,formStyle,nodeType,i18nNamespace,node);
},
resize: function(size) {
if (node && node._def.oneditresize) {
try {
node._def.oneditresize.call(node,size);
} catch(err) {
console.log("oneditresize",node.id,node.type,err.toString());
}
}
},
close: function() {
},
apply: function(editState) {
var newValue;
var d;
if (node._def.defaults) {
for (d in node._def.defaults) {
if (node._def.defaults.hasOwnProperty(d)) {
var input = $("#"+this.inputClass+"-"+d);
if (input.attr('type') === "checkbox") {
newValue = input.prop('checked');
} else if (input.prop("nodeName") === "select" && input.attr("multiple") === "multiple") {
// An empty select-multiple box returns null.
// Need to treat that as an empty array.
newValue = input.val();
if (newValue == null) {
newValue = [];
}
} else if ("format" in node._def.defaults[d] && node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") {
newValue = input.text();
} else {
newValue = input.val();
}
if (newValue != null) {
if (d === "outputs") {
if (newValue.trim() === "") {
continue;
}
if (isNaN(newValue)) {
editState.outputMap = JSON.parse(newValue);
var outputCount = 0;
var outputsChanged = false;
var keys = Object.keys(editState.outputMap);
keys.forEach(function(p) {
if (isNaN(p)) {
// New output;
outputCount ++;
delete editState.outputMap[p];
} else {
editState.outputMap[p] = editState.outputMap[p]+"";
if (editState.outputMap[p] !== "-1") {
outputCount++;
if (editState.outputMap[p] !== p) {
// Output moved
outputsChanged = true;
} else {
delete editState.outputMap[p];
}
} else {
// Output removed
outputsChanged = true;
}
}
});
newValue = outputCount;
if (outputsChanged) {
editState.changed = true;
}
} else {
newValue = parseInt(newValue);
}
}
if (node._def.defaults[d].type) {
if (newValue == "_ADD_") {
newValue = "";
}
}
if (node[d] != newValue) {
if (node._def.defaults[d].type) {
// Change to a related config node
var configNode = RED.nodes.node(node[d]);
if (configNode) {
var users = configNode.users;
users.splice(users.indexOf(node),1);
RED.events.emit("nodes:change",configNode);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(node);
RED.events.emit("nodes:change",configNode);
}
}
editState.changes[d] = node[d];
node[d] = newValue;
editState.changed = true;
}
}
}
}
}
if (node._def.credentials) {
var credDefinition = node._def.credentials;
var credsChanged = updateNodeCredentials(node,credDefinition,this.inputClass);
editState.changed = editState.changed || credsChanged;
}
}
}
});
/**
* Update the node credentials from the edit form
* @param node - the node containing the credentials
* @param credDefinition - definition of the credentials
* @param prefix - prefix of the input fields
* @return {boolean} whether anything has changed
*/
function updateNodeCredentials(node, credDefinition, prefix) {
var changed = false;
if (!node.credentials) {
node.credentials = {_:{}};
} else if (!node.credentials._) {
node.credentials._ = {};
}
for (var cred in credDefinition) {
if (credDefinition.hasOwnProperty(cred)) {
var input = $("#" + prefix + '-' + cred);
if (input.length > 0) {
var value = input.val();
if (credDefinition[cred].type == 'password') {
node.credentials['has_' + cred] = (value !== "");
if (value == '__PWRD__') {
continue;
}
changed = true;
}
node.credentials[cred] = value;
if (value != node.credentials._[cred]) {
changed = true;
}
}
}
}
return changed;
}
})();

View File

@@ -0,0 +1,179 @@
(function() {
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
'<div class="form-row">'+
'<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+
'</div>'+
'</form>';
RED.editor.registerEditPane("editor-tab-subflow-module", function(node) {
return {
label: RED._("editor-tab.module"),
name: RED._("editor-tab.module"),
iconClass: "fa fa-cube",
create: function(container) {
buildModuleForm(container, node);
},
resize: function(size) {
},
close: function() {
},
apply: function(editState) {
var newMeta = exportSubflowModuleProperties(node);
if (!isSameObj(node.meta,newMeta)) {
editState.changes.meta = node.meta;
node.meta = newMeta;
editState.changed = true;
}
}
}
});
function isSameObj(env0, env1) {
return (JSON.stringify(env0) === JSON.stringify(env1));
}
function setupInputValidation(input,validator) {
var errorTip;
var validateTimeout;
var validateFunction = function() {
if (validateTimeout) {
return;
}
validateTimeout = setTimeout(function() {
var error = validator(input.val());
// if (!error && errorTip) {
// errorTip.close();
// errorTip = null;
// } else if (error && !errorTip) {
// errorTip = RED.popover.create({
// tooltip: true,
// target:input,
// size: "small",
// direction: "bottom",
// content: error,
// }).open();
// }
input.toggleClass("input-error",!!error);
validateTimeout = null;
})
}
input.on("change keyup paste", validateFunction);
}
function buildModuleForm(container, node) {
$(_subflowModulePaneTemplate).appendTo(container);
var moduleProps = node.meta || {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords',
'license'
].forEach(function(property) {
$("#subflow-input-module-"+property).val(moduleProps[property]||"")
})
$("#subflow-input-module-type").attr("placeholder",node.id);
setupInputValidation($("#subflow-input-module-module"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue.length < 215;
isValid = isValid && !/^[._]/.test(newValue);
isValid = isValid && !/[A-Z]/.test(newValue);
if (newValue !== encodeURIComponent(newValue)) {
var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue);
if (m) {
isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2]))
} else {
isValid = false;
}
}
return isValid?"":"Invalid module name"
})
setupInputValidation($("#subflow-input-module-version"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue === "" ||
/^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue);
return isValid?"":"Invalid version number"
})
var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"];
var typedLicenses = {
types: licenses.map(function(l) {
return {
value: l,
label: l === "none" ? RED._("editor:subflow.licenseNone") : l,
hasValue: false
};
})
}
typedLicenses.types.push({
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
})
if (!moduleProps.license) {
typedLicenses.default = "none";
} else if (licenses.indexOf(moduleProps.license) > -1) {
typedLicenses.default = moduleProps.license;
} else {
typedLicenses.default = "_custom_";
}
$("#subflow-input-module-license").typedInput(typedLicenses)
}
function exportSubflowModuleProperties(node) {
var value;
var moduleProps = {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords'
].forEach(function(property) {
value = $("#subflow-input-module-"+property).val().trim();
if (value) {
moduleProps[property] = value;
}
})
var selectedLicenseType = $("#subflow-input-module-license").typedInput("type");
if (selectedLicenseType === '_custom_') {
value = $("#subflow-input-module-license").val();
if (value) {
moduleProps.license = value;
}
} else if (selectedLicenseType !== "none") {
moduleProps.license = selectedLicenseType;
}
return moduleProps;
}
})();

View File

@@ -87,16 +87,18 @@ RED.group = (function() {
"label-position": "nw"
};
var groupDef = {
defaults:{
name:{value:""},
style:{value:{label:true}},
nodes:{value:[]}
nodes:{value:[]},
env: {value:[]},
},
category: "config",
oneditprepare: function() {
var style = this.style || {};
RED.colorPicker.create({
RED.editor.colorPicker.create({
id:"node-input-style-stroke",
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
palette: colorPalette,
@@ -107,7 +109,7 @@ RED.group = (function() {
none: true,
opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0)
}).appendTo("#node-input-row-style-stroke");
RED.colorPicker.create({
RED.editor.colorPicker.create({
id:"node-input-style-fill",
value: style.fill || defaultGroupStyle.fill ||"none",
palette: colorPalette,
@@ -124,7 +126,7 @@ RED.group = (function() {
value:style["label-position"] || "nw"
}).appendTo("#node-input-row-style-label-position");
RED.colorPicker.create({
RED.editor.colorPicker.create({
id:"node-input-style-color",
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
palette: colorPalette,
@@ -144,7 +146,6 @@ RED.group = (function() {
})
$("#node-input-style-label").prop("checked", this.style.label)
$("#node-input-style-label").trigger("change");
},
oneditresize: function(size) {
},
@@ -183,7 +184,9 @@ RED.group = (function() {
var activateUngroup = false;
var activateMerge = false;
var activateRemove = false;
var singleGroupSelected = false;
if (activateGroup) {
singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
selection.nodes.forEach(function (n) {
if (n.type === "group") {
activateUngroup = true;
@@ -200,6 +203,8 @@ RED.group = (function() {
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-edit-copy-group-style", !singleGroupSelected);
RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup);
});
RED.actions.add("core:group-selection", function() { groupSelection() })
@@ -252,6 +257,7 @@ RED.group = (function() {
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') {
groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style));
RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"})
RED.menu.setDisabled("menu-item-edit-paste-group-style", false)
}
}
function pasteGroupStyle() {
@@ -584,12 +590,14 @@ RED.group = (function() {
markDirty(group);
}
function getNodes(group,recursive) {
function getNodes(group,recursive,excludeGroup) {
var nodes = [];
group.nodes.forEach(function(n) {
nodes.push(n);
if (n.type !== 'group' || !excludeGroup) {
nodes.push(n);
}
if (recursive && n.type === 'group') {
nodes = nodes.concat(getNodes(n,recursive))
nodes = nodes.concat(getNodes(n,recursive,excludeGroup))
}
})
return nodes;

View File

@@ -131,7 +131,7 @@ RED.keyboard = (function() {
return mergedKeymap;
}
function init() {
function init(done) {
// Migrate from pre-0.18
migrateOldKeymap();
@@ -164,6 +164,7 @@ RED.keyboard = (function() {
}
}
}
done();
});
RED.userSettings.add({
@@ -174,6 +175,9 @@ RED.keyboard = (function() {
setTimeout(function() {
$("#red-ui-settings-tab-keyboard-filter").trigger("focus");
},200);
},
close: function() {
RED.menu.refreshShortcuts();
}
})
}
@@ -239,7 +243,13 @@ RED.keyboard = (function() {
function resolveKeyEvent(evt) {
var slot = partialState||handlers;
if (evt.ctrlKey || evt.metaKey) {
// We cheat with MacOS CMD key and consider it the same as Ctrl.
// That means we don't have to have separate keymaps for different OS.
// It mostly works.
// One exception is shortcuts that include both Cmd and Ctrl. We don't
// support them - but we need to make sure we don't block browser-specific
// shortcuts (such as Cmd-Ctrl-F for fullscreen).
if ((evt.ctrlKey || evt.metaKey) && (evt.ctrlKey !== evt.metaKey)) {
slot = slot.ctrl;
}
if (slot && evt.shiftKey) {

View File

@@ -536,7 +536,7 @@ RED.library = (function() {
// evt.preventDefault();
// var icon = libraryFields['icon'].input.val() || "";
// var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
// RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) {
// RED.editor.iconPicker.show(iconButton, null, iconPath, true, function (newIcon) {
// iconButton.empty();
// var path = newIcon || "";
// var newPath = RED.utils.separateIconPath(path);
@@ -820,7 +820,7 @@ RED.library = (function() {
close: function(e) {
RED.keyboard.enable();
if (libraryEditor) {
libraryEditor.remove();
libraryEditor.destroy();
libraryEditor = null;
}
}

View File

@@ -45,6 +45,22 @@ RED.notifications = (function() {
var persistentNotifications = {};
var shade = (function() {
var shadeUsers = 0;
return {
show: function() {
shadeUsers++;
$("#red-ui-full-shade").show();
},
hide: function() {
shadeUsers--;
if (shadeUsers === 0) {
$("#red-ui-full-shade").hide();
}
}
}
})();
var currentNotifications = [];
var c = 0;
function notify(msg,type,fixed,timeout) {
@@ -54,6 +70,10 @@ RED.notifications = (function() {
fixed = options.fixed;
timeout = options.timeout;
type = options.type;
} else {
options.type = type;
options.fixed = fixed;
options.timeout = options.timeout;
}
if (options.id && persistentNotifications.hasOwnProperty(options.id)) {
@@ -62,7 +82,7 @@ RED.notifications = (function() {
}
if (options.modal) {
$("#red-ui-full-shade").show();
shade.show();
}
if (currentNotifications.length > 4) {
@@ -79,6 +99,8 @@ RED.notifications = (function() {
var n = document.createElement("div");
n.id="red-ui-notification-"+c;
n.className = "red-ui-notification";
n.options = options;
n.fixed = fixed;
if (type) {
n.className = "red-ui-notification red-ui-notification-"+type;
@@ -115,7 +137,6 @@ RED.notifications = (function() {
})
}
$("#red-ui-notifications").append(n);
if (!RED.notifications.hide) {
$(n).slideDown(300);
@@ -141,8 +162,8 @@ RED.notifications = (function() {
} else {
nn.parentNode.removeChild(nn);
}
if (options.modal) {
$("#red-ui-full-shade").hide();
if (nn.options.modal) {
shade.hide();
}
};
})();
@@ -173,7 +194,7 @@ RED.notifications = (function() {
n.update = (function() {
var nn = n;
return function(msg,options) {
return function(msg,newOptions) {
if (typeof msg === "string") {
if (!/<p>/i.test(msg)) {
msg = "<p>"+msg+"</p>";
@@ -182,16 +203,31 @@ RED.notifications = (function() {
} else {
$(nn).empty().append(msg);
}
var timeout;
if (typeof options === 'number') {
timeout = options;
} else if (options !== undefined) {
if (!options.fixed) {
timeout = options.timeout || 5000;
var newTimeout;
if (typeof newOptions === 'number') {
newTimeout = newOptions;
nn.options.timeout = newTimeout;
} else if (newOptions !== undefined) {
if (!options.modal && newOptions.modal) {
nn.options.modal = true;
shade.show();
} else if (options.modal && newOptions.modal === false) {
nn.options.modal = false;
shade.hide();
}
if (options.buttons) {
var newType = newOptions.hasOwnProperty('type')?newOptions.type:type;
if (newType) {
n.className = "red-ui-notification red-ui-notification-"+newType;
}
if (!fixed || newOptions.fixed === false) {
newTimeout = (newOptions.hasOwnProperty('timeout')?newOptions.timeout:timeout)||5000;
}
if (newOptions.buttons) {
var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn)
options.buttons.forEach(function(buttonDef) {
newOptions.buttons.forEach(function(buttonDef) {
var b = $('<button>').text(buttonDef.text).on("click", buttonDef.click).appendTo(buttonSet);
if (buttonDef.id) {
b.attr('id',buttonDef.id);
@@ -202,15 +238,22 @@ RED.notifications = (function() {
})
}
}
if (timeout !== undefined && timeout > 0) {
$(nn).off("click.red-ui-notification-close");
if (newTimeout !== undefined && newTimeout > 0) {
window.clearTimeout(nn.timeoutid);
nn.timeoutid = window.setTimeout(nn.close,timeout);
nn.timeoutid = window.setTimeout(nn.close,newTimeout);
setTimeout(function() {
$(nn).on("click.red-ui-notification-close", function() {
nn.close();
window.clearTimeout(nn.timeoutid);
});
},50);
} else {
window.clearTimeout(nn.timeoutid);
}
if (nn.hidden) {
nn.showNotification();
} else if (!options || !options.silent){
} else if (!newOptions || !newOptions.silent){
$(nn).addClass("red-ui-notification-shake-horizontal");
setTimeout(function() {
$(nn).removeClass("red-ui-notification-shake-horizontal");
@@ -221,7 +264,7 @@ RED.notifications = (function() {
})();
if (!fixed) {
$(n).on("click", (function() {
$(n).on("click.red-ui-notification-close", (function() {
var nn = n;
return function() {
nn.close();

View File

@@ -264,6 +264,8 @@ RED.palette.editor = (function() {
var errMessage = set.err;
if (set.err.message) {
errMessage = set.err.message;
} else if (set.err.code) {
errMessage = set.err.code;
}
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
}
@@ -331,7 +333,10 @@ RED.palette.editor = (function() {
nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').css('display', 'inline-block');
} else if (loadedIndex.hasOwnProperty(module)) {
if (semVerCompare(loadedIndex[module].version,moduleInfo.version) > 0) {
if (updateAllowed &&
semVerCompare(loadedIndex[module].version,moduleInfo.version) > 0 &&
RED.utils.checkModuleAllowed(module,null,updateAllowList,updateDenyList)
) {
nodeEntry.updateButton.show();
nodeEntry.updateButton.text(RED._('palette.editor.update',{version:loadedIndex[module].version}));
} else {
@@ -482,6 +487,9 @@ RED.palette.editor = (function() {
var installAllowList = ['*'];
var installDenyList = [];
var updateAllowed = true;
var updateAllowList = ['*'];
var updateDenyList = [];
function init() {
if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
@@ -496,6 +504,17 @@ RED.palette.editor = (function() {
installAllowList = RED.utils.parseModuleList(installAllowList);
installDenyList = RED.utils.parseModuleList(installDenyList);
var settingsUpdateAllowList = RED.settings.get("externalModules.palette.allowUpdateList")
var settingsUpdateDenyList = RED.settings.get("externalModules.palette.denyUpdateList")
if (settingsUpdateAllowList || settingsUpdateDenyList) {
updateAllowList = settingsUpdateAllowList;
updateDenyList = settingsUpdateDenyList;
}
updateAllowList = RED.utils.parseModuleList(updateAllowList);
updateDenyList = RED.utils.parseModuleList(updateDenyList);
updateAllowed = RED.settings.get("externalModules.palette.allowUpdate",true);
createSettingsPane();
RED.userSettings.add({

View File

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

View File

@@ -22,6 +22,7 @@ RED.search = (function() {
var selected = -1;
var visible = false;
var searchHistory = [];
var index = {};
var currentResults = [];
var previousActiveElement;
@@ -105,6 +106,7 @@ RED.search = (function() {
val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags);
val = extractFlag(val,"hidden",flags);
// uses:<node-id>
val = extractValue(val,"uses",flags);
@@ -150,7 +152,15 @@ RED.search = (function() {
continue;
}
}
if (flags.hasOwnProperty("hidden")) {
// Only tabs can be hidden
if (node.node.type !== 'tab') {
continue
}
if (!RED.workspaces.isHidden(node.node.id)) {
continue
}
}
if (flags.hasOwnProperty("unused")) {
var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
(isConfigNode && node.node.users.length === 0)
@@ -196,6 +206,20 @@ RED.search = (function() {
}
}
function populateSearchHistory() {
if (searchHistory.length > 0) {
searchResults.editableList('addItem',{
historyHeader: true
});
searchHistory.forEach(function(entry) {
searchResults.editableList('addItem',{
history: true,
value: entry
});
})
}
}
function createDialog() {
dialog = $("<div>",{id:"red-ui-search",class:"red-ui-search"}).appendTo("#red-ui-main-container");
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
@@ -204,7 +228,12 @@ RED.search = (function() {
change: function() {
searchResults.editableList('empty');
selected = -1;
currentResults = search($(this).val());
var value = $(this).val();
if (value === "") {
populateSearchHistory();
return;
}
currentResults = search(value);
if (currentResults.length > 0) {
for (i=0;i<Math.min(currentResults.length,25);i++) {
searchResults.editableList('addItem',currentResults[i])
@@ -276,7 +305,12 @@ RED.search = (function() {
})
}
}
} else {
} if ($(children[selected]).hasClass("red-ui-search-history")) {
var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data');
if (object) {
searchInput.searchBox('value',object.value)
}
} else if (!$(children[selected]).hasClass("red-ui-search-historyHeader")) {
if (currentResults.length > 0) {
reveal(currentResults[Math.max(0,selected)].node);
}
@@ -292,7 +326,32 @@ RED.search = (function() {
addItem: function(container,i,object) {
var node = object.node;
var div;
if (object.more) {
if (object.historyHeader) {
container.parent().addClass("red-ui-search-historyHeader")
$('<div>',{class:"red-ui-search-empty"}).text(RED._("search.history")).appendTo(container);
$('<button type="button" class="red-ui-button red-ui-button-small"></button>').text(RED._("search.clear")).appendTo(container).on("click", function(evt) {
evt.preventDefault();
searchHistory = [];
searchResults.editableList('empty');
});
} else if (object.history) {
container.parent().addClass("red-ui-search-history")
div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
div.text(object.value);
div.on("click", function(evt) {
evt.preventDefault();
searchInput.searchBox('value',object.value)
searchInput.focus();
})
$('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></button>').appendTo(container).on("click", function(evt) {
evt.preventDefault();
var index = searchHistory.indexOf(object.value);
searchHistory.splice(index,1);
searchResults.editableList('removeItem', object);
});
} else if (object.more) {
container.parent().addClass("red-ui-search-more")
div = $('<a>',{href:'#',class:"red-ui-search-result red-ui-search-empty"}).appendTo(container);
div.text(RED._("palette.editor.more",{count:object.more.results.length-object.more.start}));
@@ -347,6 +406,12 @@ RED.search = (function() {
}
function reveal(node) {
var searchVal = searchInput.val();
var existingIndex = searchHistory.indexOf(searchVal);
if (existingIndex > -1) {
searchHistory.splice(existingIndex,1);
}
searchHistory.unshift(searchInput.val());
hide();
RED.view.reveal(node.id);
}
@@ -365,9 +430,14 @@ RED.search = (function() {
if (dialog === null) {
createDialog();
} else {
searchResults.editableList('empty');
}
dialog.slideDown(300);
searchInput.searchBox('value',v)
if (!v || v === "") {
populateSearchHistory();
}
RED.events.emit("search:open");
visible = true;
}

View File

@@ -27,5 +27,7 @@ RED.state = {
PANNING: 10,
SELECTING_NODE: 11,
GROUP_DRAGGING: 12,
GROUP_RESIZE: 13
GROUP_RESIZE: 13,
DETACHED_DRAGGING: 14,
SLICING: 15
}

View File

@@ -16,8 +16,6 @@
RED.subflow = (function() {
var currentLocale = "en-US";
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
'<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
@@ -37,7 +35,7 @@ RED.subflow = (function() {
'<div id="subflow-env-tabs-content">'+
'<div id="subflow-env-tab-edit">'+
'<div class="form-row node-input-env-container-row" id="subflow-input-edit-ui">'+
'<ol class="red-ui-editor-subflow-env-list" id="node-input-env-container"></ol>'+
'<ol id="node-input-env-container"></ol>'+
'<div class="node-input-env-locales-row"><i class="fa fa-language"></i> <select id="subflow-input-env-locale"></select></div>'+
'</div>'+
'</div>'+
@@ -47,37 +45,6 @@ RED.subflow = (function() {
'</div>'+
'</script>';
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
'<div class="form-row">'+
'<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+
'</div>'+
'</form>';
function findAvailableSubflowIOPosition(subflow,isInput) {
var pos = {x:50,y:30};
if (!isInput) {
@@ -909,7 +876,6 @@ RED.subflow = (function() {
* Create interface for controlling env var UI definition
*/
function buildEnvControl(envList,node) {
var tabs = RED.tabs.create({
id: "subflow-env-tabs",
onchange: function(tab) {
@@ -950,588 +916,11 @@ RED.subflow = (function() {
locales.val(locale);
locales.on("change", function() {
currentLocale = $(this).val();
var items = $("#node-input-env-container").editableList("items");
items.each(function (i, item) {
var entry = $(this).data('data');
var labelField = entry.ui.labelField;
labelField.val(lookupLabel(entry.ui.label, "", currentLocale));
if (labelField.timeout) {
clearTimeout(labelField.timeout);
delete labelField.timeout;
}
labelField.addClass("input-updated");
labelField.timeout = setTimeout(function() {
delete labelField.timeout
labelField.removeClass("input-updated");
},3000);
});
RED.editor.envVarList.setLocale($(this).val(), $("#node-input-env-container"));
});
RED.editor.envVarList.setLocale(locale);
}
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'];
/**
* Create env var edit interface
* @param container - container
* @param node - subflow node
*/
function buildPropertiesList(envContainer, node) {
var isTemplateNode = (node.type === "subflow");
if (isTemplateNode) {
buildEnvControl(envContainer, node);
}
envContainer
.css({
'min-height':'150px',
'min-width':'450px'
})
.editableList({
header: isTemplateNode?$('<div><div><div></div><div data-i18n="common.label.name"></div><div data-i18n="editor-tab.defaultValue"></div><div></div></div></div>'):undefined,
addItem: function(container, i, opt) {
// If this is an instance node, these are properties unique to
// this instance - ie opt.parent will not be defined.
if (isTemplateNode) {
container.addClass("red-ui-editor-subflow-env-editable")
}
var envRow = $('<div/>').appendTo(container);
var nameField = null;
var valueField = null;
nameField = $('<input/>', {
class: "node-input-env-name",
type: "text",
placeholder: RED._("common.label.name")
}).attr("autocomplete","disable").appendTo(envRow).val(opt.name);
valueField = $('<input/>',{
style: "width:100%",
class: "node-input-env-value",
type: "text",
}).attr("autocomplete","disable").appendTo(envRow)
valueField.typedInput({default:'str',types:isTemplateNode?DEFAULT_ENV_TYPE_LIST:DEFAULT_ENV_TYPE_LIST_INC_CRED});
valueField.typedInput('type', opt.type);
if (opt.type === "cred") {
if (opt.value) {
valueField.typedInput('value', opt.value);
} else if (node.credentials && node.credentials[opt.name]) {
valueField.typedInput('value', node.credentials[opt.name]);
} else if (node.credentials && node.credentials['has_'+opt.name]) {
valueField.typedInput('value', "__PWRD__");
} else {
valueField.typedInput('value', "");
}
} else {
valueField.typedInput('value', opt.value);
}
opt.nameField = nameField;
opt.valueField = valueField;
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(envRow);
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
actionButton.on("click", function(evt) {
evt.preventDefault();
removeTip.close();
container.parent().addClass("red-ui-editableList-item-deleting")
container.fadeOut(300, function() {
envContainer.editableList('removeItem',opt);
});
});
if (isTemplateNode) {
// Add the UI customisation row
// if `opt.ui` does not exist, then apply defaults. If these
// defaults do not change then they will get stripped off
// before saving.
if (opt.type === 'cred') {
opt.ui = opt.ui || {
icon: "",
type: "cred"
}
opt.ui.type = "cred";
} else {
opt.ui = opt.ui || {
icon: "",
type: "input",
opts: {types:DEFAULT_ENV_TYPE_LIST}
}
}
opt.ui.label = opt.ui.label || {};
opt.ui.type = opt.ui.type || "input";
var uiRow = $('<div/>').appendTo(container).hide();
// save current info for reverting on cancel
// var copy = $.extend(true, {}, ui);
$('<a href="#"><i class="fa fa-angle-right"></a>').prependTo(envRow).on("click", function (evt) {
evt.preventDefault();
if ($(this).hasClass('expanded')) {
uiRow.slideUp();
$(this).removeClass('expanded');
} else {
uiRow.slideDown();
$(this).addClass('expanded');
}
});
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
nameField.trigger('change');
}
},
sortable: ".red-ui-editableList-item-handle",
removable: false
});
var parentEnv = {};
var envList = [];
if (/^subflow:/.test(node.type)) {
var subflowDef = RED.nodes.subflow(node.type.substring(8));
if (subflowDef.env) {
subflowDef.env.forEach(function(env) {
var item = {
name:env.name,
parent: {
type: env.type,
value: env.value,
ui: env.ui
}
}
envList.push(item);
parentEnv[env.name] = item;
})
}
}
if (node.env) {
for (var i = 0; i < node.env.length; i++) {
var env = node.env[i];
if (parentEnv.hasOwnProperty(env.name)) {
parentEnv[env.name].type = env.type;
parentEnv[env.name].value = env.value;
} else {
envList.push({
name: env.name,
type: env.type,
value: env.value,
ui: env.ui
});
}
}
}
envList.forEach(function(env) {
if (env.parent && env.parent.ui && env.parent.ui.type === 'hide') {
return;
}
if (!isTemplateNode && env.parent) {
return;
}
envContainer.editableList('addItem', JSON.parse(JSON.stringify(env)));
});
}
/**
* Create UI edit interface for environment variable
* @param container - container
* @param env - env var definition
* @param nameField - name field of env var
* @param valueField - value field of env var
*/
function buildEnvEditRow(container, ui, nameField, valueField) {
container.addClass("red-ui-editor-subflow-env-ui-row")
var topRow = $('<div></div>').appendTo(container);
$('<div></div>').appendTo(topRow);
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
$('<div>').text(RED._("editor.label")).appendTo(topRow);
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
var row = $('<div></div>').appendTo(container);
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
var typeOptions = {
'input': {types:DEFAULT_ENV_TYPE_LIST},
'select': {opts:[]},
'spinner': {},
'cred': {}
};
if (ui.opts) {
typeOptions[ui.type] = ui.opts;
} else {
// Pick up the default values if not otherwise provided
ui.opts = typeOptions[ui.type];
}
var iconCell = $('<div></div>').appendTo(row);
var iconButton = $('<a href="#"></a>').appendTo(iconCell);
iconButton.on("click", function(evt) {
evt.preventDefault();
var icon = ui.icon || "";
var iconPath = (icon ? RED.utils.separateIconPath(icon) : {});
RED.editor.showIconPicker(iconButton, null, iconPath, true, function (newIcon) {
iconButton.empty();
var path = newIcon || "";
var newPath = RED.utils.separateIconPath(path);
if (newPath) {
$('<i class="fa"></i>').addClass(newPath.file).appendTo(iconButton);
}
ui.icon = path;
});
})
if (ui.icon) {
var newPath = RED.utils.separateIconPath(ui.icon);
$('<i class="fa '+newPath.file+'"></i>').appendTo(iconButton);
}
var labelCell = $('<div></div>').appendTo(row);
var label = ui.label && ui.label[currentLocale] || "";
var labelInput = $('<input type="text">').val(label).appendTo(labelCell);
ui.labelField = labelInput;
labelInput.on('change', function(evt) {
ui.label = ui.label || {};
var val = $(this).val().trim();
if (val === "") {
delete ui.label[currentLocale];
} else {
ui.label[currentLocale] = val;
}
})
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelCell);
RED.popover.tooltip(labelIcon,function() {
var langs = Object.keys(ui.label);
var content = $("<div>");
if (langs.indexOf(currentLocale) === -1) {
langs.push(currentLocale);
langs.sort();
}
langs.forEach(function(l) {
var row = $('<div>').appendTo(content);
$('<span>').css({display:"inline-block",width:"120px"}).text(RED._("languages."+l)+(l===currentLocale?"*":"")).appendTo(row);
$('<span>').text(ui.label[l]||"").appendTo(row);
});
return content;
})
nameField.on('change',function(evt) {
labelInput.attr("placeholder",$(this).val())
});
var inputCell = $('<div></div>').appendTo(row);
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
if (ui.type === "input") {
inputCellInput.val(ui.opts.types.join(","));
}
var checkbox;
var selectBox;
inputCellInput.typedInput({
types: [
{
value:"input",
label:RED._("editor.inputs.input"), icon:"fa fa-i-cursor",showLabel:false,multiple:true,options:[
{value:"str",label:RED._("editor.types.str"),icon:"red/images/typedInput/az.svg"},
{value:"num",label:RED._("editor.types.num"),icon:"red/images/typedInput/09.svg"},
{value:"bool",label:RED._("editor.types.bool"),icon:"red/images/typedInput/bool.svg"},
{value:"json",label:RED._("editor.types.json"),icon:"red/images/typedInput/json.svg"},
{value: "bin",label: RED._("editor.types.bin"),icon: "red/images/typedInput/bin.svg"},
{value: "env",label: RED._("editor.types.env"),icon: "red/images/typedInput/env.svg"},
{value: "cred",label: RED._("editor.types.cred"),icon: "fa fa-lock"}
],
default: DEFAULT_ENV_TYPE_LIST,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-i-cursor"></i></span>').appendTo(input);
if (value.length) {
value.forEach(function(v) {
if (!/^fa /.test(v.icon)) {
$('<img>',{src:v.icon,style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
} else {
var s = $('<span>',{style:"max-width:14px; padding: 0 3px; margin-top:-4px; margin-left: 1px"}).appendTo(input);
$("<i>",{class: v.icon}).appendTo(s);
}
})
} else {
$('<span class="red-ui-editor-subflow-env-input-type-placeholder"></span>').text(RED._("editor.selectType")).appendTo(input);
}
}
},
{
value: "cred",
label: RED._("typedInput.type.cred"), icon:"fa fa-lock", showLabel: false,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type">').css({
"border-top-right-radius": "4px",
"border-bottom-right-radius": "4px"
}).appendTo(container);
$('<div class="placeholder-input">').html("&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;").appendTo(innerContainer);
}
},
{
value:"select",
label:RED._("editor.inputs.select"), icon:"fa fa-tasks",showLabel:false,
valueLabel: function(container,value) {
container.css("padding","0");
selectBox = $('<select></select>').appendTo(container);
if (ui.opts && Array.isArray(ui.opts.opts)) {
ui.opts.opts.forEach(function(o) {
var label = lookupLabel(o.l, o.l["en-US"]||o.v, currentLocale);
// $('<option>').val((o.t||'str')+":"+o.v).text(label).appendTo(selectBox);
$('<option>').val(o.v).text(label).appendTo(selectBox);
})
}
selectBox.on('change', function(evt) {
var v = selectBox.val();
// var parts = v.split(":");
// var t = parts.shift();
// v = parts.join(":");
//
// valueField.typedInput("type",'str')
valueField.typedInput("value",v)
});
selectBox.val(valueField.typedInput("value"));
// selectBox.val(valueField.typedInput('type')+":"+valueField.typedInput("value"));
},
expand: {
icon: "fa-caret-down",
minWidth: 400,
content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
var optList = $('<ol>').appendTo(content).editableList({
header:$("<div><div>"+RED._("editor.select.label")+"</div><div>"+RED._("editor.select.value")+"</div></div>"),
addItem: function(row,index,itemData) {
var labelDiv = $('<div>').appendTo(row);
var label = lookupLabel(itemData.l, "", currentLocale);
itemData.label = $('<input type="text">').val(label).appendTo(labelDiv);
itemData.label.on('keydown', function(evt) {
if (evt.keyCode === 13) {
itemData.input.focus();
evt.preventDefault();
}
});
var labelIcon = $('<span class="red-ui-editor-subflow-env-lang-icon"><i class="fa fa-language"></i></span>').appendTo(labelDiv);
RED.popover.tooltip(labelIcon,function() {
return currentLocale;
})
itemData.input = $('<input type="text">').val(itemData.v).appendTo(row);
// Problem using a TI here:
// - this is in a popout panel
// - clicking the expand button in the TI will close the parent edit tray
// and open the type editor.
// - but it leaves the popout panel over the top.
// - there is no way to get back to the popout panel after closing the type editor
//.typedInput({default:itemData.t||'str', types:DEFAULT_ENV_TYPE_LIST});
itemData.input.on('keydown', function(evt) {
if (evt.keyCode === 13) {
// Enter or Tab
var index = optList.editableList('indexOf',itemData);
var length = optList.editableList('length');
if (index + 1 === length) {
var newItem = {};
optList.editableList('addItem',newItem);
setTimeout(function() {
if (newItem.label) {
newItem.label.focus();
}
},100)
} else {
var nextItem = optList.editableList('getItemAt',index+1);
if (nextItem.label) {
nextItem.label.focus()
}
}
evt.preventDefault();
}
});
},
sortable: true,
removable: true,
height: 160
})
if (ui.opts.opts.length > 0) {
ui.opts.opts.forEach(function(o) {
optList.editableList('addItem',$.extend(true,{},o))
})
} else {
optList.editableList('addItem',{})
}
return {
onclose: function() {
var items = optList.editableList('items');
var vals = [];
items.each(function (i,el) {
var data = el.data('data');
var l = data.label.val().trim();
var v = data.input.val();
// var t = data.input.typedInput('type');
// var v = data.input.typedInput('value');
if (l.length > 0) {
data.l = data.l || {};
data.l[currentLocale] = l;
}
data.v = v;
if (l.length > 0 || v.length > 0) {
var val = {l:data.l,v:data.v};
// if (t !== 'str') {
// val.t = t;
// }
vals.push(val);
}
});
ui.opts.opts = vals;
inputCellInput.typedInput('value',Date.now())
}
}
}
}
},
{
value:"checkbox",
label:RED._("editor.inputs.checkbox"), icon:"fa fa-check-square-o",showLabel:false,
valueLabel: function(container,value) {
container.css("padding",0);
checkbox = $('<input type="checkbox">').appendTo(container);
checkbox.on('change', function(evt) {
valueField.typedInput('value',$(this).prop('checked')?"true":"false");
})
checkbox.prop('checked',valueField.typedInput('value')==="true");
}
},
{
value:"spinner",
label:RED._("editor.inputs.spinner"), icon:"fa fa-sort-numeric-asc", showLabel:false,
valueLabel: function(container,value) {
container.css("padding",0);
var innerContainer = $('<div class="red-ui-editor-subflow-env-input-type"></div>').appendTo(container);
var input = $('<div class="placeholder-input">').appendTo(innerContainer);
$('<span><i class="fa fa-sort-numeric-asc"></i></span>').appendTo(input);
var min = ui.opts && ui.opts.min;
var max = ui.opts && ui.opts.max;
var label = "";
if (min !== undefined && max !== undefined) {
label = Math.min(min,max)+" - "+Math.max(min,max);
} else if (min !== undefined) {
label = "> "+min;
} else if (max !== undefined) {
label = "< "+max;
}
$('<span>').css("margin-left","15px").text(label).appendTo(input);
},
expand: {
icon: "fa-caret-down",
content: function(container) {
var content = $('<div class="red-ui-editor-subflow-ui-edit-panel">').appendTo(container);
content.css("padding","8px 5px")
var min = ui.opts.min;
var max = ui.opts.max;
var minInput = $('<input type="number" style="margin-bottom:0; width:60px">');
minInput.val(min);
var maxInput = $('<input type="number" style="margin-bottom:0; width:60px">');
maxInput.val(max);
$('<div class="form-row" style="margin-bottom:3px"><label>'+RED._("editor.spinner.min")+'</label></div>').append(minInput).appendTo(content);
$('<div class="form-row" style="margin-bottom:0"><label>'+RED._("editor.spinner.max")+'</label></div>').append(maxInput).appendTo(content);
return {
onclose: function() {
var min = minInput.val().trim();
var max = maxInput.val().trim();
if (min !== "") {
ui.opts.min = parseInt(min);
} else {
delete ui.opts.min;
}
if (max !== "") {
ui.opts.max = parseInt(max);
} else {
delete ui.opts.max;
}
inputCellInput.typedInput('value',Date.now())
}
}
}
}
},
{
value:"none",
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
},
{
value:"hide",
label:RED._("editor.inputs.hidden"), icon:"fa fa-ban",hasValue:false
}
],
default: 'none'
}).on("typedinputtypechange", function(evt,type) {
ui.type = $(this).typedInput("type");
ui.opts = typeOptions[ui.type];
if (ui.type === 'input') {
// In the case of 'input' type, the typedInput uses the multiple-option
// mode. Its value needs to be set to a comma-separately list of the
// selected options.
inputCellInput.typedInput('value',ui.opts.types.join(","))
} else {
// No other type cares about `value`, but doing this will
// force a refresh of the label now that `ui.opts` has
// been updated.
inputCellInput.typedInput('value',Date.now())
}
switch (ui.type) {
case 'input':
valueField.typedInput('types',ui.opts.types);
break;
case 'select':
valueField.typedInput('types',['str']);
break;
case 'checkbox':
valueField.typedInput('types',['bool']);
break;
case 'spinner':
valueField.typedInput('types',['num']);
break;
case 'cred':
valueField.typedInput('types',['cred']);
break;
default:
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
}
if (ui.type === 'checkbox') {
valueField.typedInput('type','bool');
} else if (ui.type === 'spinner') {
valueField.typedInput('type','num');
}
if (ui.type !== 'checkbox') {
checkbox = null;
}
}).on("change", function(evt,type) {
if (ui.type === 'input') {
var types = inputCellInput.typedInput('value');
ui.opts.types = (types === "") ? ["str"] : types.split(",");
valueField.typedInput('types',ui.opts.types);
}
});
valueField.on("change", function(evt) {
if (checkbox) {
checkbox.prop('checked',$(this).typedInput('value')==="true")
}
})
// Set the input to the right type. This will trigger the 'typedinputtypechange'
// event handler (just above ^^) to update the value if needed
inputCellInput.typedInput('type',ui.type)
}
function buildEnvUIRow(row, tenv, ui, node) {
ui.label = ui.label||{};
@@ -1540,7 +929,7 @@ RED.subflow = (function() {
ui.opts = {};
} else if (!ui.type) {
ui.type = "input";
ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
} else {
if (!ui.opts) {
ui.opts = (ui.type === "select") ? {opts:[]} : {};
@@ -1549,7 +938,7 @@ RED.subflow = (function() {
var labels = ui.label || {};
var locale = RED.i18n.lang();
var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, locale);
var labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"]||tenv.name, locale);
var label = $('<label>').appendTo(row);
$('<span>&nbsp;</span>').appendTo(row);
var labelContainer = $('<span></span>').appendTo(label);
@@ -1602,7 +991,7 @@ RED.subflow = (function() {
input = $('<select>').css('width','70%').appendTo(row);
if (ui.opts.opts) {
ui.opts.opts.forEach(function(o) {
$('<option>').val(o.v).text(lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
})
}
input.val(val.value);
@@ -1668,9 +1057,8 @@ RED.subflow = (function() {
* @param uiContainer - container for UI
* @param envList - env var definitions of template
*/
function buildEnvUI(uiContainer, envList,node) {
function buildEnvUI(uiContainer, envList, node) {
uiContainer.empty();
var elementID = 0;
for (var i = 0; i < envList.length; i++) {
var tenv = envList[i];
if (tenv.ui && tenv.ui.type === 'hide') {
@@ -1678,8 +1066,6 @@ RED.subflow = (function() {
}
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
buildEnvUIRow(row,tenv, tenv.ui || {}, node);
// console.log(ui);
}
}
// buildEnvUI
@@ -1715,7 +1101,7 @@ RED.subflow = (function() {
// icon: "",
// label: {},
// type: "input",
// opts: {types:DEFAULT_ENV_TYPE_LIST}
// opts: {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST}
// }
if (!ui.icon) {
delete ui.icon;
@@ -1725,7 +1111,7 @@ RED.subflow = (function() {
}
switch (ui.type) {
case "input":
if (JSON.stringify(ui.opts) === JSON.stringify({types:DEFAULT_ENV_TYPE_LIST})) {
if (JSON.stringify(ui.opts) === JSON.stringify({types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST})) {
// This is the default input config. Delete it as it will
// be applied automatically
delete ui.type;
@@ -1841,7 +1227,6 @@ RED.subflow = (function() {
function exportSubflowInstanceEnv(node) {
var env = [];
// First, get the values for the SubflowTemplate defined properties
// - these are the ones with custom UI elements
var parentEnv = getSubflowInstanceParentEnv(node);
@@ -1853,7 +1238,7 @@ RED.subflow = (function() {
ui.type = "cred";
} else {
ui.type = "input";
ui.opts = {types:DEFAULT_ENV_TYPE_LIST}
ui.opts = {types:RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST}
}
} else {
ui.opts = ui.opts || {};
@@ -1893,22 +1278,6 @@ RED.subflow = (function() {
}
}
})
// Second, get the values from the Properties table tab
var items = $('#red-ui-editor-subflow-env-list').editableList('items');
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")
}
if (item.name.trim() !== "") {
env.push(item);
}
}
});
return env;
}
@@ -1916,164 +1285,18 @@ RED.subflow = (function() {
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
}
/**
* Lookup text for specific locale
* @param labels - dict of labels
* @param defaultLabel - fallback label if not found
* @param locale - target locale
* @returns {string} text for specified locale
*/
function lookupLabel(labels, defaultLabel, locale) {
if (labels) {
if (labels[locale]) {
return labels[locale];
}
if (locale) {
var lang = locale.substring(0, 2);
if (labels[lang]) {
return labels[lang];
}
}
}
return defaultLabel;
}
// Called by subflow.oneditprepare for both instances and templates
function buildEditForm(type,node) {
if (type === "subflow-template") {
buildPropertiesList($('#node-input-env-container'), node);
// This is the tabbed UI that offers the env list - with UI options
// plus the preview tab
buildEnvControl($('#node-input-env-container'), node);
RED.editor.envVarList.create($('#node-input-env-container'), node);
} else if (type === "subflow") {
// This gets called by the subflow type `oneditprepare` function
// registered in nodes.js#addSubflow()
// This is the rendered version of the subflow env var list
buildEnvUI($("#subflow-input-ui"), getSubflowInstanceParentEnv(node), node);
}
}
function buildPropertiesForm(node) {
var container = $('#editor-subflow-envProperties-content');
var form = $('<form class="dialog-form form-horizontal"></form>').appendTo(container);
var listContainer = $('<div class="form-row node-input-env-container-row"></div>').appendTo(form);
var list = $('<ol id="red-ui-editor-subflow-env-list" class="red-ui-editor-subflow-env-list"></ol>').appendTo(listContainer);
buildPropertiesList(list, node);
}
function setupInputValidation(input,validator) {
var errorTip;
var validateTimeout;
var validateFunction = function() {
if (validateTimeout) {
return;
}
validateTimeout = setTimeout(function() {
var error = validator(input.val());
// if (!error && errorTip) {
// errorTip.close();
// errorTip = null;
// } else if (error && !errorTip) {
// errorTip = RED.popover.create({
// tooltip: true,
// target:input,
// size: "small",
// direction: "bottom",
// content: error,
// }).open();
// }
input.toggleClass("input-error",!!error);
validateTimeout = null;
})
}
input.on("change keyup paste", validateFunction);
}
function buildModuleForm(container, node) {
$(_subflowModulePaneTemplate).appendTo(container);
var moduleProps = node.meta || {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords',
'license'
].forEach(function(property) {
$("#subflow-input-module-"+property).val(moduleProps[property]||"")
})
$("#subflow-input-module-type").attr("placeholder",node.id);
setupInputValidation($("#subflow-input-module-module"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue.length < 215;
isValid = isValid && !/^[._]/.test(newValue);
isValid = isValid && !/[A-Z]/.test(newValue);
if (newValue !== encodeURIComponent(newValue)) {
var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue);
if (m) {
isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2]))
} else {
isValid = false;
}
}
return isValid?"":"Invalid module name"
})
setupInputValidation($("#subflow-input-module-version"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue === "" ||
/^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue);
return isValid?"":"Invalid version number"
})
var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"];
var typedLicenses = {
types: licenses.map(function(l) {
return {
value: l,
label: l === "none" ? RED._("editor:subflow.licenseNone") : l,
hasValue: false
};
})
}
typedLicenses.types.push({
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
})
if (!moduleProps.license) {
typedLicenses.default = "none";
} else if (licenses.indexOf(moduleProps.license) > -1) {
typedLicenses.default = moduleProps.license;
} else {
typedLicenses.default = "_custom_";
}
$("#subflow-input-module-license").typedInput(typedLicenses)
}
function exportSubflowModuleProperties(node) {
var value;
var moduleProps = {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords'
].forEach(function(property) {
value = $("#subflow-input-module-"+property).val().trim();
if (value) {
moduleProps[property] = value;
}
})
var selectedLicenseType = $("#subflow-input-module-license").typedInput("type");
if (selectedLicenseType === '_custom_') {
value = $("#subflow-input-module-license").val();
if (value) {
moduleProps.license = value;
}
} else if (selectedLicenseType !== "none") {
moduleProps.license = selectedLicenseType;
}
return moduleProps;
}
return {
init: init,
@@ -2085,14 +1308,9 @@ RED.subflow = (function() {
removeOutput: removeSubflowOutput,
removeStatus: removeSubflowStatus,
buildEditForm: buildEditForm,
buildPropertiesForm: buildPropertiesForm,
buildModuleForm: buildModuleForm,
exportSubflowTemplateEnv: exportEnvList,
exportSubflowInstanceEnv: exportSubflowInstanceEnv,
exportSubflowModuleProperties: exportSubflowModuleProperties
exportSubflowInstanceEnv: exportSubflowInstanceEnv
}
})();

View File

@@ -229,7 +229,7 @@ RED.sidebar.config = (function() {
var globalConfigNodes = [];
var configList = {};
RED.nodes.eachConfig(function(cn) {
if (cn.z) {//} == RED.workspaces.active()) {
if (cn.z) {
configList[cn.z.replace(/\./g,"-")] = configList[cn.z.replace(/\./g,"-")]||[];
configList[cn.z.replace(/\./g,"-")].push(cn);
} else if (!cn.z) {

View File

@@ -25,7 +25,6 @@ RED.sidebar.help = (function() {
var tocPanel;
var helpIndex = {};
function resizeStack() {
var h = $(content).parent().height() - toolbar.outerHeight();
panels.resize(h)
@@ -93,9 +92,28 @@ RED.sidebar.help = (function() {
$('<span class="red-ui-help-info-none">'+RED._("sidebar.help.noHelp")+'</span>').appendTo(helpSection);
treeList = $("<div>").css({width: "100%"}).appendTo(tocPanel).treeList({data: []})
var pendingContentLoad;
treeList.on('treelistselect', function(e,item) {
pendingContentLoad = item;
if (item.nodeType) {
showHelp(item.nodeType);
showNodeTypeHelp(item.nodeType);
} else if (item.content) {
helpSection.empty();
if (typeof item.content === "string") {
setInfoText(item.label, item.content);
} else if (typeof item.content === "function") {
if (item.content.length === 0) {
setInfoText(item.label, item.content());
} else {
setInfoText(item.label, '<div class="red-ui-component-spinner red-ui-component-spinner-contain"><img src="red/images/spin.svg" /></div>',helpSection)
item.content(function(content) {
if (pendingContentLoad === item) {
helpSection.empty();
setInfoText(item.label, content);
}
})
}
}
}
})
@@ -174,21 +192,28 @@ RED.sidebar.help = (function() {
var moduleNames = Object.keys(modules);
moduleNames.sort();
var helpData = [{
var nodeHelp = {
label: RED._("sidebar.help.nodeHelp"),
children: [],
expanded: true
}]
}
var helpData = [
{
id: 'changelog',
label: "Node-RED v"+RED.settings.version,
content: getChangelog
},
nodeHelp
]
var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
if (subflows.length > 0) {
helpData[0].children.push({
nodeHelp.children.push({
label: RED._("menu.label.subflows"),
children: []
})
subflows.forEach(function(nodeType) {
var sf = RED.nodes.getType(nodeType);
helpData[0].children[0].children.push({
nodeHelp.children[0].children.push({
id:"node-type:"+nodeType,
nodeType: nodeType,
subflowLabel: sf.label().toLowerCase(),
@@ -218,7 +243,7 @@ RED.sidebar.help = (function() {
nodeTypes.sort(function(A,B) {
return A.nodeType.localeCompare(B.nodeType)
})
helpData[0].children.push({
nodeHelp.children.push({
id: moduleName,
icon: "fa fa-cube",
label: moduleName,
@@ -233,7 +258,7 @@ RED.sidebar.help = (function() {
var div = $('<div>',{class:"red-ui-node-list-item"});
var icon = RED.utils.createNodeIcon(n).appendTo(div);
var label = n.name;
if (!label && n._def.paletteLabel) {
if (!label && n._def && n._def.paletteLabel) {
try {
label = (typeof n._def.paletteLabel === "function" ? n._def.paletteLabel.call(n._def) : n._def.paletteLabel)||"";
} catch (err) {
@@ -244,7 +269,7 @@ RED.sidebar.help = (function() {
return div;
}
function showHelp(nodeType) {
function showNodeTypeHelp(nodeType) {
helpSection.empty();
var helpText;
var title;
@@ -265,7 +290,7 @@ RED.sidebar.help = (function() {
}
}
}
setInfoText(title, helpText, helpSection);
setInfoText(title, helpText);
var ratio = panels.ratio();
if (ratio > 0.7) {
@@ -282,7 +307,7 @@ RED.sidebar.help = (function() {
}
if (type) {
// hideTOC();
showHelp(type);
showNodeTypeHelp(type);
}
resizeStack();
}
@@ -298,11 +323,12 @@ RED.sidebar.help = (function() {
return el;
}
function setInfoText(title, infoText,target) {
function setInfoText(title, infoText) {
helpSection.empty();
if (title) {
$("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(target);
$("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(helpSection);
}
var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(target);
var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(helpSection);
info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
var foldingHeader = "H3";
info.find(foldingHeader).wrapInner('<a class="red-ui-help-info-header expanded" href="#"></a>')
@@ -316,12 +342,12 @@ RED.sidebar.help = (function() {
}
$(this).toggleClass('expanded',!isExpanded);
})
target.parent().scrollTop(0);
helpSection.parent().scrollTop(0);
}
function set(html,title) {
$(helpSection).empty();
setInfoText(title,html,helpSection);
setInfoText(title,html);
hideTOC();
show();
}
@@ -336,13 +362,91 @@ RED.sidebar.help = (function() {
if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports
} else if (node.type !== 'group'){
showHelp(node.type);
showNodeTypeHelp(node.type);
}
}
}
}
RED.events.on("view:selection-changed",refreshSelection);
function getChangelog(done) {
$.get('red/about', function(data) {
// data will be strictly markdown. Any HTML should be escaped.
data = RED.utils.sanitize(data);
RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
var tourHeader = '<div><img width="50px" src="red/images/node-red-icon.svg" /></div>';
if (tour) {
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) {
tourHeader = '<div><button type="button" onclick="RED.actions.invoke(\'core:show-welcome-tour\')" class="red-ui-button">' + RED._("tourGuide.takeATour") + '</button></div>';
}
}
var aboutHeader = '<div style="text-align:center;">'+tourHeader+'</div>'
done(aboutHeader+RED.utils.renderMarkdown(data))
});
});
}
function showAbout() {
treeList.treeList("show","changelog")
treeList.treeList("select","changelog");
show();
}
function showWelcomeTour(lastSeenVersion, done) {
done = done || function() {};
RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
if (err) {
console.warn("Failed to load welcome tour",err);
done()
return;
}
var currentVersionParts = RED.settings.version.split(".");
var tourVersionParts = tour.version.split(".");
// Only display the tour if its MAJ.MIN versions the current version
// This means if we update MAJ/MIN without updating the tour, the old tour won't get shown
if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) {
done()
return;
}
if (lastSeenVersion) {
// Previously displayed a welcome tour.
if (lastSeenVersion === RED.settings.version) {
// Exact match - don't show the tour
done()
return;
}
var lastSeenParts = lastSeenVersion.split(".");
if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) {
// Running an *older* version than last displayed tour.
done()
return;
}
if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) {
if (lastSeenParts.length === 3 && currentVersionParts.length === 3) {
// Matching non-beta MAJ.MIN - don't repeat tour
done()
return;
}
if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) {
// Running an *older* beta than last displayed tour.
done()
return
}
}
}
RED.tourGuide.run("./tours/welcome.js", function(err) {
RED.settings.set("editor.tours.welcome", RED.settings.version)
done()
})
})
}
RED.actions.add("core:show-about", showAbout);
RED.actions.add("core:show-welcome-tour", showWelcomeTour);
return {
init: init,
show: show,

View File

@@ -122,11 +122,20 @@ RED.sidebar.info.outliner = (function() {
})
RED.popover.tooltip(triggerButton,RED._("sidebar.info.triggerAction"));
}
// $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-eye"></i></button>').appendTo(controls).on("click",function(evt) {
// evt.preventDefault();
// evt.stopPropagation();
// RED.view.reveal(n.id);
// })
if (n.type === "tab") {
var toggleVisibleButton = $('<button type="button" class="red-ui-info-outline-item-control-hide red-ui-button red-ui-button-small"><i class="fa fa-eye"></i><i class="fa fa-eye-slash"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
var isHidden = !div.hasClass("red-ui-info-outline-item-hidden");
div.toggleClass("red-ui-info-outline-item-hidden",isHidden);
if (isHidden) {
RED.workspaces.hide(n.id);
} else {
RED.workspaces.show(n.id, null, true);
}
});
}
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) {
evt.preventDefault();
@@ -166,6 +175,7 @@ RED.sidebar.info.outliner = (function() {
n.d = true;
}
n.dirty = true;
n.dirtyStatus = true;
n.changed = true;
RED.events.emit("nodes:change",n);
groupHistoryEvent.events.push(historyEvent);
@@ -194,6 +204,7 @@ RED.sidebar.info.outliner = (function() {
n.d = true;
}
n.dirty = true;
n.dirtyStatus = true;
n.changed = true;
RED.events.emit("nodes:change",n);
RED.history.push(historyEvent);
@@ -263,6 +274,7 @@ RED.sidebar.info.outliner = (function() {
{label:RED._("sidebar.info.search.invalidNodes"), value: "is:invalid"},
{label:RED._("sidebar.info.search.uknownNodes"), value: "type:unknown"},
{label:RED._("sidebar.info.search.unusedSubflows"), value:"is:subflow is:unused"},
{label:RED._("sidebar.info.search.hiddenFlows"), value:"is:hidden"},
]
});
@@ -278,9 +290,11 @@ RED.sidebar.info.outliner = (function() {
var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
if (node) {
if (node.type === 'group' || node._def.category !== "config") {
RED.view.select({nodes:[node]})
// RED.view.select({nodes:[node]})
} else if (node._def.category === "config") {
RED.sidebar.info.refresh(node);
} else {
RED.view.select({nodes:[]})
// RED.view.select({nodes:[]})
}
}
})
@@ -309,18 +323,33 @@ RED.sidebar.info.outliner = (function() {
RED.events.on("nodes:add",onNodeAdd);
RED.events.on("nodes:remove",onObjectRemove);
RED.events.on("nodes:change",onNodeChange);
// RED.events.on("nodes:reorder",onNodesReorder);
RED.events.on("groups:add",onNodeAdd);
RED.events.on("groups:remove",onObjectRemove);
RED.events.on("groups:change",onNodeChange);
RED.events.on("workspace:clear", onWorkspaceClear)
RED.events.on("workspace:show", onWorkspaceShow);
RED.events.on("workspace:hide", onWorkspaceHide);
RED.events.on("workspace:clear", onWorkspaceClear);
return container;
}
function onWorkspaceClear() {
treeList.treeList('data',getFlowData());
}
function onWorkspaceShow(event) {
var existingObject = objects[event.workspace];
if (existingObject) {
existingObject.element.removeClass("red-ui-info-outline-item-hidden")
}
}
function onWorkspaceHide(event) {
var existingObject = objects[event.workspace];
if (existingObject) {
existingObject.element.addClass("red-ui-info-outline-item-hidden")
}
}
function onFlowAdd(ws) {
objects[ws.id] = {
id: ws.id,
@@ -367,6 +396,21 @@ RED.sidebar.info.outliner = (function() {
return indexMap[A.id] - indexMap[B.id]
})
}
// function onNodesReorder(event) {
// //
// var nodes = RED.nodes.getNodeOrder(event.z);
// var indexMap = {};
// nodes.forEach(function(id,index) {
// indexMap[id] = index;
// })
// var existingObject = objects[event.z];
// existingObject.treeList.sortChildren(function(A,B) {
// if (A.children && !B.children) { return -1 }
// if (!A.children && B.children) { return 1 }
// if (A.children && B.children) { return -1 }
// return indexMap[A.id] - indexMap[B.id]
// })
// }
function onSubflowAdd(sf) {
objects[sf.id] = {
id: sf.id,
@@ -522,7 +566,7 @@ RED.sidebar.info.outliner = (function() {
}
}
function getGutter(n) {
var span = $("<span>",{class:"red-ui-info-outline-gutter"});
var span = $("<span>",{class:"red-ui-info-outline-gutter red-ui-treeList-gutter-float"});
var revealButton = $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-search"></i></button>').appendTo(span).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();

View File

@@ -180,6 +180,10 @@ RED.sidebar.info = (function() {
if (node === null) {
RED.sidebar.info.outliner.select(null);
propertiesPanelHeaderIcon.empty();
propertiesPanelHeaderLabel.text("");
propertiesPanelHeaderReveal.hide();
propertiesPanelHeaderHelp.hide();
return;
} else if (Array.isArray(node)) {
// Multiple things selected

View File

@@ -0,0 +1,441 @@
RED.tourGuide = (function() {
var activeListeners = [];
var shade;
var focus;
var popover;
var stepContent;
var targetElement;
var fullscreen;
var tourCache = {};
function run(tourPath, done) {
done = done || function(err) {
if (err) {
console.error(err);
}
};
loadTour(tourPath, function(err, tour) {
if (err) {
console.warn("Error loading tour:",err);
return;
}
runTour(tour, done);
})
}
function loadTour(tourPath, done) {
if (tourCache[tourPath]) {
done(null, tourCache[tourPath]);
} else {
/* jshint ignore:start */
// jshint<2.13 doesn't support dynamic imports. Once grunt-contrib-jshint
// has been updated with the new jshint, we can stop ignoring this block
import(tourPath).then(function(module) {
tourCache[tourPath] = module.default;
done(null, tourCache[tourPath]);
}).catch(function(err) {
done(err);
})
/* jshint ignore:end */
}
}
function repositionFocus() {
if (targetElement) {
if (!fullscreen) {
var pos = targetElement[0].getBoundingClientRect();
var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5);
focus.css({
left: (pos.left+pos.width/2)+"px",
top: (pos.top+pos.height/2)+"px",
width: (2*dimension)+"px",
height: (2*dimension)+"px"
})
var flush = focus[0].offsetHeight; // Flush CSS changes
focus.addClass("transition");
focus.css({
width: dimension+"px",
height: dimension+"px"
})
} else {
focus.css({
left: ($(window).width()/2)+"px",
top: ($(window).height()/2)+"px",
width: "0px",
height: "0px"
})
}
if (popover) {
popover.move({
target: targetElement,
})
}
}
}
function runTour(tour, done) {
shade = $('<div class="red-ui-tourGuide-shade"></div>').appendTo(document.body);
focus = $('<div class="red-ui-tourGuide-shade-focus"></div>').appendTo(shade);
// var resizeTimer;
//
$(window).on("resize.red-ui-tourGuide", function() {
repositionFocus();
})
var i = 0;
var state = {
index: 0,
count: tour.steps.length
};
function endTour(err) {
$(window).off("resize.red-ui-tourGuide");
$(document).off('keydown.red-ui-tourGuide');
if (popover) {
popover.close();
}
stepContent = null;
popover = null;
shade.remove();
shade = null;
done(err);
}
function runStep(carryOn) {
if (carryOn === false) {
endTour(false);
return;
}
if (i === tour.steps.length) {
endTour();
return
}
state.index = i;
// console.log("TOUR STEP",i+1,"OF",tour.steps.length)
try {
runTourStep(tour.steps[i++], state, runStep)
} catch(err) {
endTour(err);
return;
}
}
runStep();
}
function clearListeners() {
activeListeners.forEach(function(listener) {
if (listener.type === "dom-event") {
listener.target[0].removeEventListener(listener.event,listener.listener,listener.opts);
} else if (listener.type === "nr-event") {
RED.events.off(listener.event, listener.listener)
}
})
activeListeners = [];
}
function prepareStep(step, state, done) {
if (step.prepare) {
if (step.prepare.length === 0) {
step.prepare.call(state);
} else {
if (popover) {
popover.element.hide();
if (!fullscreen) {
fullscreen = true;
repositionFocus()
}
}
step.prepare.call(state, function() {
if (popover) {
popover.element.show();
}
done();
})
return;
}
}
done();
}
function completeStep(step, state, done) {
function finish() {
clearListeners();
setTimeout(function() {
done();
},0)
}
if (step.complete) {
if (step.complete.length === 0) {
step.complete.call(state);
} else {
if (popover) {
popover.element.hide();
if (!fullscreen) {
fullscreen = true;
repositionFocus()
}
}
step.complete.call(state, function() {
if (popover) {
popover.element.show();
}
finish();
})
return;
}
}
finish();
}
function getLocaleText(property) {
if (typeof property === 'string') {
return property;
}
var currentLang = RED.i18n.lang() || 'en-US';
var availableLangs = Object.keys(property);
return property[currentLang]||property['en-US']||property[availableLangs[0]]
}
function runTourStep(step, state, done) {
shade.fadeIn();
prepareStep(step, state, function() {
var zIndex;
var direction = step.direction || "bottom";
fullscreen = false;
if (typeof step.element === "string") {
targetElement = $(step.element)
} else if (typeof step.element === "function") {
targetElement = step.element.call(state);
} else if (!step.element) {
targetElement = $(".red-ui-editor")
fullscreen = true;
direction = "inset";
} else {
targetElement = step.element;
}
if (targetElement.length === 0) {
targetElement = null;
shade.hide();
throw new Error("Element not found")
}
if ($(window).width() < 400) {
targetElement = $(".red-ui-editor");
fullscreen = true;
direction = "inset";
}
zIndex = targetElement.css("z-index");
if (!fullscreen && (step.interactive || step.wait)) {
targetElement.css("z-index",2002);
}
repositionFocus();
if (!stepContent) {
stepContent = $('<div style="position:relative"></div>');
} else {
stepContent.empty();
}
$('<button type="button" class="red-ui-button red-ui-button-small" style="float: right; margin-top: -4px; margin-right: -4px;"><i class="fa fa-times"></i></button>').appendTo(stepContent).click(function(evt) {
evt.preventDefault();
completeStep(step, state, function() {
done(false);
});
})
var stepDescription = $('<div class="red-ui-tourGuide-popover-description"></div>').appendTo(stepContent);
if (step.titleIcon) {
$('<h2><i class="'+step.titleIcon+'"></i></h2>').appendTo(stepDescription);
}
if (step.title) {
$('<h2>').text(getLocaleText(step.title)).appendTo(stepDescription);
}
$('<div>').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription);
var stepToolbar = $('<div>',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent);
// var breadcrumbs = $('<div>',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
// var bcStart = Math.max(0,state.index - 3);
// var bcEnd = Math.min(state.count, bcStart + 7);
// if (bcEnd === state.count) {
// bcStart = Math.max(0,bcEnd - 7);
// }
// for (var i = bcStart; i < bcEnd; i++) {
// var bullet = $('<i class="fa"></i>').addClass(i===state.index ? "fa-circle":"fa-circle-o").appendTo(breadcrumbs);
// if (i === bcStart) {
// if (i > 1) {
// bullet.css("font-size", "3px");
// } else if (i === 1) {
// bullet.css("font-size", "4px");
// }
// } else if (i === bcStart + 1) {
// if (i > 2) {
// bullet.css("font-size", "4px");
// }
// }
// if (i === bcEnd - 1) {
// if (i < state.count - 2) {
// bullet.css("font-size", "3px");
// } else if (i === state.count - 2) {
// bullet.css("font-size", "4px");
// }
// } else if (i === bcEnd - 2) {
// if (i < state.count - 3) {
// bullet.css("font-size", "4px");
// }
// }
// // if (i === bcEnd - 1) {
// // if (i < state.count - 2) {
// // bullet.css("font-size", "3px");
// // } else if (i === state.count - 2) {
// // bullet.css("font-size", "4px");
// // }
// // }
// }
$('<small>').text((state.index+1)+"/"+state.count).appendTo(stepToolbar)
var nextButton;
if (fullscreen || !step.wait) {
nextButton = $('<button type="button" class="red-ui-button" style="position: absolute; right:0;bottom:0;"></button>').appendTo(stepToolbar).one('click',function(evt) {
evt.preventDefault();
stepEventListener();
});
if (state.index === state.count - 1) {
$('<span></span>').text(RED._("common.label.close")).appendTo(nextButton);
} else if (state.index === 0) {
$('<span>start</span>').text(RED._("tourGuide.start")).appendTo(nextButton);
$('<span style="margin-left: 6px"><i class="fa fa-chevron-right"></i></span>').appendTo(nextButton);
} else if (state.index < state.count-1) {
$('<span></span>').text(RED._("tourGuide.next")).appendTo(nextButton);
$('<span style="margin-left: 6px"><i class="fa fa-chevron-right"></i></span>').appendTo(nextButton);
}
}
var width = step.width;
if (fullscreen) {
width = 500;
}
var maxWidth = Math.min($(window).width()-10,Math.max(width || 0, 300));
if (!popover) {
popover = RED.popover.create({
target: targetElement,
width: width || "auto",
maxWidth: maxWidth+"px",
direction: direction,
class: "red-ui-tourGuide-popover"+(fullscreen?" ":""),
trigger: "manual",
content: stepContent
}).open();
}
$(document).off('keydown.red-ui-tourGuide');
$(document).on('keydown.red-ui-tourGuide', function(evt) {
if (evt.key === "Escape" || evt.key === "Esc") {
evt.preventDefault();
evt.stopPropagation();
completeStep(step, state, function() {
done(false);
});
}
})
popover.element.toggleClass("red-ui-tourGuide-popover-full",!!fullscreen);
popover.move({
target: targetElement,
width: width || "auto",
maxWidth: maxWidth+"px",
direction: direction,
})
setTimeout(function() {
var pos = popover.element.position()
if (pos.left < 0) {
popover.element.css({left: 0});
}
},100);
if (nextButton) {
setTimeout(function() {
nextButton.focus();
},100);
}
var isSVG = targetElement[0] instanceof SVGElement;
if (step.fallback) {
focus.one("mouseenter", function(evt) {
setTimeout(function() {
var pos = targetElement[0].getBoundingClientRect();
var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5);
focus.css({
width: (4*dimension)+"px",
height: (4*dimension)+"px"
})
shade.fadeOut();
popover.move({
target: $(".red-ui-editor"),
direction: step.fallback,
offset: 10,
transition: true
})
// popover.element.addClass('red-ui-tourGuide-popover-bounce');
},isSVG?0:500);
})
}
var stepEventListener = function() {
focus.removeClass("transition");
targetElement.css("z-index",zIndex);
completeStep(step, state, done);
}
if (step.wait) {
if (step.wait.type === "dom-event") {
var eventTarget = targetElement;
if (step.wait.element) {
if (typeof step.wait.element === "string") {
eventTarget = $(step.wait.element);
} else if (typeof step.wait.element === "function") {
eventTarget = step.wait.element.call(state);
}
}
var listener = {
type: step.wait.type,
target: eventTarget,
event: step.wait.event,
listener: function() {
stepEventListener();
},
opts: { once: true }
}
activeListeners.push(listener)
eventTarget[0].addEventListener(listener.event,listener.listener,listener.opts)
} else if (step.wait.type === "nr-event") {
var listener = {
type: step.wait.type,
event: step.wait.event,
listener: function() {
if (step.wait.filter) {
if (!step.wait.filter.apply(state,arguments)) {
return;
}
}
stepEventListener();
}
}
activeListeners.push(listener);
RED.events.on(listener.event,listener.listener);
}
}
})
}
return {
load: loadTour,
run: run,
reset: function() {
RED.settings.set("editor.tours.welcome",'');
}
}
})();

View File

@@ -260,6 +260,9 @@
showTray(options);
},250)
} else {
if (stack.length > 0) {
stack[stack.length-1].tray.css("z-index", 0);
}
RED.events.emit("editor:open");
showTray(options);
}
@@ -301,7 +304,7 @@
oldTray.tray.css({right:0});
if (oldTray.options.show) {
raiseTrayZ();
handleWindowResize();//cause call to monaco layout
handleWindowResize();//cause call to monaco layout
oldTray.options.show();
}
},0);
@@ -322,6 +325,8 @@
$(".red-ui-sidebar-shade").hide();
RED.events.emit("editor:close");
RED.view.focus();
} else {
stack[stack.length-1].tray.css("z-index", "auto");
}
},250)
}

View File

@@ -139,7 +139,8 @@ RED.userSettings = (function() {
{
title: "menu.label.other",
options: [
{setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"}
{setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"},
{setting:"view-show-welcome-tours",label:"menu.label.showWelcomeTours",toggle:true,default:true}
]
}
];

View File

@@ -22,8 +22,82 @@ RED.utils = (function() {
return renderMarkdown(txt);
}
_marked.setOptions({
renderer: new _marked.Renderer(),
const descriptionList = {
name: 'descriptionList',
level: 'block', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:[^:\n]/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
if (!src) { return null; }
const rule = /^(?::[^:\n]+:[^:\n]*(?:\n|$))+/; // Regex for the complete token
const match = rule.exec(src);
if (match) {
return { // Token to generate
type: 'descriptionList', // Should match "name" above
raw: match[0], // Text to consume from the source
text: match[0].trim(), // Additional custom properties
tokens: this.lexer.inlineTokens(match[0].trim()) // inlineTokens to process **bold**, *italics*, etc.
};
}
},
renderer(token) {
return `<dl class="message-properties">${this.parser.parseInline(token.tokens)}\n</dl>`; // parseInline to turn child tokens into HTML
}
};
const description = {
name: 'description',
level: 'inline', // Is this a block-level or inline-level tokenizer?
start(src) {
if (!src) { return null; }
let m = src.match(/:/g);
return m && m.index; // Hint to Marked.js to stop and check for a match
},
tokenizer(src, tokens) {
if (!src) { return null; }
const rule = /^:([^:\n]+)\(([^:\n]+)\).*?:([^:\n]*)(?:\n|$)/; // Regex for the complete token
const match = rule.exec(src);
if (match) {
return { // Token to generate
type: 'description', // Should match "name" above
raw: match[0], // Text to consume from the source
dt: this.lexer.inlineTokens(match[1].trim()), // Additional custom properties
types: this.lexer.inlineTokens(match[2].trim()),
dd: this.lexer.inlineTokens(match[3].trim()),
};
}
},
renderer(token) {
return `\n<dt>${this.parser.parseInline(token.dt)}<span class="property-type">${this.parser.parseInline(token.types)}</span></dt><dd>${this.parser.parseInline(token.dd)}</dd>`;
},
childTokens: ['dt', 'dd'], // Any child tokens to be visited by walkTokens
walkTokens(token) { // Post-processing on the completed token tree
if (token.type === 'strong') {
token.text += ' walked';
}
}
};
const renderer = new window._marked.Renderer();
//override list creation - add node-ports to order lists
renderer.list = function (body, ordered, start) {
let addClass = /dl.*?class.*?message-properties.*/.test(body);
if (addClass && ordered) {
return '<ol class="node-ports">' + body + '</ol>';
} else if (ordered) {
return '<ol>' + body + '</ol>';
} else {
return '<ul>' + body + '</ul>'
}
}
window._marked.setOptions({
renderer: renderer,
gfm: true,
tables: true,
breaks: false,
@@ -32,6 +106,8 @@ RED.utils = (function() {
smartypants: false
});
window._marked.use({extensions: [descriptionList, description] } );
function renderMarkdown(txt) {
var rendered = _marked(txt);
var cleaned = DOMPurify.sanitize(rendered, {SAFE_FOR_JQUERY: true})
@@ -58,10 +134,16 @@ RED.utils = (function() {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('buffer['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'set' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('set['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'map' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('map');
} else if (value.hasOwnProperty('type') && value.type === 'function') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
} else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
} else if (value.hasOwnProperty('type') && value.type === 'regexp') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-string"></span>').text(value.data);
} else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
}
@@ -350,7 +432,7 @@ RED.utils = (function() {
var isArray = Array.isArray(obj);
var isArrayObject = false;
if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__enc__ && obj.type === 'array') || obj.type === 'Buffer')) {
if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__enc__ && obj.type === 'set') || (obj.__enc__ && obj.type === 'array') || obj.type === 'Buffer')) {
isArray = true;
isArrayObject = true;
}
@@ -360,6 +442,8 @@ RED.utils = (function() {
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
} else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
} else if (typeHint === "regexp" || (obj.__enc__ && obj.type === 'regexp')) {
e = $('<span class="red-ui-debug-msg-type-string red-ui-debug-msg-object-header"></span>').text((typeof obj === "string")?obj:obj.data).appendTo(entryObj);
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);
} else if (typeHint === "internal" || (obj.__enc__ && obj.type === 'internal')) {
@@ -417,7 +501,7 @@ RED.utils = (function() {
}
var fullLength = data.length;
if (originalLength > 0) {
if (originalLength > 0) {
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').prependTo(header);
var arrayRows = $('<div class="red-ui-debug-msg-array-rows"></div>').appendTo(element);
element.addClass('red-ui-debug-msg-buffer-raw');
@@ -483,7 +567,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -512,7 +597,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -532,12 +618,18 @@ RED.utils = (function() {
}
} else if (typeof obj === 'object') {
element.addClass('collapsed');
var keys = Object.keys(obj);
var data = obj;
var type = "object";
if (data.__enc__) {
data = data.data;
type = obj.type.toLowerCase();
}
var keys = Object.keys(data);
if (key || keys.length > 0) {
$('<i class="fa fa-caret-right red-ui-debug-msg-object-handle"></i> ').prependTo(header);
makeExpandable(header, function() {
if (!key) {
$('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text('object').appendTo(header);
$('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-type-header"></span>').text(type).appendTo(header);
}
for (i=0;i<keys.length;i++) {
var row = $('<div class="red-ui-debug-msg-object-entry collapsed"></div>').appendTo(element);
@@ -550,7 +642,7 @@ RED.utils = (function() {
}
}
subElements[newPath] = buildMessageElement(
obj[keys[i]],
data[keys[i]],
{
key: keys[i],
typeHint: false,
@@ -561,7 +653,8 @@ RED.utils = (function() {
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi,
tools: tools
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -573,7 +666,7 @@ RED.utils = (function() {
checkExpanded(strippedKey,expandPaths));
}
if (key) {
$('<span class="red-ui-debug-msg-type-meta"></span>').text('object').appendTo(entryObj);
$('<span class="red-ui-debug-msg-type-meta"></span>').text(type).appendTo(entryObj);
} else {
headerHead = $('<span class="red-ui-debug-msg-object-header"></span>').appendTo(entryObj);
$('<span>{ </span>').appendTo(headerHead);
@@ -581,7 +674,7 @@ RED.utils = (function() {
for (i=0;i<keysLength;i++) {
$('<span class="red-ui-debug-msg-object-key"></span>').text(keys[i]).appendTo(headerHead);
$('<span>: </span>').appendTo(headerHead);
buildMessageSummaryValue(obj[keys[i]]).appendTo(headerHead);
buildMessageSummaryValue(data[keys[i]]).appendTo(headerHead);
if (i < keysLength-1) {
$('<span>, </span>').appendTo(headerHead);
}
@@ -1062,7 +1155,7 @@ RED.utils = (function() {
payload = Infinity;
} else if ((format === 'number') && (payload === "-Infinity")) {
payload = -Infinity;
} else if (format === 'Object' || /^array/.test(format) || format === 'boolean' || format === 'number' ) {
} else if (format === 'Object' || /^(array|set|map)/.test(format) || format === 'boolean' || format === 'number' ) {
payload = JSON.parse(payload);
} else if (/error/i.test(format)) {
payload = JSON.parse(payload);
@@ -1268,13 +1361,13 @@ RED.utils = (function() {
r.os = /Windows NT 10/.test(ua) ? "win10" : /Windows NT 6\.0/.test(ua) ? "winvista" : /Windows NT 6\.1/.test(ua) ? "win7" : /Windows NT 6\.\d/.test(ua) ? "win8" : /Windows NT 5\.1/.test(ua) ? "winxp" : /Windows NT [1-5]\./.test(ua) ? "winnt" : /Mac/.test(ua) ? "mac" : /Linux/.test(ua) ? "linux" : /X11/.test(ua) ? "nix" : "";
r.touch = 'ontouchstart' in document.documentElement;
r.mobile = /IEMobile|Windows Phone|Lumia/i.test(ua) ? 'w' : /iPhone|iP[oa]d/.test(ua) ? 'i' : /Android/.test(ua) ? 'a' : /BlackBerry|PlayBook|BB10/.test(ua) ? 'b' : /Mobile Safari/.test(ua) ? 's' : /webOS|Mobile|Tablet|Opera Mini|\bCrMo\/|Opera Mobi/i.test(ua) ? 1 : 0;
r.tablet = /Tablet|iPad/i.test(ua);
r.tablet = /Tablet|iPad/i.test(ua);
r.ie = /MSIE \d|Trident.*rv:/.test(navigator.userAgent);
r.android = /android/i.test(navigator.userAgent);
} catch (error) { }
return r;
}
return {
createObjectElement: buildMessageElement,
getMessageProperty: getMessageProperty,

View File

@@ -0,0 +1,151 @@
RED.view.annotations = (function() {
var annotations = {};
function init() {
RED.hooks.add("viewRedrawNode.annotations", function(evt) {
try {
if (evt.node.__pendingAnnotation__) {
addAnnotation(evt.node.__pendingAnnotation__,evt);
delete evt.node.__pendingAnnotation__;
}
var badgeDX = 0;
var controlDX = 0;
for (var i=0,l=evt.el.__annotations__.length;i<l;i++) {
var annotation = evt.el.__annotations__[i];
if (annotations.hasOwnProperty(annotation.id)) {
var opts = annotations[annotation.id];
var showAnnotation = true;
var isBadge = opts.type === 'badge';
if (opts.show !== undefined) {
if (typeof opts.show === "string") {
showAnnotation = !!evt.node[opts.show]
} else if (typeof opts.show === "function"){
showAnnotation = opts.show(evt.node)
} else {
showAnnotation = !!opts.show;
}
annotation.element.classList.toggle("hide", !showAnnotation);
}
if (isBadge) {
if (showAnnotation) {
var rect = annotation.element.getBoundingClientRect();
badgeDX += rect.width;
annotation.element.setAttribute("transform", "translate("+(evt.node.w-3-badgeDX)+", -8)");
badgeDX += 4;
}
} else {
if (showAnnotation) {
var rect = annotation.element.getBoundingClientRect();
annotation.element.setAttribute("transform", "translate("+(3+controlDX)+", -12)");
controlDX += rect.width + 4;
}
}
} else {
annotation.element.parentNode.removeChild(annotation.element);
evt.el.__annotations__.splice(i,1);
i--;
l--;
}
}
}catch(err) {
console.log(err)
}
});
}
/**
* Register a new node annotation
* @param {string} id - unique identifier
* @param {type} opts - annotations options
*
* opts: {
* type: "badge"
* class: "",
* element: function(node),
* show: string|function(node),
* filter: function(node) -> boolean
* }
*/
function register(id, opts) {
if (opts.type !== 'badge') {
throw new Error("Unsupported annotation type: "+opts.type);
}
annotations[id] = opts
RED.hooks.add("viewAddNode.annotation-"+id, function(evt) {
if (opts.filter && !opts.filter(evt.node)) {
return;
}
addAnnotation(id,evt);
});
var nodes = RED.view.getActiveNodes();
nodes.forEach(function(n) {
n.__pendingAnnotation__ = id;
})
RED.view.redraw();
}
function addAnnotation(id,evt) {
var opts = annotations[id];
evt.el.__annotations__ = evt.el.__annotations__ || [];
var annotationGroup = document.createElementNS("http://www.w3.org/2000/svg","g");
annotationGroup.setAttribute("class",opts.class || "");
evt.el.__annotations__.push({
id:id,
element: annotationGroup
});
var annotation = opts.element(evt.node);
if (opts.tooltip) {
annotation.addEventListener("mouseenter", getAnnotationMouseEnter(annotation,evt.node,opts.tooltip));
annotation.addEventListener("mouseleave", annotationMouseLeave);
}
annotationGroup.appendChild(annotation);
evt.el.appendChild(annotationGroup);
}
function unregister(id) {
delete annotations[id]
RED.hooks.remove("*.annotation-"+id);
RED.view.redraw();
}
var badgeHoverTimeout;
var badgeHover;
function getAnnotationMouseEnter(annotation,node,tooltip) {
return function() {
var text = typeof tooltip === "function"?tooltip(node):tooltip;
if (text) {
clearTimeout(badgeHoverTimeout);
badgeHoverTimeout = setTimeout(function() {
var pos = RED.view.getElementPosition(annotation);
var rect = annotation.getBoundingClientRect();
badgeHoverTimeout = null;
badgeHover = RED.view.showTooltip(
(pos[0]+rect.width/2),
(pos[1]),
text,
"top"
);
},500);
}
}
}
function annotationMouseLeave() {
clearTimeout(badgeHoverTimeout);
if (badgeHover) {
badgeHover.remove();
badgeHover = null;
}
}
return {
init: init,
register:register,
unregister:unregister
}
})();

View File

@@ -159,15 +159,15 @@ RED.view.tools = (function() {
nodes.forEach(function(n) {
var modified = false;
var oldValue = n.l === undefined?true:n.l;
var isLink = /^link (in|out)$/.test(n._def.type);
var showLabel = n._def.hasOwnProperty("showLabel")?n._def.showLabel:true;
if (labelShown) {
if (n.l === false || (isLink && !n.hasOwnProperty('l'))) {
if (n.l === false || (!showLabel && !n.hasOwnProperty('l'))) {
n.l = true;
modified = true;
}
} else {
if ((!isLink && (!n.hasOwnProperty('l') || n.l === true)) || (isLink && n.l === true) ) {
if ((showLabel && (!n.hasOwnProperty('l') || n.l === true)) || (!showLabel && n.l === true) ) {
n.l = false;
modified = true;
}
@@ -427,18 +427,393 @@ RED.view.tools = (function() {
}
}
}
}
function alignSelectionToEdge(direction) {
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length > 1) {
var changedNodes = [];
var bounds = {
minX: Number.MAX_SAFE_INTEGER,
minY: Number.MAX_SAFE_INTEGER,
maxX: Number.MIN_SAFE_INTEGER,
maxY: Number.MIN_SAFE_INTEGER
}
selection.nodes.forEach(function(n) {
if (n.type === "group") {
bounds.minX = Math.min(bounds.minX, n.x);
bounds.minY = Math.min(bounds.minY, n.y);
bounds.maxX = Math.max(bounds.maxX, n.x + n.w);
bounds.maxY = Math.max(bounds.maxY, n.y + n.h);
} else {
bounds.minX = Math.min(bounds.minX, n.x - n.w/2);
bounds.minY = Math.min(bounds.minY, n.y - n.h/2);
bounds.maxX = Math.max(bounds.maxX, n.x + n.w/2);
bounds.maxY = Math.max(bounds.maxY, n.y + n.h/2);
}
});
bounds.midX = bounds.minX + (bounds.maxX - bounds.minX)/2;
bounds.midY = bounds.minY + (bounds.maxY - bounds.minY)/2;
selection.nodes.forEach(function(n) {
var targetX;
var targetY;
var isGroup = n.type==="group";
switch(direction) {
case 'top':
targetX = n.x;
targetY = bounds.minY + (isGroup?0:(n.h/2));
break;
case 'bottom':
targetX = n.x;
targetY = bounds.maxY - (isGroup?n.h:(n.h/2));
break;
case 'left':
targetX = bounds.minX + (isGroup?0:(n.w/2));
targetY = n.y;
break;
case 'right':
targetX = bounds.maxX - (isGroup?n.w:(n.w/2));
targetY = n.y;
break;
case 'middle':
targetX = n.x;
targetY = bounds.midY - (isGroup?n.h/2:0)
break;
case 'center':
targetX = bounds.midX - (isGroup?n.w/2:0)
targetY = n.y;
break;
}
if (n.x !== targetX || n.y !== targetY) {
if (!isGroup) {
changedNodes.push({
n:n,
ox: n.x,
oy: n.y,
moved: n.moved
});
n.x = targetX;
n.y = targetY;
n.dirty = true;
n.moved = true;
} else {
var groupNodes = RED.group.getNodes(n, true);
var deltaX = n.x - targetX;
var deltaY = n.y - targetY;
groupNodes.forEach(function(gn) {
if (gn.type !== "group" ) {
changedNodes.push({
n:gn,
ox: gn.x,
oy: gn.y,
moved: gn.moved
});
gn.x = gn.x - deltaX;
gn.y = gn.y - deltaY;
gn.dirty = true;
gn.moved = true;
}
})
}
}
});
if (changedNodes.length > 0) {
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
function distributeSelection(direction) {
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length > 2) {
var changedNodes = [];
var bounds = {
minX: Number.MAX_SAFE_INTEGER,
minY: Number.MAX_SAFE_INTEGER,
maxX: Number.MIN_SAFE_INTEGER,
maxY: Number.MIN_SAFE_INTEGER
}
var startAnchors = [];
var endAnchors = [];
selection.nodes.forEach(function(n) {
var nx,ny;
if (n.type === "group") {
nx = n.x + n.w/2;
ny = n.y + n.h/2;
} else {
nx = n.x;
ny = n.y;
}
if (direction === "h") {
if (nx < bounds.minX) {
startAnchors = [];
bounds.minX = nx;
}
if (nx === bounds.minX) {
startAnchors.push(n);
}
if (nx > bounds.maxX) {
endAnchors = [];
bounds.maxX = nx;
}
if (nx === bounds.maxX) {
endAnchors.push(n);
}
} else {
if (ny < bounds.minY) {
startAnchors = [];
bounds.minY = ny;
}
if (ny === bounds.minY) {
startAnchors.push(n);
}
if (ny > bounds.maxY) {
endAnchors = [];
bounds.maxY = ny;
}
if (ny === bounds.maxY) {
endAnchors.push(n);
}
}
});
var startAnchor = startAnchors[0];
var endAnchor = endAnchors[0];
var nodeSpace = 0;
var nodesToMove = selection.nodes.filter(function(n) {
if (n.id !== startAnchor.id && n.id !== endAnchor.id) {
nodeSpace += direction === 'h'?n.w:n.h;
return true;
}
return false;
}).sort(function(A,B) {
if (direction === 'h') {
return A.x - B.x
} else {
return A.y - B.y
}
})
var saX = startAnchor.x + startAnchor.w/2;
var saY = startAnchor.y + startAnchor.h/2;
if (startAnchor.type === "group") {
saX = startAnchor.x + startAnchor.w;
saY = startAnchor.y + startAnchor.h;
}
var eaX = endAnchor.x;
var eaY = endAnchor.y;
if (endAnchor.type !== "group") {
eaX -= endAnchor.w/2;
eaY -= endAnchor.h/2;
}
var spaceToFill = direction === 'h'?(eaX - saX - nodeSpace): (eaY - saY - nodeSpace);
var spaceBetweenNodes = spaceToFill / (nodesToMove.length + 1);
var tx = saX;
var ty = saY;
while(nodesToMove.length > 0) {
if (direction === 'h') {
tx += spaceBetweenNodes;
} else {
ty += spaceBetweenNodes;
}
var nextNode = nodesToMove.shift();
var isGroup = nextNode.type==="group";
var nx = nextNode.x;
var ny = nextNode.y;
if (!isGroup) {
tx += nextNode.w/2;
ty += nextNode.h/2;
}
if ((direction === 'h' && nx !== tx) || (direction === 'v' && ny !== ty)) {
if (!isGroup) {
changedNodes.push({
n:nextNode,
ox: nextNode.x,
oy: nextNode.y,
moved: nextNode.moved
});
if (direction === 'h') {
nextNode.x = tx;
} else {
nextNode.y = ty;
}
nextNode.dirty = true;
nextNode.moved = true;
} else {
var groupNodes = RED.group.getNodes(nextNode, true);
var deltaX = direction === 'h'? nx - tx : 0;
var deltaY = direction === 'v'? ny - ty : 0;
groupNodes.forEach(function(gn) {
if (gn.type !== "group" ) {
changedNodes.push({
n:gn,
ox: gn.x,
oy: gn.y,
moved: gn.moved
});
gn.x = gn.x - deltaX;
gn.y = gn.y - deltaY;
gn.dirty = true;
gn.moved = true;
}
})
}
}
if (isGroup) {
tx += nextNode.w;
ty += nextNode.h;
} else {
tx += nextNode.w/2;
ty += nextNode.h/2;
}
}
if (changedNodes.length > 0) {
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
function reorderSelection(dir) {
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";
}))
} else if (n.type !== "subflow"){
nodesToMove.push(n);
}
})
if (nodesToMove.length > 0) {
var z = nodesToMove[0].z;
var existingOrder = RED.nodes.getNodeOrder(z);
var movedNodes;
if (dir === "forwards") {
movedNodes = RED.nodes.moveNodesForwards(nodesToMove);
} else if (dir === "backwards") {
movedNodes = RED.nodes.moveNodesBackwards(nodesToMove);
} else if (dir === "front") {
movedNodes = RED.nodes.moveNodesToFront(nodesToMove);
} else if (dir === "back") {
movedNodes = RED.nodes.moveNodesToBack(nodesToMove);
}
if (movedNodes.length > 0) {
var newOrder = RED.nodes.getNodeOrder(z);
RED.history.push({t:"reorder",nodes:{z:z,from:existingOrder,to:newOrder},dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
function wireSeriesOfNodes() {
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
var i = 0;
var newLinks = [];
while (i < selection.nodes.length - 1) {
var nodeA = selection.nodes[i];
var nodeB = selection.nodes[i+1];
if (nodeA.outputs > 0 && nodeB.inputs > 0) {
var existingLinks = RED.nodes.filterLinks({
source: nodeA,
target: nodeB,
sourcePort: 0
})
if (existingLinks.length === 0) {
var newLink = {
source: nodeA,
target: nodeB,
sourcePort: 0
}
RED.nodes.addLink(newLink);
newLinks.push(newLink);
}
}
i++;
}
if (newLinks.length > 0) {
RED.history.push({
t: 'add',
links: newLinks,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
function wireNodeToMultiple() {
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
var sourceNode = selection.nodes[0];
if (sourceNode.outputs === 0) {
return;
}
var i = 1;
var newLinks = [];
while (i < selection.nodes.length) {
var targetNode = selection.nodes[i];
if (targetNode.inputs > 0) {
var existingLinks = RED.nodes.filterLinks({
source: sourceNode,
target: targetNode,
sourcePort: Math.min(sourceNode.outputs-1,i-1)
})
if (existingLinks.length === 0) {
var newLink = {
source: sourceNode,
target: targetNode,
sourcePort: Math.min(sourceNode.outputs-1,i-1)
}
RED.nodes.addLink(newLink);
newLinks.push(newLink);
}
}
i++;
}
if (newLinks.length > 0) {
RED.history.push({
t: 'add',
links: newLinks,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
return {
init: function() {
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
RED.actions.add("core:hide-selected-node-labels", function() { setSelectedNodeLabelState(false); })
RED.actions.add("core:align-selection-to-grid", alignToGrid);
RED.actions.add("core:scroll-view-up", function() { RED.view.scroll(0,-RED.view.gridSize());});
RED.actions.add("core:scroll-view-right", function() { RED.view.scroll(RED.view.gridSize(),0);});
RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());});
@@ -454,6 +829,12 @@ RED.view.tools = (function() {
RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);});
RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);});
RED.actions.add("core:move-selection-forwards", function() { reorderSelection('forwards') })
RED.actions.add("core:move-selection-backwards", function() { reorderSelection('backwards') })
RED.actions.add("core:move-selection-to-front", function() { reorderSelection('front') })
RED.actions.add("core:move-selection-to-back", function() { reorderSelection('back') })
RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());});
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
@@ -474,6 +855,21 @@ RED.view.tools = (function() {
RED.actions.add("core:go-to-nearest-node-on-right", function() { gotoNearestNode('right')})
RED.actions.add("core:go-to-nearest-node-above", function() { gotoNearestNode('up') })
RED.actions.add("core:go-to-nearest-node-below", function() { gotoNearestNode('down') })
RED.actions.add("core:align-selection-to-grid", alignToGrid);
RED.actions.add("core:align-selection-to-left", function() { alignSelectionToEdge('left') })
RED.actions.add("core:align-selection-to-right", function() { alignSelectionToEdge('right') })
RED.actions.add("core:align-selection-to-top", function() { alignSelectionToEdge('top') })
RED.actions.add("core:align-selection-to-bottom", function() { alignSelectionToEdge('bottom') })
RED.actions.add("core:align-selection-to-middle", function() { alignSelectionToEdge('middle') })
RED.actions.add("core:align-selection-to-center", function() { alignSelectionToEdge('center') })
RED.actions.add("core:distribute-selection-horizontally", function() { distributeSelection('h') })
RED.actions.add("core:distribute-selection-vertically", function() { distributeSelection('v') })
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
// RED.actions.add("core:add-node", function() { addNode() })
},
/**

File diff suppressed because it is too large Load Diff

View File

@@ -21,21 +21,46 @@ RED.workspaces = (function() {
var workspaceIndex = 0;
var viewStack = [];
var hideStack = [];
var viewStackPos = 0;
function addToViewStack(id) {
if (viewStackPos !== viewStack.length) {
viewStack.splice(viewStackPos);
}
viewStack.push(id);
viewStackPos = viewStack.length;
// console.warn("addToViewStack",id,viewStack);
}
function removeFromHideStack(id) {
hideStack = hideStack.filter(function(v) {
if (v === id) {
return false;
} else if (Array.isArray(v)) {
var i = v.indexOf(id);
if (i > -1) {
v.splice(i,1);
}
if (v.length === 0) {
return false;
}
return true
}
return true;
})
}
function addWorkspace(ws,skipHistoryEntry,targetIndex) {
if (ws) {
if (!ws.closeable) {
ws.hideable = true;
}
workspace_tabs.addTab(ws,targetIndex);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
if (hiddenTabs[ws.id]) {
workspace_tabs.hideTab(ws.id);
}
workspace_tabs.resize();
} else {
var tabId = RED.nodes.id();
@@ -43,7 +68,15 @@ RED.workspaces = (function() {
workspaceIndex += 1;
} while ($("#red-ui-workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0);
ws = {type:"tab",id:tabId,disabled: false,info:"",label:RED._('workspace.defaultName',{number:workspaceIndex})};
ws = {
type: "tab",
id: tabId,
disabled: false,
info: "",
label: RED._('workspace.defaultName',{number:workspaceIndex}),
env: [],
hideable: true
};
RED.nodes.addWorkspace(ws,targetIndex);
workspace_tabs.addTab(ws,targetIndex);
workspace_tabs.activateTab(tabId);
@@ -55,6 +88,7 @@ RED.workspaces = (function() {
RED.view.focus();
return ws;
}
function deleteWorkspace(ws) {
if (workspaceTabCount === 1) {
return;
@@ -78,165 +112,9 @@ RED.workspaces = (function() {
if (subflow) {
RED.editor.editSubflow(subflow);
}
return;
} else {
RED.editor.editFlow(workspace);
}
RED.view.state(RED.state.EDITING);
var tabflowEditor;
var trayOptions = {
title: RED._("workspace.editFlow",{name:RED.utils.sanitize(workspace.label)}),
buttons: [
{
id: "node-dialog-delete",
class: 'leftButton'+((workspaceTabCount === 1)?" disabled":""),
text: RED._("common.label.delete"), //'<i class="fa fa-trash"></i>',
click: function() {
deleteWorkspace(workspace);
RED.tray.close();
}
},
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
RED.tray.close();
}
},
{
id: "node-dialog-ok",
class: "primary",
text: RED._("common.label.done"),
click: function() {
var label = $( "#node-input-name" ).val();
var changed = false;
var changes = {};
if (workspace.label != label) {
changes.label = workspace.label;
changed = true;
workspace.label = label;
workspace_tabs.renameTab(workspace.id,label);
}
var disabled = $("#node-input-disabled").prop("checked");
if (workspace.disabled !== disabled) {
changes.disabled = workspace.disabled;
changed = true;
workspace.disabled = disabled;
}
var info = tabflowEditor.getValue();
if (workspace.info !== info) {
changes.info = workspace.info;
changed = true;
workspace.info = info;
}
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
if (changed) {
var historyEvent = {
t: "edit",
changes:changes,
node: workspace,
dirty: RED.nodes.dirty()
}
workspace.changed = true;
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.sidebar.config.refresh();
if (changes.hasOwnProperty('disabled')) {
RED.nodes.eachNode(function(n) {
if (n.z === workspace.id) {
n.dirty = true;
}
});
RED.view.redraw();
}
RED.events.emit("flows:change",workspace);
}
RED.tray.close();
}
}
],
resize: function(dimensions) {
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
var editorRow = $("#dialog-form>div.node-text-editor-row");
var height = $("#dialog-form").height();
for (var i=0; i<rows.size(); i++) {
height -= $(rows[i]).outerHeight(true);
}
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
$(".node-text-editor").css("height",height+"px");
tabflowEditor.resize();
},
open: function(tray) {
var trayFooter = tray.find(".red-ui-tray-footer");
var trayBody = tray.find('.red-ui-tray-body');
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody);
$('<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>').appendTo(dialogForm);
if (!workspace.hasOwnProperty("disabled")) {
workspace.disabled = false;
}
$('<input id="node-input-disabled" type="checkbox">').prop("checked",workspace.disabled).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
invertState: true
})
var row = $('<div class="form-row node-text-editor-row">'+
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
'<div style="min-height:250px;" class="node-text-editor" id="node-input-info"></div>'+
'</div>').appendTo(dialogForm);
tabflowEditor = RED.editor.createEditor({
id: 'node-input-info',
mode: 'ace/mode/markdown',
value: ""
});
$('#node-info-input-info-expand').on("click", function(e) {
e.preventDefault();
var value = tabflowEditor.getValue();
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: tabflowEditor.getCursorPosition(),
complete: function(v,cursor) {
tabflowEditor.setValue(v, -1);
tabflowEditor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
tabflowEditor.focus();
},300);
}
})
});
$('<input type="text" style="display: none;" />').prependTo(dialogForm);
dialogForm.on("submit", function(e) { e.preventDefault();});
$("#node-input-name").val(workspace.label);
RED.text.bidi.prepareInput($("#node-input-name"));
tabflowEditor.getSession().setValue(workspace.info || "", -1);
dialogForm.i18n();
},
close: function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
var selection = RED.view.selection();
if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
RED.sidebar.info.refresh(workspace);
}
tabflowEditor.destroy();
}
}
RED.tray.show(trayOptions);
}
@@ -249,11 +127,18 @@ RED.workspaces = (function() {
var event = {
old: activeWorkspace
}
activeWorkspace = tab.id;
if (tab) {
$("#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-chart").hide();
activeWorkspace = 0;
window.location.hash = '';
}
event.workspace = activeWorkspace;
RED.events.emit("workspace:change",event);
window.location.hash = 'flow/'+tab.id;
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled);
RED.sidebar.config.refresh();
RED.view.focus();
},
@@ -278,7 +163,7 @@ RED.workspaces = (function() {
if (tab.disabled) {
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-disabled');
}
RED.menu.setDisabled("menu-item-workspace-delete",workspaceTabCount <= 1);
RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
if (workspaceTabCount === 1) {
showWorkspace();
}
@@ -286,14 +171,23 @@ RED.workspaces = (function() {
onremove: function(tab) {
if (tab.type === "tab") {
workspaceTabCount--;
} else {
hideStack.push(tab.id);
}
RED.menu.setDisabled("menu-item-workspace-delete",workspaceTabCount <= 1);
RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
if (workspaceTabCount === 0) {
hideWorkspace();
}
},
onreorder: function(oldOrder, newOrder) {
RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()});
RED.history.push({
t:'reorder',
workspaces: {
from:oldOrder,
to:newOrder
},
dirty:RED.nodes.dirty()
});
RED.nodes.dirty(true);
setWorkspaceOrder(newOrder);
},
@@ -312,12 +206,86 @@ RED.workspaces = (function() {
$(".red-ui-sidebar-shade").show();
}
},
onhide: function(tab) {
hideStack.push(tab.id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
hiddenTabs[tab.id] = true;
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:hide",{workspace: tab.id})
},
onshow: function(tab) {
removeFromHideStack(tab.id);
var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
delete hiddenTabs[tab.id];
RED.settings.setLocal("hiddenTabs",JSON.stringify(hiddenTabs));
RED.events.emit("workspace:show",{workspace: tab.id})
},
minimumActiveTabWidth: 150,
scrollable: true,
addButton: "core:add-flow",
addButtonCaption: RED._("workspace.addFlow"),
searchButton: "core:list-flows",
searchButtonCaption: RED._("workspace.listFlows")
menu: function() {
var menuItems = [
{
id:"red-ui-tabs-menu-option-search-flows",
label: RED._("workspace.listFlows"),
onselect: "core:list-flows"
},
{
id:"red-ui-tabs-menu-option-search-subflows",
label: RED._("workspace.listSubflows"),
onselect: "core:list-subflows"
},
null,
{
id:"red-ui-tabs-menu-option-add-flow",
label: RED._("workspace.addFlow"),
onselect: "core:add-flow"
},
{
id:"red-ui-tabs-menu-option-add-flow-right",
label: RED._("workspace.addFlowToRight"),
onselect: "core:add-flow-to-right"
},
null,
{
id:"red-ui-tabs-menu-option-add-hide-flows",
label: RED._("workspace.hideFlow"),
onselect: "core:hide-flow"
},
{
id:"red-ui-tabs-menu-option-add-hide-other-flows",
label: RED._("workspace.hideOtherFlows"),
onselect: "core:hide-other-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-all-flows",
label: RED._("workspace.showAllFlows"),
onselect: "core:show-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows"
},
{
id:"red-ui-tabs-menu-option-add-show-last-flow",
label: RED._("workspace.showLastHiddenFlow"),
onselect: "core:show-last-hidden-flow"
}
]
if (hideStack.length > 0) {
menuItems.unshift({
label: RED._("workspace.hiddenFlows",{count: hideStack.length}),
onselect: "core:list-hidden-flows"
})
}
return menuItems;
}
});
workspaceTabCount = 0;
}
@@ -368,15 +336,104 @@ 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: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:hide-flow", function() {
var selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
}
var hiddenTabs = [];
selection.forEach(function(ws) {
RED.workspaces.hide(ws.id);
hideStack.pop();
hiddenTabs.push(ws.id);
})
if (hiddenTabs.length > 0) {
hideStack.push(hiddenTabs);
}
workspace_tabs.clearSelection();
})
RED.actions.add("core:hide-other-flows", function() {
var selection = workspace_tabs.selection();
if (selection.length === 0) {
selection = [{id:activeWorkspace}]
}
var selected = new Set(selection.map(function(ws) { return ws.id }))
var currentTabs = workspace_tabs.listTabs();
var hiddenTabs = [];
currentTabs.forEach(function(id) {
if (!selected.has(id)) {
RED.workspaces.hide(id);
hideStack.pop();
hiddenTabs.push(id);
}
})
if (hiddenTabs.length > 0) {
hideStack.push(hiddenTabs);
}
})
RED.actions.add("core:hide-all-flows", function() {
var currentTabs = workspace_tabs.listTabs();
currentTabs.forEach(function(id) {
RED.workspaces.hide(id);
hideStack.pop();
})
if (currentTabs.length > 0) {
hideStack.push(currentTabs);
}
workspace_tabs.clearSelection();
})
RED.actions.add("core:show-all-flows", function() {
var currentTabs = workspace_tabs.listTabs();
currentTabs.forEach(function(id) {
RED.workspaces.show(id, null, true)
})
})
// RED.actions.add("core:toggle-flows", function() {
// var currentTabs = workspace_tabs.listTabs();
// var visibleCount = workspace_tabs.count();
// currentTabs.forEach(function(id) {
// if (visibleCount === 0) {
// RED.workspaces.show(id)
// } else {
// RED.workspaces.hide(id)
// }
// })
// })
RED.actions.add("core:show-last-hidden-flow", function() {
var id = hideStack.pop();
if (id) {
if (typeof id === 'string') {
RED.workspaces.show(id);
} else {
var last = id.pop();
id.forEach(function(i) {
RED.workspaces.show(i, null, true);
})
setTimeout(function() {
RED.workspaces.show(last);
},150)
}
}
})
RED.actions.add("core:list-hidden-flows",function() {
RED.actions.invoke("core:search","is:hidden ");
})
RED.actions.add("core:list-flows",function() {
RED.actions.invoke("core:search","type:tab ");
})
RED.actions.add("core:list-subflows",function() {
RED.actions.invoke("core:search","type:subflow ");
})
RED.actions.add("core:go-to-previous-location", function() {
if (viewStackPos > 0) {
if (viewStackPos === viewStack.length) {
@@ -392,8 +449,6 @@ RED.workspaces = (function() {
RED.workspaces.show(viewStack[++viewStackPos],true);
}
})
hideWorkspace();
}
@@ -416,7 +471,7 @@ RED.workspaces = (function() {
var changes = { disabled: workspace.disabled };
workspace.disabled = disabled;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
if (id === activeWorkspace) {
if (!id || (id === activeWorkspace)) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
var historyEvent = {
@@ -445,7 +500,6 @@ RED.workspaces = (function() {
}
}
function removeWorkspace(ws) {
if (!ws) {
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
@@ -474,7 +528,10 @@ RED.workspaces = (function() {
return {
init: init,
add: addWorkspace,
// remove: remove workspace without editor history etc
remove: removeWorkspace,
// delete: remove workspace and update editor history
delete: deleteWorkspace,
order: setWorkspaceOrder,
edit: editWorkspace,
contains: function(id) {
@@ -489,7 +546,18 @@ RED.workspaces = (function() {
selection: function() {
return workspace_tabs.selection();
},
show: function(id,skipStack) {
hide: function(id) {
if (!id) {
id = activeWorkspace;
}
if (workspace_tabs.contains(id)) {
workspace_tabs.hideTab(id);
}
},
isHidden: function(id) {
return hideStack.includes(id)
},
show: function(id,skipStack,unhideOnly) {
if (!workspace_tabs.contains(id)) {
var sf = RED.nodes.subflow(id);
if (sf) {
@@ -498,14 +566,19 @@ RED.workspaces = (function() {
null,
workspace_tabs.activeIndex()+1
);
removeFromHideStack(id);
} else {
return;
}
}
if (!skipStack && activeWorkspace !== id) {
addToViewStack(activeWorkspace)
if (unhideOnly) {
workspace_tabs.showTab(id);
} else {
if (!skipStack && activeWorkspace !== id) {
addToViewStack(activeWorkspace)
}
workspace_tabs.activateTab(id);
}
workspace_tabs.activateTab(id);
},
refresh: function() {
RED.nodes.eachWorkspace(function(ws) {

View File

@@ -21,10 +21,10 @@ RED.user = (function() {
opts = {};
}
var dialog = $('<div id="node-dialog-login" class="hide">'+
'<div style="display: inline-block;width: 250px; vertical-align: top; margin-right: 10px; margin-bottom: 20px;"><img id="node-dialog-login-image" src=""/></div>'+
'<div style="display: inline-block; width: 250px; vertical-align: bottom; margin-left: 10px; margin-bottom: 20px;">'+
'<form id="node-dialog-login-fields" class="form-horizontal" style="margin-bottom: 0px;"></form>'+
var dialog = $('<div id="node-dialog-login" class="hide" style="display: flex; align-items: flex-end;">'+
'<div style="width: 250px; flex-grow: 0;"><img id="node-dialog-login-image" src=""/></div>'+
'<div style="flex-grow: 1;">'+
'<form id="node-dialog-login-fields" class="form-horizontal" style="margin-bottom: 0px; margin-left:20px;"></form>'+
'</div>'+
'</div>');
@@ -76,7 +76,7 @@ RED.user = (function() {
}
row.appendTo("#node-dialog-login-fields");
}
$('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">'+RED._("user.loginFailed")+'</span><img src="red/images/spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+
$('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;color:var(--red-ui-text-color-error);" class="hide">'+RED._("user.loginFailed")+'</span><img src="red/images/spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+
(opts.cancelable?'<a href="#" id="node-dialog-login-cancel" class="red-ui-button" style="margin-right: 20px;" tabIndex="'+(i+1)+'">'+RED._("common.label.cancel")+'</a>':'')+
'<input type="submit" id="node-dialog-login-submit" class="red-ui-button" style="width: auto;" tabIndex="'+(i+2)+'" value="'+RED._("user.login")+'"></div>').appendTo("#node-dialog-login-fields");
@@ -121,6 +121,24 @@ RED.user = (function() {
i = 0;
for (;i<data.prompts.length;i++) {
var field = data.prompts[i];
var sessionMessage = /[?&]session_message=(.*?)(?:$|&)/.exec(window.location.search);
if (sessionMessage) {
RED.sessionMessages = RED.sessionMessages || [];
RED.sessionMessages.push(sessionMessage[1]);
if (history.pushState) {
var newurl = window.location.protocol+"//"+window.location.host+window.location.pathname
window.history.replaceState({ path: newurl }, "", newurl);
} else {
window.location.search = "";
}
}
if (RED.sessionMessages) {
var sessionMessages = $("<div/>",{class:"form-row",style:"text-align: center"}).appendTo("#node-dialog-login-fields");
RED.sessionMessages.forEach(function (msg) {
$('<div>').css("color","var(--red-ui-text-color-error)").text(msg).appendTo(sessionMessages);
});
delete RED.sessionMessages;
}
var row = $("<div/>",{class:"form-row",style:"text-align: center"}).appendTo("#node-dialog-login-fields");
var loginButton = $('<a href="#" class="red-ui-button"></a>',{style: "padding: 10px"}).appendTo(row).on("click", function() {
@@ -152,7 +170,7 @@ RED.user = (function() {
});
}
var loginImageSrc = data.image || "red/images/node-red-256.png";
var loginImageSrc = data.image || "red/images/node-red-256.svg";
$("#node-dialog-login-image").load(function() {
dialog.dialog("open");

View File

@@ -140,8 +140,8 @@ $workspace-button-color-focus-outline: $form-input-focus-color;
$shade-color: rgba(160,160,160,0.5);
$popover-background: #333;
$popover-border: $popover-background;
$popover-color: #eee;
$popover-button-border-color: #bbb;
$popover-button-border-color-hover: #666;
@@ -196,14 +196,26 @@ $view-background: $secondary-background;
$view-select-mode-background: $secondary-background-selected;
$view-grid-color: #eee;
$link-color: #999;
$link-link-color: #aaa;
$link-disabled-color: #ccc;
$link-link-active-color: #ff7f0e;
$link-unknown-color: #f00;
$node-label-color: #333;
$node-port-label-color: #888;
$node-border: #999;
$node-border-unknown: #f33;
$node-border-placeholder: #aaa;
$node-background-placeholder: #eee;
$node-port-border: $node-border;
$node-port-border-connected: $link-color;
$node-port-background: #d9d9d9;
$node-port-background-hover: #eee;
$node-port-background-connected: $link-color;
$node-icon-color: #fff;
$node-icon-background-color: rgba(0,0,0,0.05);
$node-icon-background-color-fill: #000;
@@ -231,12 +243,6 @@ $node-status-colors: (
$node-selected-color: #ff7f0e;
$port-selected-color: #ff7f0e;
$link-color: #999;
$link-link-color: #aaa;
$link-disabled-color: #ccc;
$link-link-active-color: #ff7f0e;
$link-unknown-color: #f00;
$clipboard-textarea-background: #F3E7E7;
@@ -295,6 +301,9 @@ $group-default-stroke: #999;
$group-default-stroke-opacity: 1;
$group-default-label-color: #a4a4a4;
$tourGuide-border: #c56c6c;
$tourGuide-heading-color: #c56c6c;
// Deprecated
$text-color-green: $text-color-success;
$info-text-code-color: $text-color-code;

View File

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

View File

@@ -19,7 +19,7 @@
font-size: $primary-font-size;
position: absolute;
top: 100%;
width: 200px;
width: 230px;
left: 0;
z-index: 1000;
display: none;
@@ -46,7 +46,7 @@
& > li > a,
& > li > a:focus {
display: block;
padding: 4px 0 4px 32px;
padding: 4px 12px 4px 32px;
clear: both;
font-weight: normal;
line-height: 20px;
@@ -68,6 +68,10 @@
& > .disabled > a:hover,
& > .disabled > a:focus {
color: $menuDisabledColor;
.red-ui-popover-key {
color: $menuDisabledColor;
border-color: $menuDisabledColor;
}
}
& > .disabled > a:hover,
@@ -83,6 +87,7 @@
max-width: 14px;
}
.fa {
float: left;
width: 20px;
margin-left: -25px;
margin-top: 3px;
@@ -102,6 +107,20 @@
display: none;
}
}
.red-ui-menu-label {
display: flex;
& > :first-child {
flex-grow: 1
}
}
.red-ui-popover-key {
border: none;
padding: 0;
font-size: 13px;
// float: right;
color: $menuColor;
border-color: $menuColor;
}
}
}
@@ -113,6 +132,7 @@
.red-ui-menu-dropdown > li > a:hover,
.red-ui-menu-dropdown > li.open > a,
.red-ui-menu-dropdown > li > a:focus,
.red-ui-menu-dropdown-submenu:hover > a,
.red-ui-menu-dropdown-submenu:focus > a {
@@ -129,6 +149,7 @@
margin-top: -6px;
margin-left: -1px;
}
&.open > .red-ui-menu-dropdown,
&:hover > .red-ui-menu-dropdown {
display: block;
}
@@ -209,4 +230,4 @@ ul.red-ui-menu:not(.red-ui-menu-dropdown) {
}
}
}
}

View File

@@ -21,6 +21,13 @@
stroke-dasharray: 10 5;
}
.nr-ui-view-slice {
stroke-width: 1px;
stroke: $view-lasso-stroke;
fill: none;
stroke-dasharray: 10 5;
}
.node_label_italic, // deprecated: use red-ui-flow-node-label-italic
.red-ui-flow-node-label-italic {
font-style: italic;
@@ -47,7 +54,7 @@
.red-ui-flow-port-label {
stroke-width: 0;
fill: $secondary-text-color;
fill: $node-port-label-color;
font-size: 16px;
dominant-baseline: middle;
text-anchor: middle;
@@ -178,12 +185,29 @@
}
.red-ui-flow-port {
stroke: $node-border;
stroke: $node-port-border;
stroke-width: 1;
fill: $node-port-background;
cursor: crosshair;
}
.red-ui-flow-port-background {
opacity: 0;
stroke: none;
fill: #f00;
cursor: crosshair;
}
.red-ui-flow-port-shape {
pointer-events: none;
cursor: crosshair;
}
.red-ui-flow-port-connected .red-ui-flow-port {
fill: $node-port-background-connected;
stroke: $node-port-border-connected;
}
.red-ui-flow-node-error {
fill: $node-status-error-background;
stroke: $node-status-error-border;
@@ -273,9 +297,10 @@ g.red-ui-flow-node-selected {
text-anchor:start;
}
.red-ui-flow-port-hovered {
stroke: $port-selected-color;
fill: $port-selected-color;
.red-ui-flow-port-hovered:not(.red-ui-flow-port-background),
.red-ui-flow-port-background.red-ui-flow-port-hovered + .red-ui-flow-port-shape {
stroke: $port-selected-color !important;
fill: $port-selected-color !important;
}
.red-ui-flow-subflow-port {

View File

@@ -187,18 +187,21 @@
ul.red-ui-menu-dropdown {
background: $header-menu-background;
border: 1px solid $header-menu-background;
width: 250px !important;
width: 260px !important;
margin-top: 0;
li a {
color: $header-menu-color;
padding: 3px 40px;
padding: 3px 10px 3px 40px;
img {
max-width: 100%;
margin-right: 10px;
padding: 4px;
border: 3px solid transparent;
}
.red-ui-popover-key {
color: $header-menu-color-disabled !important;
border-color: $header-menu-color-disabled !important;
}
&.active img {
border: 3px solid $header-menu-item-border-active;
}
@@ -211,7 +214,6 @@
}
span.red-ui-menu-label {
font-size: 14px;
display: inline-block;
text-indent: 0px;
}
span.red-ui-menu-sublabel {
@@ -222,6 +224,7 @@
}
}
> li > a:hover,
> li.open > a,
> li > a:focus,
> li:hover > a,
> li:focus > a {

View File

@@ -99,6 +99,9 @@
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.button-group &:focus {
position: relative;
}
.button-row &:not(:first-child) {
margin-left: 15px;
@@ -106,6 +109,7 @@
&:focus {
outline: 1px solid $workspace-button-color-focus-outline;
outline-offset: 1px;
}
&.primary {

View File

@@ -60,13 +60,13 @@
}
.red-ui-notification-compact {
p {
margin: 0;
}
.ui-dialog-buttonset {
button {
line-height: 12px;
}
margin-top: 0;
position: absolute;
top: 8px;
top: 6px;
right: 10px;
}
}

View File

@@ -40,7 +40,7 @@
height: 7px;
box-sizing: border-box;
cursor: ns-resize;
background: $primary-background url(images/grip.png) no-repeat 50% 50%;
background: $primary-background url(images/grip-horizontal.png) no-repeat 50% 50%;
}
@@ -70,5 +70,6 @@
width: 7px;
display: inline-block;
cursor: ew-resize;
background: $primary-background url(images/grip.png) no-repeat 50% 50%;
}
}

View File

@@ -19,19 +19,23 @@
display: none;
position: absolute;
width: auto;
padding: 10px;
padding: 2px;
height: auto;
background: $popover-background;
color: $popover-color;
background: var(--red-ui-popover-border);
color: var(--red-ui-popover-color);
border-radius: 4px;
z-index: 1000;
font-family: $primary-font;
font-size: 14px;
line-height: 1.4em;
@include component-shadow;
border-color: $popover-background;
border-color: var(--red-ui-popover-border);
}
.red-ui-popover-content {
padding: 8px;
border-radius: 2px;
background: var(--red-ui-popover-background);
}
.red-ui-popover:after, .red-ui-popover:before {
border: solid transparent;
content: " ";
@@ -61,26 +65,26 @@
.red-ui-popover.red-ui-popover-right:after {
border-color: transparent;
border-right-color: $popover-background;
border-right-color: var(--red-ui-popover-border);
border-width: 10px;
margin-top: -10px;
}
.red-ui-popover.red-ui-popover-right:before {
border-color: transparent;
border-right-color: $popover-background;
border-right-color: var(--red-ui-popover-border);
border-width: 11px;
margin-top: -11px;
}
.red-ui-popover.red-ui-popover-left:after {
border-color: transparent;
border-left-color: $popover-background;
border-left-color: var(--red-ui-popover-border);
border-width: 10px;
margin-top: -10px;
}
.red-ui-popover.red-ui-popover-left:before {
border-color: transparent;
border-left-color: $popover-background;
border-left-color: var(--red-ui-popover-border);
border-width: 11px;
margin-top: -11px;
}
@@ -88,26 +92,26 @@
.red-ui-popover.red-ui-popover-bottom:after {
border-color: transparent;
border-bottom-color: $popover-background;
border-bottom-color: var(--red-ui-popover-border);
border-width: 10px;
margin-left: -10px;
}
.red-ui-popover.red-ui-popover-bottom:before {
border-color: transparent;
border-bottom-color: $popover-background;
border-bottom-color: var(--red-ui-popover-border);
border-width: 11px;
margin-left: -11px;
}
.red-ui-popover.red-ui-popover-top:after {
border-color: transparent;
border-top-color: $popover-background;
border-top-color: var(--red-ui-popover-border);
border-width: 10px;
margin-left: -10px;
}
.red-ui-popover.red-ui-popover-top:before {
border-color: transparent;
border-top-color: $popover-background;
border-top-color: var(--red-ui-popover-border);
border-width: 11px;
margin-left: -11px;
}
@@ -116,9 +120,10 @@
.red-ui-popover-size-small {
font-size: 12px;
padding: 5px 7px;
line-height: 1.8em;
.red-ui-popover-content {
padding: 1px 4px;
}
&.red-ui-popover-right:after, &.red-ui-popover-left:after {
border-width: 7px;
margin-top: -7px;
@@ -143,7 +148,7 @@
font-size: 11px;
font-family: $monospace-font;
margin-left: 3px;
border: 1px solid $popover-color;
border: 1px solid var(--red-ui-popover-color);
border-radius:3px;
padding: 1px 2px;
}
@@ -152,8 +157,8 @@
.red-ui-popover button.red-ui-button {
&:not(.primary) {
border-color: $popover-button-border-color;
background: $popover-background;
color: $popover-color !important;
background: var(--red-ui-popover-background);
color: var(--red-ui-popover-color) !important;
}
&:not(.primary):not(.disabled):not(.ui-button-disabled):hover {
border-color: $popover-button-border-color-hover;

View File

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

View File

@@ -204,6 +204,28 @@
font-style: italic;
color: $form-placeholder-color;
}
.red-ui-search-history {
button {
display: none;
position: absolute;
top: 8px;
right: 7px;
}
&:hover button {
display: inline;
}
}
.red-ui-search-historyHeader {
button {
position: absolute;
top: 10px;
right: 7px;
}
}
.red-ui-search-history-result {
}
.red-ui-search-result-action {
color: $primary-text-color;

View File

@@ -61,6 +61,7 @@
@import "ui/common/checkboxSet";
@import "ui/common/stack";
@import "ui/common/treeList";
@import "ui/common/autoComplete";
@import "dragdrop";
@@ -69,3 +70,5 @@
@import "debug";
@import "radialMenu";
@import "tourGuide";

View File

@@ -16,6 +16,7 @@
.red-ui-sidebar-info {
height: 100%;
overflow: hidden;
}
.red-ui-sidebar-info hr {
margin: 10px 0;
@@ -212,6 +213,9 @@ div.red-ui-info-table {
border: none;
}
}
p {
display: inline;
}
}
.red-ui-help-info-header {
i {
@@ -230,7 +234,26 @@ div.red-ui-info-table {
}
}
}
table {
border-collapse: collapse;
border: 1px solid var(--red-ui-secondary-border-color);
margin : 8px 0 8px 0;
min-width : 300px;
overflow : hidden;
}
table thead tr {
background-color: var(--red-ui-primary-background); //$primary-text-color;
border-bottom: 1px solid var(--red-ui-secondary-border-color);
color: var(--red-ui-header-text-color);
text-align: left;
}
table th,
table td {
padding: 6px 8px;
}
table tbody tr:nth-of-type(even) {
background-color: var(--red-ui-tertiary-background); //$primary-background;
}
}
.red-ui-sidebar-info-stack {
height: 100%;
@@ -332,7 +355,7 @@ div.red-ui-info-table {
.red-ui-sidebar-help-toc, #red-ui-clipboard-dialog-import-conflicts-list, #red-ui-clipboard-dialog-export-tab-clipboard-preview
{
.red-ui-info-outline-item {
display: inline-block;
display: inline-flex;
padding: 0;
font-size: 13px;
border: none;
@@ -402,8 +425,7 @@ div.red-ui-info-table {
.red-ui-info-outline-gutter {
display:none;
button {
position: absolute;
top: 1px;
position: relative;
left: 2px;
}
.red-ui-treeList-label:hover & {
@@ -412,16 +434,19 @@ div.red-ui-info-table {
}
.red-ui-info-outline-item-controls {
position: absolute;
top:0;
bottom: 0;
right: 0px;
padding: 2px 3px 0 1px;
top:1px;
bottom: 1px;
right: 1px;
padding: 1px 2px 0 1px;
text-align: right;
background: $list-item-background;
.red-ui-treeList-label:hover & {
background: $list-item-background-hover;
}
.red-ui-treeList-label.focus & {
background: $list-item-background-hover;
}
.red-ui-treeList-label.selected & {
background: $list-item-background-selected;
}
@@ -436,6 +461,12 @@ div.red-ui-info-table {
border: none;
background: none;
}
.fa-circle-thin {
display: none;
}
.fa-eye {
display: none;
}
}
.red-ui-info-outline-item-control-reveal,
.red-ui-info-outline-item-control-action {
@@ -447,7 +478,17 @@ div.red-ui-info-table {
display: inline-block;
}
}
.fa-eye-slash {
display: none;
}
.red-ui-info-outline-item.red-ui-info-outline-item-hidden & {
.fa-eye-slash {
display: inline-block;
}
.fa-eye {
display: none;
}
}
.fa-ban {
display: none;
}

View File

@@ -21,6 +21,9 @@
height: 35px;
box-sizing: border-box;
.hide-tab {
transition: width 0.1s ease-in;
}
.red-ui-tabs-scroll-container {
height: 60px;
overflow-x: scroll;
@@ -102,10 +105,21 @@
img.red-ui-tab-icon {
opacity: 0.2;
}
.red-ui-tabs-fade {
background-image: linear-gradient(to right, change-color($tab-background-active, $alpha: 0.001), $tab-background-active);
}
}
&.selected {
&:not(.active) {
background: $tab-background-selected;
.red-ui-tabs-fade {
background-image: linear-gradient(to right, change-color($tab-background-selected, $alpha: 0.001), $tab-background-selected);
}
.red-ui-tabs-badge-selected {
background: $tab-background-selected;
}
}
font-weight: bold;
.red-ui-tabs-badge-selected {
@@ -120,6 +134,9 @@
&:not(.active) a:hover {
color: $workspace-button-color-hover;
background: $tab-background-hover;
&+.red-ui-tabs-fade {
background-image: linear-gradient(to right, change-color($tab-background-hover, $alpha: 0.001), $tab-background-hover);
}
}
}
}
@@ -128,13 +145,18 @@
padding-right: 21px;
}
&.red-ui-tabs-add {
padding-right: 35px;
padding-right: 29px;
}
&.red-ui-tabs-add.red-ui-tabs-scrollable {
padding-right: 59px;
padding-right: 53px;
}
&.red-ui-tabs-add.red-ui-tabs-menu.red-ui-tabs-scrollable,
&.red-ui-tabs-add.red-ui-tabs-search.red-ui-tabs-scrollable {
padding-right: 95px;
padding-right: 83px;
}
&.red-ui-tabs-add.red-ui-tabs-search.red-ui-tabs-menu.red-ui-tabs-scrollable {
padding-right: 113px;
}
&.red-ui-tabs-collapsible {
@@ -218,13 +240,14 @@
a {
@include workspace-button;
line-height: 32px;
height: 32px;
width: 32px;
line-height: 30px;
height: 28px;
width: 28px;
margin-left: 2px;
margin-right: 2px;
margin-top: 3px;
margin-right:3px;
margin-left:3px;
border: 1px solid $primary-border-color;
margin-bottom: 3px;
border: none;
z-index: 2;
}
}
@@ -266,6 +289,8 @@
border-left: none;
border-right: none;
border-top: none;
border-bottom: 1px solid $primary-border-color;
line-height: 34px;
}
}
.red-ui-tab-scroll-left {
@@ -282,14 +307,38 @@
}
.red-ui-tabs.red-ui-tabs-add .red-ui-tab-scroll-right {
right: 38px;
right: 32px;
}
.red-ui-tabs.red-ui-tabs-add.red-ui-tabs-menu .red-ui-tab-scroll-right,
.red-ui-tabs.red-ui-tabs-add.red-ui-tabs-search .red-ui-tab-scroll-right {
right: 76px;
right: 64px;
}
.red-ui-tabs.red-ui-tabs-add.red-ui-tabs-menu .red-ui-tabs-add,
.red-ui-tabs.red-ui-tabs-add.red-ui-tabs-search .red-ui-tabs-add {
right: 38px;
right: 32px;
}
.red-ui-tabs.red-ui-tabs-add.red-ui-tabs-search.red-ui-tabs-menu {
.red-ui-tab-scroll-right {
right: 96px;
}
.red-ui-tabs-add {
right: 64px;
}
.red-ui-tabs-search {
right: 32px;
}
}
.red-ui-tabs-fade {
position: absolute;
bottom: 0;
top: 0;
right: 0;
width: 15px;
background-image: linear-gradient(to right, change-color($tab-background-inactive, $alpha: 0.001), $tab-background-inactive);
pointer-events: none;
}
@@ -340,7 +389,19 @@ i.red-ui-tab-icon {
vertical-align: top;
}
.red-ui-tab-hide {
.fa-eye-slash {
display: none;
}
&:hover {
.fa-eye-slash {
display: inline
}
.fa-eye {
display: none
}
}
}
.red-ui-tab-close {
display: none;
background: $tab-background-inactive;

View File

@@ -0,0 +1,118 @@
.red-ui-tourGuide-shade {
position: absolute;
top:0;
left:0;
bottom:0;
right:0;
z-index: 2000;
overflow: hidden;
}
.red-ui-tourGuide-shade-focus {
display: block;
width: 100px;
height: 100px;
position: absolute;
z-index: 2001;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 2px solid var(--red-ui-tourGuide-border);
&.transition {
transition: 0.4s ease;
transition-property: width,height;
}
&::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: solid 6000px var(--red-ui-shade-color);
margin-left: -6000px;
margin-top: -6000px;
pointer-events: none;
}
}
.red-ui-popover.red-ui-tourGuide-popover {
z-index: 2003;
--red-ui-popover-background: var(--red-ui-secondary-background);
--red-ui-popover-border: var(--red-ui-tourGuide-border);
--red-ui-popover-color: var(--red-ui-primary-text-color);
.red-ui-popover-content {
h2 {
text-align: center;
margin-top: 0px;
line-height: 1.2em;
color: var(--red-ui-tourGuide-heading-color);
i.fa {
font-size: 1.5em
}
}
}
}
.red-ui-tourGuide-toolbar {
min-height: 36px;
position: relative;
display: flex;
align-items: flex-end;
}
.red-ui-tourGuide-breadcrumbs {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 6px;
& > div {
display: inline-block;
}
i {
line-height: 16px;
margin: 0 3px;
}
}
.red-ui-tourGuide-popover-description {
padding: 10px 20px 5px;
}
.red-ui-tourGuide-popover-full {
.red-ui-tourGuide-popover-description {
padding: 20px 40px 10px;
text-align: center;
}
}
.red-ui-popover.red-ui-tourGuide-popover button.red-ui-button {
&:not(.primary) {
background: var(--red-ui-secondary-background);
color: var(--red-ui-primary-text-color) !important;
}
&:not(.primary):not(.disabled):not(.ui-button-disabled):hover {
border-color: $popover-button-border-color-hover;
}
}
// .red-ui-tourGuide-popover-bounce {
// animation: 10s ease-in 5s infinite both red-ui-tourGuide-popover-bounce;
// }
// // @keyframes *must* be on multiple lines so build-custom-theme can filter them out
// @keyframes red-ui-tourGuide-popover-bounce {
// 0%,
// 10%,
// 100% {
// -webkit-transform: translateY(0);
// transform: translateY(0);
// }
// 2%,8% {
// -webkit-transform: translateY(-5px);
// transform: translateY(-5px);
// }
// 5% {
// -webkit-transform: translateY(5px);
// transform: translateY(5px);
// }
// }

View File

@@ -0,0 +1,5 @@
.red-ui-autoComplete-container {
&.red-ui-popover-panel {
border-top: none;
}
}

View File

@@ -69,7 +69,8 @@
.red-ui-treeList-label {
@include disable-selection;
padding: 6px 0;
display: block;
display: flex;
align-items: center;
color: $list-item-color;
text-decoration: none;
cursor: pointer;
@@ -88,18 +89,30 @@
color: $list-item-color;
text-decoration: none;
}
&.focus, &.focus .red-ui-treeList-sublabel-text {
background: $list-item-background-hover;
outline: 1px solid $form-input-focus-color !important;
outline-offset: -1px;
color: $list-item-color;
}
&.selected, &.selected .red-ui-treeList-sublabel-text {
background: $list-item-background-selected;
outline: none;
color: $list-item-color;
}
input.red-ui-treeList-checkbox {
input.red-ui-treeList-checkbox,
input.red-ui-treeList-radio {
margin: 0;
}
}
.red-ui-treeList-label-text {
margin-left: 4px;
overflow: hidden;
text-overflow: ellipsis;
&:empty {
min-height: 20px;
}
}
.red-ui-treeList-sublabel-text {
top: 0;
@@ -116,6 +129,7 @@
.red-ui-treeList-icon {
display: inline-block;
flex-shrink: 0;
width: 20px;
text-align: center;
}

View File

@@ -81,7 +81,8 @@
z-index: 2000;
a {
padding: 6px 18px 6px 6px;
display: block;
display: flex;
align-items: center;
border-bottom: 1px solid $secondary-border-color;
color: $form-text-color;
&:hover {
@@ -98,7 +99,7 @@
background: $workspace-button-background-active;
}
input[type="checkbox"] {
margin-right: 6px;
margin: 0 6px 0 0;
}
}
.red-ui-typedInput-icon {

View File

@@ -81,6 +81,18 @@
--red-ui-node-status-changed-border: #{$node-status-changed-border};
--red-ui-node-status-changed-background: #{$node-status-changed-background};
--red-ui-node-border: #{$node-border};
--red-ui-node-port-background:#{$node-port-background};
--red-ui-node-label-color: #{$node-label-color};
--red-ui-node-selected-color: #{$node-selected-color};
--red-ui-port-selected-color: #{$port-selected-color};
--red-ui-popover-background: #{$popover-background};
--red-ui-popover-border: #{$popover-border};
--red-ui-popover-color: #{$popover-color};
--red-ui-tourGuide-border: #{$tourGuide-border};
--red-ui-tourGuide-heading-color: #{$tourGuide-heading-color};
}

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