Compare commits

..

850 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
Nick O'Leary
4acb66fb7a Merge branch 'master' into dev 2021-06-10 16:06:30 +01:00
Nick O'Leary
68ef85b64b Update CHANGELOG 2021-06-10 15:48:24 +01:00
Dave Conway-Jones
b73efe6bb4 Ensure CSV node tries to parse number when set to do so (trim whitespace) 2021-06-10 14:55:47 +01:00
Nick O'Leary
89c84522d2 Merge branch 'master' into dev 2021-06-10 14:19:24 +01:00
Nick O'Leary
98172764ac Handle node icon paths for scoped modules
Fixes #3013
2021-06-10 14:15:43 +01:00
Nick O'Leary
448e881104 Update to latest node-red-admin 2021-06-10 10:31:54 +01:00
Nick O'Leary
f16134ab1f Update changelog 2021-06-09 13:49:33 +01:00
Nick O'Leary
f5dc1564a4 Merge pull request #3009 from hardillb/get-got
Get got - deactivate HTTP - Proxy unit tests
2021-06-09 09:59:31 +01:00
Nick O'Leary
133df75bd4 Merge branch 'dev' into get-got 2021-06-09 09:59:23 +01:00
Nick O'Leary
440be0653a Merge pull request #3012 from node-red/reorganise-settings
Restructure default settings.js to be more organised
2021-06-09 09:57:34 +01:00
Nick O'Leary
d721a40ca5 Clarify flowFilePretty option 2021-06-09 09:26:55 +01:00
Nick O'Leary
a9b252b8fa Ensure httpServerOptions gets applied to ALL the express apps
This is silly. Turns out setting options at a top level app
does not percolate down to sub apps (and vice versa). You
have to apply the options to ALL express apps.
2021-06-08 21:17:42 +01:00
Nick O'Leary
8a5b3ddee7 Change comment style in default settings file.
Makes clearer what is a comment and what is commented-out code.
2021-06-08 21:04:59 +01:00
Nick O'Leary
d83e543a98 Update dependencies 2021-06-08 17:33:40 +01:00
Nick O'Leary
bcd6e8fd63 Restructure default settings.js to be more organised 2021-06-08 15:58:19 +01:00
Ben Hardill
d5c5738aab Fix proxy authentication problem & reinstate tests
Down to only 2 of the previous tests not working
2021-06-08 14:22:20 +01:00
Ben Hardill
9e4dfe081f Remove unused dev dependency 2021-06-08 13:27:26 +01:00
Ben Hardill
090852b72b Revet changes to /proxyAuthneticate
This end point is currently not hit
2021-06-08 12:06:18 +01:00
Nick O'Leary
ff5e038c49 Improve contrast of export dialog JSON font color 2021-06-08 11:54:58 +01:00
Nick O'Leary
5cc2e5f6e1 Fix merge 2021-06-08 11:48:45 +01:00
Ben Hardill
4e8c0573c4 Re-enable as many http-request proxy tests as possible 2021-06-08 11:46:54 +01:00
Nick O'Leary
ce905ba2c4 Merge branch 'master' into dev 2021-06-08 11:44:31 +01:00
Nick O'Leary
3104c17fb3 Update to latest node-red-admin 2021-06-08 11:36:16 +01:00
Nick O'Leary
7651941722 Do not assign z property to tab node when updating flow
Fixes #3010
2021-06-08 11:35:43 +01:00
Nick O'Leary
7bf938901a Merge branch 'pr_2952' into dev 2021-06-08 11:09:56 +01:00
Nick O'Leary
f8b61d2926 Merge pull request #2997 from hardillb/watch-fix
Watch node throws errors if new files deleted
2021-06-08 11:07:37 +01:00
Nick O'Leary
4edea59ab1 Merge branch 'pr_2990' into dev 2021-06-08 10:56:57 +01:00
Nick O'Leary
c8bcd2818d Disable inject-now button if inside subflow 2021-06-08 10:56:34 +01:00
Nick O'Leary
9b46dbaff1 Switch editableList buttons from <a> to <button> elements 2021-06-08 10:56:04 +01:00
Ben Hardill
17a139f27f Start to add HTTP-PROXY tests back in 2021-06-08 10:30:24 +01:00
Nick O'Leary
bd00c728d1 Merge pull request #3008 from Steve-Mcl/node-red-typings-update
Node red typings update
2021-06-08 10:29:03 +01:00
Nick O'Leary
9d510b514c Merge pull request #3007 from Steve-Mcl/monaco-update
update monaco v0.24.0
2021-06-08 10:27:15 +01:00
Nick O'Leary
00dcc5ecda Merge pull request #3011 from node-red/cheerio-update
HTML node: cheerio update to 1.x
2021-06-08 10:22:20 +01:00
Nick O'Leary
dbbdd3f799 Update to latest cheerio module 2021-06-08 09:31:27 +01:00
Nick O'Leary
3541b4b968 Merge pull request #3004 from node-red-hitachi/fix-get-subflow-info
Fix allow Flow.getNode to return subflowInstance nodes
2021-06-07 18:16:01 +01:00
Nick O'Leary
5b1bf35a23 Fix over-greeding matching whilst parsing commit history
Fixes #3006
2021-06-07 18:07:58 +01:00
Ben Hardill
591b61945f Merge branch 'get-got' of github.com:hardillb/node-red into get-got 2021-06-07 16:01:06 +01:00
Ben Hardill
bd1943626b Add more comment 2021-06-07 16:00:07 +01:00
Ben Hardill
f152cdef51 Merge pull request #1 from node-red/get-got
Get got
2021-06-07 13:04:27 +01:00
Ben Hardill
33f8c9747d Comment out HTTP Proxy tests
These have been removed because the mock proxy doesn't
support using the HTTP CONNECT verb and the new Got based
http-request node only uses CONNET for proxied content
2021-06-07 13:00:02 +01:00
Steve-Mcl
714a5e26b3 add env.get to function node defs 2021-06-05 12:42:54 +01:00
Steve-Mcl
7f2c6e40d3 fix typescript errors in node-red d.ts def files 2021-06-05 12:42:13 +01:00
Steve-Mcl
db676ec223 add snippet for env.get 2021-06-05 12:34:47 +01:00
Steve-Mcl
ffb3e511a7 update monaco v0.24.0 2021-06-05 12:28:25 +01:00
Nick O'Leary
e9e64f6a44 Add option to RED.nodes.createCompleteNodeSet to include node dimensions
This allows the linter to use a flow json with more than just position
information.
2021-06-04 13:53:01 +01:00
Kazuhiro Ito
a7b8adb0e1 Fix allow Flow.getNode to return subflowInstance nodes 2021-06-04 15:08:03 +09:00
Jiye Yu
4140ff03d7 fix typo in zh-CN translation (#3003) 2021-06-03 19:03:00 +01:00
Nick O'Leary
e042ef05a4 Update dependencies 2021-06-02 16:24:12 +01:00
Nick O'Leary
7c02e4d66a Merge branch 'master' into dev 2021-06-02 15:40:56 +01:00
Nick O'Leary
711794cfe1 Merge pull request #2993 from Steve-Mcl/master
ensure context get/set key is a string
2021-06-02 15:20:25 +01:00
Nick O'Leary
c0e4cf2358 Fix handling of boolean subflow module properties
Fixes #3000
2021-06-02 14:48:54 +01:00
Nick O'Leary
a92f8f36c1 Keep proper track of moved/changed state in undo/redo history
Fixes #2999
2021-06-02 14:07:22 +01:00
Nick O'Leary
12698dc347 Add full ast parsing in Function node to spot node.done calls
Fixes #2998
2021-06-02 12:32:44 +01:00
Nick O'Leary
3e6a55f78e Update to latest 6.x ws module 2021-06-02 11:58:51 +01:00
Ben Hardill
7585f14b89 Watch node throws errors if new files deleted
before the node has finished processing them all.

Fixes #2996
2021-05-28 08:34:08 +01:00
Nick O'Leary
01b5fc4d49 Merge pull request #2987 from node-red/clean-ids
Change node id generation to give fixed length values without '.'
2021-05-27 12:18:38 +01:00
Nick O'Leary
0fb7d3bfc8 Merge pull request #2991 from node-red-hitachi/fix-empty-switch-rule
fix handling of empty switch rules
2021-05-27 12:18:23 +01:00
Nick O'Leary
2cd74d355c Merge pull request #2994 from node-red/css-vars
Add css named variables for certain key colours
2021-05-27 12:18:05 +01:00
Nick O'Leary
3d405f8c63 Merge pull request #2969 from node-red/ajv-update
Update ajv to 8.2.0 - drop support for JSON-Schema draft-04
2021-05-27 12:17:51 +01:00
Steve-Mcl
a92f0c4c6e fix context key validation + add tests
- adds a helper function validateContextKey to keep it DRY
- adds tests ensure key of null "" 1 {} [] [""] [1] [{}] all throw error
2021-05-26 13:04:09 +01:00
Nick O'Leary
de142ac9d6 Add css named variables for certain key colors 2021-05-25 21:03:01 +01:00
Nick O'Leary
468ef7ecff Allow RED.settings.set to replace string property with object property 2021-05-25 21:02:14 +01:00
Steve-Mcl
4d768fd236 ensure context get/set key is a string 2021-05-25 14:53:06 +01:00
Steve-Mcl
bfc1f95190 remove redundant i18n entries
- no longer needed since moving inject onto edit form
2021-05-25 13:53:59 +01:00
Nick O'Leary
bc17ebd90e Fix css of node help table of contents elements 2021-05-24 10:23:00 +01:00
Hiroyasu Nishiyama
bb1b3727cb add test case 2021-05-23 10:42:28 +09:00
Hiroyasu Nishiyama
4dbebefb45 fix handling empty rules 2021-05-23 10:01:45 +09:00
Steve-Mcl
e1c5764fbf move new inject button to existing edit form
- revert refactoring of building editableList (not needed now)
- remove node button modifier click & tray.show feature
- add inject button to editableList [buttons] array
- add `id` option to editableList to permit DOM access after creation
- get the new inject button via its `id` and float it to the right
- removed the popup tray buttons i18n entries
2021-05-22 10:52:44 +01:00
Steve-Mcl
70f975e4f0 move inject button off dialog and onto form 2021-05-21 13:26:50 +01:00
Steve-Mcl
845567d1ba add 2 tests for user inject values 2021-05-20 22:53:32 +01:00
Steve-Mcl
f570447000 i18n for buttons on user inject values 2021-05-20 22:53:10 +01:00
Steve-Mcl
9d7b8f1f2f change user prop name for injected values
- less chance for collision
2021-05-20 22:35:42 +01:00
Steve-Mcl
bae6bfc32d Add inject values without deploy feature 2021-05-20 21:12:32 +01:00
Nick O'Leary
8a63390464 Merge pull request #2988 from hardillb/ALPNProtocols
Add ALPN support to TLS node
2021-05-19 17:39:19 +01:00
Ben Hardill
0b52cd8b31 Fixes from testing
I set up a NGINX instance to test this and found I'd missed some bits.

Still can't find a way to test this easily from a nodejs unit test
2021-05-19 10:41:24 +01:00
Nick O'Leary
f97569dd34 Clarify codeEditor options only relate to monaco 2021-05-18 16:46:16 +01:00
Nick O'Leary
a9164e63ab Merge branch 'dev' into pr_2971 2021-05-18 16:30:13 +01:00
Ben Hardill
8c95067ec4 Add ALPN support to TLS node 2021-05-18 13:13:32 +01:00
Nick O'Leary
4f77bbeb2b Update for 1.3.5 2021-05-18 11:52:49 +01:00
Nick O'Leary
8bbed2c831 Change node id generation to give fixed length values without '.' 2021-05-18 11:32:17 +01:00
Nick O'Leary
6b43a23c4b Merge branch 'master' into dev 2021-05-14 21:18:32 +01:00
Nick O'Leary
be9521f659 Revert some of #2967 to fix treeList gutter width calculation 2021-05-14 21:17:47 +01:00
Nick O'Leary
90761fd840 Fix 'SyntaxError' in Function node when last line of on-stop is a comment 2021-05-13 17:23:25 +01:00
Nick O'Leary
d49d9a783c Merge branch 'master' into dev 2021-05-13 16:06:33 +01:00
Nick O'Leary
d7dc7c4eda Fix error handling in runtime/lib/api/nodes 2021-05-13 15:46:56 +01:00
Nick O'Leary
fe64c6a841 Merge branch 'master' into dev 2021-05-13 14:30:02 +01:00
Nick O'Leary
2bbdc85a29 Prevent unknown node from breaking editor
If a node provides a .js file that registers a type
but its .html is empty, then the editor will know about
the type, but there will be no node definition.

This fix handles that in some of the utility functions
for generating node appearance.

This wasn't an exhaustive check for these things - just
some obvious candidates that I hit in testing 'bad' nodes
2021-05-13 14:28:01 +01:00
Nick O'Leary
74628b7034 Update debug tests to handle compact comms format 2021-05-13 14:18:11 +01:00
Nick O'Leary
15aa249f64 Stop module with missing types from preventing editor load 2021-05-13 14:06:43 +01:00
Nick O'Leary
fdf58e1225 Fix lint errors in editor hooks 2021-05-13 14:00:21 +01:00
Nick O'Leary
866f305686 Open subflow tab next to active tab rather than at the end 2021-05-13 13:57:29 +01:00
Nick O'Leary
1550e5343c Merge branch 'master' into dev 2021-05-13 13:42:26 +01:00
Nick O'Leary
add3dd1077 Merge pull request #2986 from hardillb/get-got
Fix msg.responseUrl
2021-05-13 13:41:58 +01:00
Nick O'Leary
79a142fb19 Fix error log in runtime hooks module 2021-05-13 13:40:42 +01:00
Nick O'Leary
1a30fe4a1a Improve red-ui-node-icon css and add red-ui-node-icon-small modifier class 2021-05-13 13:39:29 +01:00
Nick O'Leary
4ff991764e Updates to encode/decode message when passed over debug comms link 2021-05-13 13:38:34 +01:00
Nick O'Leary
001f066769 Remove all input event listeners on a node once it is closed
This prevents in-flight messages being passed to the node after
it has closed.
2021-05-13 13:37:21 +01:00
Nick O'Leary
c47b553a8e Add viewAddPort viewRemovePort viewAddNode viewRemoveNode hooks to view 2021-05-13 13:36:50 +01:00
Nick O'Leary
319af51f84 Add RED.hooks to editor 2021-05-13 13:35:12 +01:00
Nick O'Leary
5dbaaae68e Merge branch 'master' into dev 2021-05-12 16:00:36 +01:00
Ben Hardill
8c1a749a5a Fix msg.responseUrl
The original version would pick up the proxy servers port number
2021-05-12 11:08:57 +01:00
Nick O'Leary
fc8643f238 Merge pull request #2983 from hardillb/get-got
Get got working with proxies
2021-05-12 09:41:26 +01:00
Nick O'Leary
c8653f19bf Merge pull request #2985 from node-red-hitachi/update-function-node-info-text
Update Japanese info text of function node
2021-05-12 09:41:09 +01:00
Hiroyasu Nishiyama
b01100d818 Update Japanese info text of function node 2021-05-12 08:54:32 +09:00
Nick O'Leary
d4096a9026 Merge pull request #2984 from node-red/migrate-sass
Migrate to sass module from node-sass
2021-05-11 18:56:06 +01:00
Nick O'Leary
b9e780cdcd Add Node 16 with sass fixed 2021-05-11 18:14:05 +01:00
Nick O'Leary
b77cd56a01 Migrate from node-sass to sass
node-sass is deprecated and doesn't work on Node 16.
sass is actively maintained and considered the canonical sass
implementation.
2021-05-11 18:13:21 +01:00
Nick O'Leary
9cdec156dc Merge pull request #2974 from aheissenberger/fix-bundle-error
Fixed esbuild bundle error "installRetry" was declared a constant and changed
2021-05-11 17:45:07 +01:00
Nick O'Leary
6aa5968863 Fix Function tab label names in the node help text
Closes #2978
2021-05-11 17:15:27 +01:00
Nick O'Leary
8f7686cd7b Handle sidebar tab that no longer exists when setting first active 2021-05-11 16:42:32 +01:00
Nick O'Leary
d8d384a979 Fix plugin loading when browser sends unrecognised lang 2021-05-11 16:42:00 +01:00
Nick O'Leary
ade318bb78 Support mousewheel scroll in tab bar 2021-05-11 15:58:01 +01:00
Nick O'Leary
ed3aa8189f Shrink default notification box
Also reduces Inject/Debug notification display time as 5 seconds is a
long time for a message telling you it worked
2021-05-11 14:45:53 +01:00
Nick O'Leary
3e43597617 Prevent error whilst drag/drop importing from leaving dropTarget visible
Fixes #2982
2021-05-11 14:10:40 +01:00
Ben Hardill
4c8e895ac7 Clean up console.logs 2021-05-11 09:56:22 +01:00
Ben Hardill
f6a3671366 Get the http-request node got port working with Proxy 2021-05-10 23:01:29 +01:00
Nick O'Leary
e641b0a965 Fix scaling issues when dragging nodes into scaled workspace 2021-05-10 21:03:27 +01:00
Hiroyasu Nishiyama
eddddc6c9b fix duplicate csv node example (#2980) 2021-05-10 15:39:23 +01:00
Nick O'Leary
f249d6306f Merge pull request #2976 from kazuhitoyokoi/master-fixinfotips
Fix incorrect shortcut keys in info tips
2021-05-10 09:42:59 +01:00
Kazuhito Yokoi
5c31bd54e4 Fix incorrect shortcut keys in info tips 2021-05-06 20:09:35 +09:00
Nick O'Leary
71ba73b38f Merge branch 'master' into dev 2021-05-04 11:19:05 +01:00
Nick O'Leary
db0ff74857 Reduce code duplication around node/label generation 2021-05-04 11:12:55 +01:00
Steve-Mcl
1acb073737 add support for explicit calls to ace.setFontSize
- found node-red-contrib-ui-svg explicitly calling ace function
2021-05-04 10:34:29 +01:00
Steve-Mcl
251dda3652 missing editor destroy() calls 2021-05-04 09:04:13 +01:00
Steve-Mcl
22db24509d Ensure monaco exists before attempting cleanup 2021-05-04 09:03:35 +01:00
Andreas Heissenberger
54c9d27fd8 fix 2021-05-03 17:35:50 +02:00
Andreas Heissenberger
01888ff078 fix "installRetry" was declared a constant and changed 2021-05-03 17:23:27 +02:00
Steve-Mcl
ffbd140a97 bug fix orphaned models and graphical tray glitch
- ensure models are disposed correctly
- clean up any orphaned models left after editor:close
- improve concurrent multiple instances javascript models
- fix graphical glitch
2021-05-03 16:19:01 +01:00
Steve-Mcl
dedf5c52d9 better support for multiple editors
- as the javascript model is singleton, need to disable
syntax checking when editor not focused
(support multiple instances of js editor (function node))
2021-05-02 23:11:29 +01:00
Steve-Mcl
10465c5d68 treat appcache files as shell (uibuilder support) 2021-05-02 23:00:35 +01:00
Steve-Mcl
1f4f64a7c0 handle node-red style affecting monaco rename box 2021-05-02 22:44:48 +01:00
Steve-Mcl
a6f116b57b add default basic entries in settings,js
- defaults to "ace" but provides instruction for using monaco
2021-05-01 22:17:48 +01:00
Steve-Mcl
0a80186a92 Merge branch 'dev' into monaco2 2021-05-01 21:56:45 +01:00
Nick O'Leary
635bdf15cb Fix use of paletteLabel in help tab
Fixes #2973
2021-05-01 21:55:50 +01:00
Steve-Mcl
a72bdfdacc Handle known bug (monaco-editor issue No 2382)
- handles "Uncaught (in promise) Canceled: Canceled"
2021-05-01 11:40:43 +01:00
Steve-Mcl
dc3e04456c remove temporary debugging window variable 2021-05-01 11:01:04 +01:00
Nick O'Leary
b0e4fb7602 Fix infotip display following i18n update 2021-04-30 13:54:25 +01:00
Steve-Mcl
df7aa3339b dont tokenise JSONata
- monaco has jsonata tokeniser
2021-04-29 18:11:47 +01:00
Steve-Mcl
c475536388 provide extra libs and call to refresh 2021-04-29 18:00:23 +01:00
Steve-Mcl
cc7def89af ensure monaco is correctly sized 2021-04-29 17:59:12 +01:00
Steve-Mcl
58da87898e integrate monaco
- refactors createEditor out to own code files
- moves ace editor to own code file
- adds monaco editor to own code file
- add monaco bootstrap
- update mst to include monaco asset
- update grunt to include new files and integrate
2021-04-29 17:58:41 +01:00
Steve-Mcl
bded5490d2 add utility func getBrowserInfo
- prep for monaco - for slimming down monaco on mobile
- prep for monaco - needed to prevent monaco on IE
2021-04-29 17:35:11 +01:00
Steve-Mcl
c3715a2a3d ensure editor is removed
- Fixes issue with left over HTML in the DOM
2021-04-29 17:32:44 +01:00
Steve-Mcl
abf084f6c2 Dont call to ACE, re-use createEditor instead 2021-04-29 17:31:38 +01:00
Steve-Mcl
37ba409dc3 ensure mode is set for event log viewer 2021-04-29 17:29:54 +01:00
Steve-Mcl
f29488b24f prevent editor context menu being clipped by sidebar
- raise / lower tray z-index for correct slide animation
2021-04-29 17:03:17 +01:00
Steve-Mcl
71bdade7b9 prevent exception if ace is missing 2021-04-29 16:59:11 +01:00
Steve-Mcl
60d97c887d missing args from JSONata $now signature 2021-04-29 16:57:22 +01:00
Steve-Mcl
5bba50f01f refactor node.libs to a function getLibsList
- preparation for monaco integration
2021-04-29 16:56:16 +01:00
Steve-Mcl
1f7884dc70 add typings for basic intellisense 2021-04-29 16:54:29 +01:00
Steve-Mcl
69dafd6c68 add codeEditor settings 2021-04-29 16:53:59 +01:00
Steve-Mcl
64b79cd5ac add monaco dist
- includes themes from https://github.com/brijeshb42/monaco-themes
2021-04-29 16:52:22 +01:00
Nick O'Leary
1af21735a9 Fix theme handling when no editorTheme.page setting 2021-04-29 15:32:26 +01:00
Nick O'Leary
9886af3cec Fix jshint error in treeList 2021-04-29 14:09:21 +01:00
Nick O'Leary
1eb8f9ad97 Update changelog 2021-04-29 14:01:40 +01:00
Nick O'Leary
08e73d9d7d Merge pull request #2967 from hanc2006/master
Fixed remove item when depth=0 and wrong gutter calc treeList widget
2021-04-29 14:00:02 +01:00
Nick O'Leary
b0e349b215 Update for 1.3.4 2021-04-29 11:22:22 +01:00
Nick O'Leary
caa98b08da Merge branch 'pr_2965' into dev 2021-04-29 11:04:50 +01:00
Nick O'Leary
00caa13a12 Update externalModules_spec to handle variable npm args length 2021-04-29 11:04:19 +01:00
Nick O'Leary
cfc0135e86 Update ajv to 8.2.0 - drop support for JSON-Schema draft-04 2021-04-29 10:48:26 +01:00
Dave Conway-Jones
9ee8c1c791 Give delay node random mina nd max more space so you can see complete value 2021-04-29 10:36:13 +01:00
Nick O'Leary
cd3aba2b89 Allow nodes to access resolved theme files
Fixes #2968
2021-04-29 10:17:07 +01:00
Daniele
a150d8e289 Update packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js
Thanks for the tip, I'll remember next time.

Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-04-28 23:37:26 +02:00
Nick O'Leary
6da8e92f20 Fix inject node output tooltip extra property count 2021-04-28 22:01:39 +01:00
Nick O'Leary
1d4dd4be96 Merge pull request #2961 from node-red/rerorder-inject-typedinput
Re-order inject typedinput to de-emphasise context options
2021-04-28 21:56:47 +01:00
Nick O'Leary
7df1a03b4b Handle subflow modules that contain subflows 2021-04-28 21:50:00 +01:00
Nick O'Leary
ad316ffd37 Merge pull request #2964 from node-red-hitachi/fix-grunt-error-on-exec-node
fix grunt fail on exec node test
2021-04-28 21:02:23 +01:00
Nick O'Leary
91f5542a57 Fix importing node to currently flow rather than match its old z value
If you import a node whose z value is a known existing tab, it is getting
imported to that tab, rather than the expected behaviour of being imported
to the current tab.

This commit fixes that by checked if the node is being imported to a tab
that was included in the import, rather than pre-existing.
2021-04-28 20:54:31 +01:00
GitHub
d47a8aa562 Fix remove item when depth=0 and wrong gutter calc 2021-04-28 17:25:26 +02:00
nileio
676f790933 add engine-strict flag to npm install args 2021-04-28 22:40:17 +10:00
Hiroyasu Nishiyama
70433f3d05 fix grunt fail on exec node test 2021-04-28 21:40:17 +09:00
Nick O'Leary
9f2a2b9869 Pass npm args to preUninstall hook to be consistent with preInstall 2021-04-27 17:32:18 +01:00
Nick O'Leary
a0c09fc617 Update api docs header 2021-04-27 17:30:30 +01:00
Dave Conway-Jones
19d391fa05 only move msg. instead 2021-04-27 11:28:01 +01:00
Dave Conway-Jones
d1aa1fd4d8 reorder inject typedinput to de-empahsise context options 2021-04-27 11:15:16 +01:00
Dave Conway-Jones
0e02d03d9a Merge branch 'master' of https://github.com/node-red/node-red 2021-04-27 11:12:10 +01:00
Nick O'Leary
4133f9c56f Merge pull request #2936 from node-red/npm-install-hooks
Add pre/postInstall hooks to npm install handling
2021-04-27 10:57:14 +01:00
Nick O'Leary
53055064e1 Merge pull request #2932 from node-red/file-cwd-setting
File node: Add fileWorkingDirectory to customise how relative paths are resolved
2021-04-27 10:49:02 +01:00
Nick O'Leary
06090d8de1 Merge branch 'dev' into pr_2949 2021-04-27 10:45:33 +01:00
Nick O'Leary
d6ccae38f8 Merge pull request #2959 from node-red/inject-cronosjs
Move Inject node to CronosJS module
2021-04-27 10:44:22 +01:00
Nick O'Leary
f7210effec Rework hooks structure to be a linkedlist
Allows for safe removal of hooks whilst they are being invoked
2021-04-26 21:14:42 +01:00
Nick O'Leary
8e7efd98b2 Don't let 'escape' whilst moving nodes interrupt things
Fixes #2960
2021-04-26 16:48:21 +01:00
Nick O'Leary
ea50ba16f9 Move Inject node to CronosJS module 2021-04-26 14:47:50 +01:00
Nick O'Leary
b62e4f6662 Fix deprecation of httpRoot 2021-04-26 14:43:06 +01:00
Nick O'Leary
f5a1c8bc49 Merge pull request #2957 from node-red-hitachi/fix-error-on-git-auto-commit
fix error on auto commit for no flow change
2021-04-26 11:54:05 +01:00
Nick O'Leary
4cb8e99430 Timeout http upgrade requests that are not otherwise handled
Fixes #2956
2021-04-26 11:45:28 +01:00
Nick O'Leary
bbac49ff38 Ensure function expand button is above vertical scrollbar
Fixes #2955
2021-04-26 10:18:25 +01:00
Nick O'Leary
1d12017f11 Sort context stores in TypedInput and ensure default first
Fixes #2954
2021-04-26 10:13:57 +01:00
Kazuhito Yokoi
46af2e37a7 Add Japanese translations for delay node enhancements (#2958) 2021-04-26 09:57:11 +01:00
Hiroyasu Nishiyama
a480919ec3 fix error on auto commit for no flow change 2021-04-26 09:05:53 +09:00
Dave Conway-Jones
f8abf9fce1 add another test to csv 2021-04-25 08:53:18 +01:00
Nick O'Leary
62f2a552ea Merge pull request #2953 from node-red/depreacte-usage-of-httpRoot-and-add-warning
Deprecate usage of httpRoot and add warning
2021-04-23 16:42:01 +01:00
Dave Conway-Jones
b053e02174 remove httpRoot from setting.js entirely 2021-04-23 16:38:45 +01:00
Dave Conway-Jones
3798167908 Update packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-04-23 16:36:22 +01:00
Dave Conway-Jones
56fe2014e1 Update packages/node_modules/@node-red/runtime/lib/index.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-04-23 16:36:15 +01:00
Nick O'Leary
be2e64433f Merge pull request #2951 from node-red/fix-flowfile-name-in-settings
fix flowfile name to flows.json in settings
2021-04-23 16:27:45 +01:00
Dave Conway-Jones
8732e89e55 Update packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2021-04-23 16:22:50 +01:00
Dave Conway-Jones
fdd0a93bad Deprecate use of httpRoot in settings and add warning
(no change is actual behaviour yet - just warning)
Should we remove option from settings ? or just label it ?
2021-04-23 15:42:57 +01:00
Nick O'Leary
dd12572b1d Allow node16 build to fail for now 2021-04-23 14:30:50 +01:00
Nick O'Leary
e23f20227a Initial migration from request to got for http-request node 2021-04-23 14:19:15 +01:00
Dave Conway-Jones
5cc791690b fix flowfile name to flows.json in settings
and warn if not set (as if anyone reads warnings)
Move setting to top of settings.js as it will be edited more often.
Default behaviour will still work
(needs translations)
2021-04-23 14:09:06 +01:00
Dave Conway-Jones
9f1deb0c36 CSV Add couple more special character tests just to make sure 2021-04-23 11:19:23 +01:00
Dave Conway-Jones
4cebbf8d22 Fix CSV handling of special chars as separators
(ie escape regex special chars)
and add tests
to close #2950
2021-04-23 10:47:23 +01:00
Dave Conway-Jones
93971537b4 add rate option to queue and timed modes. so a simple check box instead. 2021-04-22 11:02:34 +01:00
Dave Conway-Jones
87e816a7f5 Delay node - Make msg.rate an optional override 2021-04-22 10:26:35 +01:00
Dave Conway-Jones
0f45b1da48 Try to make delay modes consistent as regards flush
behaviour is different. May cause minor breakage.
if flush arrives with payload that is now included with the flushed data so no data is lost. Previously any payload was dropped.
2021-04-22 09:43:54 +01:00
Kazuhito Yokoi
a20049c82a Delay node enhancements (#2294)
* Remove unused messages in message catalog

* Support msg.rate in delay node

* Support nodeMessageBufferMaxLength in delay node

* Add logging function for queue size

* Support msg.nodeMessageBufferMaxLength

* Revert "Support msg.nodeMessageBufferMaxLength"

This reverts commit cc72f892f7.

* Improve logging function for delay node

* Add support for Messaging API to delay node

* Add documentation about msg.rate in delay node

* Add test cases for msg.rate in delay node

Co-authored-by: Dave Conway-Jones <dceejay@users.noreply.github.com>
2021-04-22 09:01:28 +01:00
Nick O'Leary
e57ebdb583 Merge pull request #2947 from kazuhitoyokoi/master
Fix margin between nodes on palette
2021-04-21 09:42:43 +01:00
Kazuhito Yokoi
372122037f Fix margin between nodes on palette 2021-04-21 13:14:46 +09:00
Nick O'Leary
23a5cb1917 Ensure typedInput option is selected in dropdown menu
Part of #2945
2021-04-20 23:39:21 +01:00
Nick O'Leary
f8d5fef3c4 Ensure typedInput without value has focus class removed
Closes #2945
2021-04-20 23:25:56 +01:00
Nick O'Leary
250005ad16 Allow npm install args to be customised by preInstall trigger 2021-04-20 22:55:06 +01:00
Nick O'Leary
719aea2a58 Add Node 16 to travis 2021-04-20 18:38:35 +01:00
Nick O'Leary
0e06da6c63 Update for 1.3.3 2021-04-20 11:06:23 +01:00
Nick O'Leary
68fef169f3 Merge pull request #2944 from node-red/Pull-in-RBE-and-push-out-tail
v2: Pull in RBE node and push out tail
2021-04-20 10:57:37 +01:00
Nick O'Leary
c668201df4 Merge pull request #2931 from node-red/join-default
Join: change default manual mode to object
2021-04-20 10:57:23 +01:00
Nick O'Leary
1d68c8cc87 Merge pull request #2940 from node-red/i18n-update
Update to latest i18n in editor and runtime
2021-04-20 10:57:08 +01:00
Nick O'Leary
b9ac8b42ea Use paletteLabel if set in help sidebar 2021-04-19 21:07:44 +01:00
Nick O'Leary
b4a03a56b4 Allow preInstall hook to return false to skip npm install 2021-04-19 20:29:30 +01:00
Nick O'Leary
9eb668ab30 Prevent TypedInput label overflowing element
Also adds title attribute to the button for the label so it gets a tooltip
2021-04-19 15:28:17 +01:00
Nick O'Leary
233a74c146 Remove TypedInput from tab focus when only one type available 2021-04-19 15:27:47 +01:00
Dave Conway-Jones
e0c7269b8e bump other refs to bcrypt up to 5.0.1 2021-04-19 13:21:12 +01:00
Dave Conway-Jones
73063df11b Pull in RBE node and push out tail 2021-04-19 13:09:13 +01:00
Nick O'Leary
ff00afb5d7 Fix project credential secret reset handling
Part of #2868
2021-04-19 11:32:26 +01:00
Nick O'Leary
3f43dc1855 Fix jshint error 2021-04-19 10:43:01 +01:00
Nick O'Leary
4a4e7fc7cb Make typedInput.disable more consistent in behaviour
Fixes #2942
2021-04-19 10:39:58 +01:00
Dave Conway-Jones
0253dc9623 ensure CSV node can send false as string 2021-04-19 10:03:11 +01:00
Nick O'Leary
f8855b83fa Update i18next in util package 2021-04-16 17:11:57 +01:00
Nick O'Leary
0d0459d83d Update to latest i18n in editor and runtime
Fixes #2924
2021-04-16 16:52:12 +01:00
Nick O'Leary
374ef3902c Merge pull request #2938 from hardillb/dev
Fix for #2935
2021-04-16 13:27:22 +01:00
Ben Hardill
235690064f Fix for #2935 2021-04-16 13:26:11 +01:00
Nick O'Leary
0167c25e08 Export package version in Grunt file so docs template can access 2021-04-16 11:56:23 +01:00
Nick O'Leary
d2432716ea Fix hook requires in unit tests 2021-04-15 15:30:02 +01:00
Nick O'Leary
52ef85cba3 Update test for latest sinon 2021-04-15 15:15:52 +01:00
Nick O'Leary
8140057bea Add pre/postInstall hooks to module install path 2021-04-15 15:12:40 +01:00
Nick O'Leary
22df59e229 Update hooks api to support promise api 2021-04-15 15:12:35 +01:00
Nick O'Leary
ed351eee54 Move hooks to util package 2021-04-15 15:12:30 +01:00
Nick O'Leary
c021b4c368 Merge pull request #2922 from node-red/update-dependencies
Update dependencies
2021-04-15 14:57:07 +01:00
Nick O'Leary
04a3c4bb22 Ensure mqtt-close message is published when closing mqtt nodes
The change in 1.3 where we ensure config nodes are closed last broke this behaviour. Previously, the config node would get closed triggering the close message. With the new 1.3 behaviour, the flow nodes are stopped and as soon as the last flow node deregisters itself, the broker node would disconnect without sending the close message.

The fix is to send the close message as part of the deregister flow as that will handle all cases properly
2021-04-14 22:28:25 +01:00
Nick O'Leary
b5fda5642f Fix package semver comparison to allow >1 version increment 2021-04-14 18:06:59 +01:00
Nick O'Leary
b0955705be Update to 1.3.2 2021-04-13 13:34:16 +01:00
Nick O'Leary
a4a624d537 Update changelog 2021-04-13 13:33:41 +01:00
Nick O'Leary
6a8cf1b768 Fix variable reference error in editableList
Fixes #2933
2021-04-13 13:24:54 +01:00
Nick O'Leary
aac2a8f830 File node: Add fileWorkingDirectory to customise how relative paths are resolved 2021-04-12 18:00:58 +01:00
Nick O'Leary
8269490dd1 Join: change default manual mode to object 2021-04-12 14:22:27 +01:00
Kazuhito Yokoi
39274b0c5d Add Japanese translations for Node-RED v1.3.1 (#2930) 2021-04-12 14:15:07 +01:00
Dave Conway-Jones
55c2430671 Merge branch 'master' of https://github.com/node-red/node-red 2021-04-12 12:16:32 +01:00
Dave Conway-Jones
023486e175 File out node - fix timing of msg.send to be after close., and...
allow msg.encoding to set encoding if desired.
To close #2921
2021-04-12 12:16:23 +01:00
Nick O'Leary
8227643741 Merge branch 'pr_2920' 2021-04-12 12:08:32 +01:00
Nick O'Leary
e44131f97a Update function node help reference to node properties 2021-04-12 12:08:07 +01:00
Nick O'Leary
5028377d45 Fix MQTT Broker TLS config row layout
Fixes #2927
2021-04-12 11:48:10 +01:00
Nick O'Leary
51aaf1b150 Handle package.json without dependencies section 2021-04-12 10:34:43 +01:00
Nick O'Leary
13406e76de Ensure theme login image is passed through to api response
Fixes #2929
2021-04-12 10:06:35 +01:00
Dave Conway-Jones
4672d98e8a split node - add comment to info re $N being number of messages arriving 2021-04-12 09:47:18 +01:00
Dave Conway-Jones
858b3d640a fix CSV parsing with other than , separator
(and joining as well...
and add tests
to close #2925
2021-04-10 22:17:31 +01:00
Nick O'Leary
6087002562 Fix handling of user-provided keymap
Fixes #2926
2021-04-10 21:34:26 +01:00
Nick O'Leary
82ced56bed Update package versions 2021-04-09 14:12:10 +01:00
Nick O'Leary
982b8ea51d Update fs-extra and semver 2021-04-09 13:53:06 +01:00
Nick O'Leary
877c463494 Update to latest sinon 2021-04-09 11:22:57 +01:00
Nick O'Leary
9882582903 Update to latest bcrypt 2021-04-09 10:50:35 +01:00
Nick O'Leary
ba566657f1 Set minimum node version to 12 2021-04-09 10:37:05 +01:00
Nick O'Leary
cb1a178fbf Set 2.0.0-beta.1 version 2021-04-09 10:35:19 +01:00
Kristian Heljas
ad788fbed1 Function node: describe node.outputCount in help text 2021-04-08 21:09:44 +03:00
441 changed files with 68034 additions and 7080 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,21 +0,0 @@
sudo: false
addons:
chrome: stable
language: node_js
matrix:
include:
- 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
- node_js: "10"
script:
- ./node_modules/.bin/grunt no-coverage
#- node_js: "8"
# script:
# - ./node_modules/.bin/grunt no-coverage

14
API.md
View File

@@ -1,8 +1,12 @@
Node-RED Modules
---
Node-RED consists of 6 node modules under the `@node-red` scope, which are pulled together
by the top-level `node-red` module. The typical scenario is where you are embedding Node-RED into your
own application, in which case you would use the `node-red` module rather than any of the
internal modules directly.
```javascript
let RED = require("node-red");
```
Node-RED provides a set of node modules that implement different parts of the
application.
Module | Description
-------|-------
@@ -11,5 +15,5 @@ Module | Description
[@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED
[@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules
[@node-red/registry](@node-red_registry.html) | the internal node registry
@node-red/nodes | the default set of core nodes
@node-red/nodes | the default set of core nodes. This module only contains the Node-RED nodes - it does not expose any APIs.
@node-red/editor-client | the client-side resources of the Node-RED editor application

View File

@@ -1,3 +1,499 @@
#### 2.2.0-beta.1: Beta Release
#### 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.
- The following nodes have had significant dependency updates. Unless stated,
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.
- Tail: This node has been removed from the default palette. You can reinstall it
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.
- 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.
- `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
- [MAJOR] Set minimum node version to 12.
- [MAJOR] Fix flowfile name to flows.json in settings (#2951) @dceejay
- [MAJOR] Update to latest i18n in editor and runtime (#2940) @knolleary
- [MAJOR] Deprecate usage of httpRoot (#2953) @knolleary
- Add pre/postInstall hooks to npm install handling (#2936) @knolleary
- Add engine-strict flag to npm install args (#2965) @nileio
- Restructure default settings.js to be more organised (#3012) @knolleary
- Ensure httpServerOptions gets applied to ALL the express apps
- Allow RED.settings.set to replace string property with object property
- Update debug tests to handle compact comms format
- Updates to encode/decode message when passed over debug comms link
- Remove all input event listeners on a node once it is closed
- Move hooks to util package
- Rework hooks structure to be a linkedlist
- Update dependencies (#2922) @knolleary
Editor
- [MAJOR] Change node id generation to give fixed length values without '.' (#2987) @knolleary
- [MAJOR] Add Monaco code editor (#2971) @Steve-Mcl
- Update to latest Monaco (#3007) @Steve-Mcl
- Update Node-RED Function typings in Monaco (#3008) @Steve-Mcl
- Add css named variables for certain key colours (#2994) @knolleary
- Improve contrast of export dialog JSON font color
- Switch editableList buttons from <a> to <button> elements
- Add option to RED.nodes.createCompleteNodeSet to include node dimensions
- Fix css of node help table of contents elements
- Improve red-ui-node-icon css and add red-ui-node-icon-small modifier class
- Add RED.hooks to editor
- Add viewAddPort viewRemovePort viewAddNode viewRemoveNode hooks to view
- Use paletteLabel if set in help sidebar
- Add missing args from JSONata $now signature
Nodes
- [MAJOR] Relabel RBE node as 'filter' and move into core. Also remove tail (#2944) @dceejay
- [MAJOR] HTTP Request: migrate to 'got' module (#2952) @knolleary
- [MAJOR] Move Inject node to CronosJS module (#2959) @knolleary
- [MAJOR] JSON: Update ajv to 8.2.0 - drop support for JSON-Schema draft-04 (#2969) @knolleary
- [MAJOR] HTML node: cheerio update to 1.x (#3011) @knolleary
- Join: change default manual mode to object (#2931) @knolleary
- File node: Add fileWorkingDirectory (#2932) @knolleary
- Delay node enhancements (#2294) @kazuhitoyokoi (#2949) @dceejay
- Add Japanese translations for delay node enhancements (#2958) @kazuhitoyokoi
- Inject node: reorder TypedInput options (#2961) @dceejay
- HTTP Request: update to work with proxies (#2983) @hardillb (#3009) @hardillb
- HTTP Request: fix msg.responseUrl (#2986) @hardillb
- TLS: Add ALPN support to TLS node (#2988) @hardillb
- Inject: add "Inject now" button to edit dialog (#2990) @Steve-Mcl
### 1.3.5 Maintenance Release
Editor
- Open subflow tab next to active tab rather than at the end
- Shrink default notification box
- Support mousewheel scroll in tab bar
- Revert some of #2967 to fix treeList gutter width calculation
- Prevent unknown node from breaking editor
- Stop module with missing types from preventing editor load
- Handle sidebar tab that no longer exists when setting first active
- Fix plugin loading when browser sends unrecognised lang
- Prevent error whilst drag/drop importing from leaving dropTarget visible Fixes #2982
- Fix scaling issues when dragging nodes into scaled workspace
- Fix incorrect shortcut keys in info tips (#2980) @kazuhitoyokoi
- Reduce code duplication around node/label generation
- Fix theme handling when no editorTheme.page setting
- Fix jshint error in treeList
Runtime
- Fix error handling in runtime/lib/api/nodes
- Add Node 16 with sass fixed
- Migrate from node-sass to sass (#2984)
- Fix "installRetry" was declared a constant and changed (#2974) @aheissenberger
Nodes
- Function: Fix 'SyntaxError' in Function node when last line of on-stop is a comment
- Function: Fix Function tab label names in the node help text Closes #2978
- Function: Update Japanese info text of function node (#2985) @HiroyasuNishiyama
### 1.3.4 Maintenance Release
Editor
- Allow nodes to access resolved theme files Fixes #2968
- Fix importing node to currently flow rather than match its old z value
- Don't let 'escape' whilst moving nodes interrupt things Fixes #2960
- Sort context stores in TypedInput and ensure default first Fixes #2954
- Fix margin between nodes on palette (#2947) @kazuhitoyokoi
- Ensure typedInput option is selected in dropdown menu Part of #2945
- Ensure typedInput without value has focus class removed Closes #2945
- TreeList: Fix remove item when depth=0 and wrong gutter calc (#2967) @hanc2006
Runtime
- Handle subflow modules that contain subflows
- Timeout http upgrade requests that are not otherwise handled Fixes #2956
- Fix error on auto commit for no flow change (#2957) @HiroyasuNishiyama
Nodes
- CSV: Fix CSV handling of special chars as separators
- Delay: Give delay node random mina nd max more space so you can see complete value
- Exec: fix grunt fail on exec node test (#2964) @HiroyasuNishiyama
- Function: Ensure function expand button is above vertical scrollbar Fixes #2955
- Inject: Fix inject node output tooltip extra property count
### 1.3.3: Maintenance Release
Editor
- Fix package semver comparison to allow >1 version increment
- Prevent TypedInput label overflowing element Fixes #2941
- Remove TypedInput from tab focus when only one type available
- Make typedInput.disable more consistent in behaviour Fixes #2942
- Fix project credential secret reset handling Part of #2868
Runtime
- Export package version in Grunt file so docs template can access
Nodes
- CSV: ensure CSV node can send false as string
- HTTPIn: handle application/x-protobuf as Buffer type (#2935 #2938) @hardillb
- MQTT: Ensure mqtt-close message is published when closing mqtt nodes
### 1.3.2: Maintenance Release
Runtime
- Handle package.json without dependencies section
Editor
- Fix variable reference error in editableList Fixes #2933
- Fix handling of user-provided keymap Fixes #2926
- Ensure theme login image is passed through to api response Fixes #2929
- Add Japanese translations for Node-RED v1.3.1 (#2930) @kazuhitoyokoi
Nodes
- CSV: Fix CSV parsing with other than , separator
- File out: Fix timing of msg.send to be after close
- Function: describe `node.outputCount` in help text
- MQTT: Fix MQTT Broker TLS config row layout Fixes #2927
- Split: add comment to info re $N being number of messages arriving
### 1.3.1: Maintenance Release

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

@@ -16,7 +16,7 @@
var path = require("path");
var fs = require("fs-extra");
var sass = require("node-sass");
var sass = require("sass");
module.exports = function(grunt) {
@@ -40,8 +40,10 @@ module.exports = function(grunt) {
if (nonHeadless) {
process.env.NODE_RED_NON_HEADLESS = true;
}
const pkg = grunt.file.readJSON('package.json');
process.env.NODE_RED_PACKAGE_VERSION = pkg.version;
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
pkg: pkg,
paths: {
dist: ".dist"
},
@@ -135,6 +137,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js",
"packages/node_modules/@node-red/editor-client/src/js/red.js",
"packages/node_modules/@node-red/editor-client/src/js/events.js",
"packages/node_modules/@node-red/editor-client/src/js/hooks.js",
"packages/node_modules/@node-red/editor-client/src/js/i18n.js",
"packages/node_modules/@node-red/editor-client/src/js/settings.js",
"packages/node_modules/@node-red/editor-client/src/js/user.js",
@@ -159,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",
@@ -167,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",
@@ -178,7 +182,9 @@ 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",
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
@@ -194,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"
},
@@ -208,7 +215,9 @@ module.exports = function(grunt) {
"node_modules/marked/marked.min.js",
"node_modules/dompurify/dist/purify.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/i18next/i18next.min.js",
"node_modules/i18next/i18next.min.js",
"node_modules/i18next-http-backend/i18nextHttpBackend.min.js",
"node_modules/jquery-i18next/jquery-i18next.min.js",
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
@@ -281,7 +290,9 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/public/index.html",
"packages/node_modules/@node-red/editor-client/public/favicon.ico",
"packages/node_modules/@node-red/editor-client/public/icons",
"packages/node_modules/@node-red/editor-client/public/vendor"
"packages/node_modules/@node-red/editor-client/public/vendor",
"packages/node_modules/@node-red/editor-client/public/types/node",
"packages/node_modules/@node-red/editor-client/public/types/node-red",
]
},
release: {
@@ -317,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'
@@ -371,11 +388,24 @@ module.exports = function(grunt) {
src: [
'ace/**',
'jquery/css/base/**',
'font-awesome/**'
'font-awesome/**',
'monaco/dist/**',
'monaco/types/extraLibs.js',
'monaco/style.css',
'monaco/monaco-bootstrap.js'
],
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src',
src: [
'types/node/*.ts',
'types/node-red/*.ts',
],
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/icons',
src: '**',
@@ -401,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/'
}
]
}
@@ -467,7 +503,8 @@ module.exports = function(grunt) {
],
options: {
destination: 'docs',
configure: './jsdoc.json'
configure: './jsdoc.json',
fred: "hi there"
}
},
_editor: {
@@ -546,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": "1.3.1",
"version": "2.2.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -26,95 +26,100 @@
}
],
"dependencies": {
"ajv": "6.12.6",
"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",
"cheerio": "0.22.0",
"cheerio": "1.0.0-rc.10",
"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",
"cron": "1.7.2",
"denque": "1.5.0",
"cronosjs": "1.7.1",
"denque": "2.0.1",
"express": "4.17.1",
"express-session": "1.17.1",
"fs-extra": "8.1.0",
"express-session": "1.17.2",
"form-data": "4.0.0",
"fs-extra": "10.0.0",
"fs.notify": "0.0.4",
"got": "11.8.3",
"hash-sum": "2.0.0",
"hpagent": "0.1.2",
"https-proxy-agent": "5.0.0",
"i18next": "15.1.2",
"iconv-lite": "0.6.2",
"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": "^0.2.6",
"node-red-node-rbe": "^0.5.0",
"node-red-node-sentiment": "^0.1.6",
"node-red-node-tail": "^0.3.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",
"semver": "6.3.0",
"tar": "6.1.0",
"uglify-js": "3.13.3",
"ws": "6.2.1",
"raw-body": "2.4.2",
"semver": "7.3.5",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.14.4",
"uuid": "8.3.2",
"ws": "7.5.1",
"xml2js": "0.4.23"
},
"optionalDependencies": {
"bcrypt": "3.0.8"
"bcrypt": "5.0.1"
},
"devDependencies": {
"dompurify": "2.2.7",
"grunt": "1.3.0",
"dompurify": "2.3.3",
"grunt": "1.4.1",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.2",
"grunt-cli": "~1.4.3",
"grunt-concurrent": "3.0.0",
"grunt-contrib-clean": "~2.0.0",
"grunt-contrib-compress": "1.6.0",
"grunt-contrib-compress": "2.0.0",
"grunt-contrib-concat": "~1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-jshint": "~2.1.0",
"grunt-contrib-uglify": "~4.0.1",
"grunt-contrib-jshint": "3.1.1",
"grunt-contrib-uglify": "5.0.1",
"grunt-contrib-watch": "~1.1.0",
"grunt-jsdoc": "2.4.1",
"grunt-jsdoc-to-markdown": "5.0.0",
"grunt-jsdoc-to-markdown": "6.0.0",
"grunt-jsonlint": "2.1.3",
"grunt-mkdir": "~1.1.0",
"grunt-npm-command": "~0.1.2",
"grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1",
"http-proxy": "1.18.1",
"i18next-http-backend": "1.3.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "2.0.1",
"marked": "3.0.7",
"minami": "1.2.3",
"mocha": "^5.2.0",
"mocha": "9.1.3",
"node-red-node-test-helper": "^0.2.7",
"node-sass": "^5.0.0",
"nodemon": "2.0.7",
"nodemon": "2.0.15",
"proxy": "^1.0.2",
"sass": "1.44.0",
"should": "13.2.3",
"sinon": "1.17.7",
"sinon": "11.1.2",
"stoppable": "^1.1.0",
"supertest": "5.0.0"
"supertest": "6.1.6"
},
"engines": {
"node": ">=8"
"node": ">=12"
}
}

View File

@@ -39,6 +39,15 @@ module.exports = {
var adminApp = express();
var defaultServerSettings = {
"x-powered-by": false
}
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (var eOption in serverSettings) {
adminApp.set(eOption, serverSettings[eOption]);
}
// Flows
adminApp.get("/flows",needsPermission("flows.read"),flows.get,apiUtil.errorHandler);
adminApp.post("/flows",needsPermission("flows.write"),flows.post,apiUtil.errorHandler);

View File

@@ -17,9 +17,8 @@ module.exports = {
})
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
if (/[^a-z\-\*]/i.test(opts.lang)) {
res.json({});
return;
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
opts.lang = "en-US";
}
runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) {
res.send(configs);
@@ -32,9 +31,8 @@ module.exports = {
lang: req.query.lng,
req: apiUtils.getRequestLogObject(req)
}
if (/[^a-z\-\*]/i.test(opts.lang)) {
res.json({});
return;
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
opts.lang = "en-US";
}
runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) {
res.json(result);

View File

@@ -90,7 +90,7 @@ function getToken(req,res,next) {
return server.token()(req,res,next);
}
function login(req,res) {
async function login(req,res) {
var response = {};
if (settings.adminAuth) {
var mergedAdminAuth = Object.assign({}, settings.adminAuth, settings.adminAuth.module);
@@ -116,8 +116,9 @@ function login(req,res) {
response.prompts[0].image = theme.serveFile('/login/',mergedAdminAuth.strategy.image);
}
}
if (theme.context().login && theme.context().login.image) {
response.image = theme.context().login.image;
let themeContext = await theme.context();
if (themeContext.login && themeContext.login.image) {
response.image = themeContext.login.image;
}
}
res.json(response);
@@ -140,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);
});
@@ -172,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;
@@ -204,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
);
}
@@ -215,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

@@ -64,10 +64,12 @@ module.exports = {
}
});
}
if (settings.httpServerOptions) {
for (var eOption in settings.httpServerOptions) {
editorApp.set(eOption, settings.httpServerOptions[eOption]);
}
var defaultServerSettings = {
"x-powered-by": false
}
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (var eOption in serverSettings) {
editorApp.set(eOption, serverSettings[eOption]);
}
editorApp.get("/",ensureRuntimeStarted,ui.ensureSlash,ui.editor);

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,16 +24,20 @@ 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,8 +95,13 @@ 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" : "";
activeTheme = theme.theme;
},
@@ -122,13 +131,25 @@ 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
}
}
themeContext.page.title = theme.page.title || themeContext.page.title;
// Store the resolved urls to these resources so nodes (such as Debug)
// can access them
theme.page._ = {
css: themeContext.page.css,
scripts: themeContext.page.scripts,
favicon: themeContext.page.favicon
}
}
if (theme.header) {
@@ -207,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() {
@@ -223,6 +249,8 @@ module.exports = {
themePlugin.path
);
themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
theme.page = theme.page || {_:{}}
theme.page._.css = cssFiles.concat(theme.page._.css || [])
}
if (themePlugin.scripts) {
const scriptFiles = serveFilesFromTheme(
@@ -232,6 +260,11 @@ module.exports = {
themePlugin.path
)
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
theme.page = theme.page || {_:{}}
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

@@ -64,6 +64,14 @@ function init(settings,_server,storage,runtimeAPI) {
}
}
var defaultServerSettings = {
"x-powered-by": false
}
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
for (var eOption in serverSettings) {
adminApp.set(eOption, serverSettings[eOption]);
}
auth.init(settings,storage);
var maxApiRequestSize = settings.apiMaxLength || '5mb';
@@ -82,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": "1.3.1",
"version": "2.2.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,25 +16,25 @@
}
],
"dependencies": {
"@node-red/util": "1.3.1",
"@node-red/editor-client": "1.3.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",
"cors": "2.8.5",
"express-session": "1.17.1",
"express-session": "1.17.2",
"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": "6.2.1"
"passport": "0.5.0",
"ws": "7.5.1"
},
"optionalDependencies": {
"bcrypt": "3.0.6"
"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,17 +115,32 @@
"editPalette":"Manage palette",
"other": "Other",
"showTips": "Show tips",
"showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website",
"projects": "Projects",
"projects-new": "New",
"projects-open": "Open",
"projects-settings": "Project Settings",
"showNodeLabelDefault": "Show label of newly added nodes",
"codeEditor": "Code Editor",
"groups": "Groups",
"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": {
@@ -449,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",
@@ -463,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"
@@ -518,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",
@@ -641,7 +671,8 @@
"unusedConfigNodes": "Unused configuration nodes",
"invalidNodes": "Invalid nodes",
"uknownNodes": "Unknown nodes",
"unusedSubflows": "Unused subflows"
"unusedSubflows": "Unused subflows",
"hiddenFlows": "Hidden flows"
}
},
"help": {
@@ -863,6 +894,8 @@
"addTitle": "add an item"
},
"search": {
"history": "Search history",
"clear": "clear all",
"empty": "No matches found",
"addNode": "add a node..."
},
@@ -885,6 +918,9 @@
"eval": "Error evaluating expression:\n __message__"
}
},
"monaco": {
"setTheme": "Set theme"
},
"jsEditor": {
"title": "JavaScript editor"
},
@@ -1104,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

@@ -52,8 +52,8 @@
"desc": "Finds occurrences of `pattern` within `str` and replaces them with `replacement`.\n\nThe optional `limit` parameter is the maximum number of replacements."
},
"$now": {
"args":"",
"desc":"Generates a timestamp in ISO 8601 compatible format and returns it as a string."
"args":"$[picture [, timezone]]",
"desc":"Generates a timestamp in ISO 8601 compatible format and returns it as a string. If the optional picture and timezone parameters are supplied, then the current timestamp is formatted as described by the `$fromMillis()` function"
},
"$base64encode": {
"args":"string",
@@ -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,14 +547,15 @@
"nodeEnabled_plural": "ノードを有効化しました:",
"nodeDisabled": "ノードを無効化しました:",
"nodeDisabled_plural": "ノードを無効化しました:",
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました"
"nodeUpgraded": "ノードモジュール __module__ をバージョン __version__ へ更新しました",
"unknownNodeRegistered": "ノードの読み込みエラー: <ul><li>__type__<br>__error__</li></ul>"
},
"editor": {
"title": "パレットの管理",
"palette": "パレット",
"times": {
"seconds": "秒前",
"minutes": "分前",
"seconds": "秒前",
"minutes": "分前",
"minutesV": "__count__ 分前",
"hoursV": "__count__ 時間前",
"hoursV_plural": "__count__ 時間前",
@@ -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>"
@@ -648,7 +648,7 @@
},
"context": {
"name": "上下文数据",
"label": "上下",
"label": "上下",
"none": "未选择",
"refresh": "刷新以加载",
"empty": "空",

View File

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

@@ -66,12 +66,14 @@ RED.history = (function() {
var importedResult = RED.nodes.import(ev.config,{importMap: importMap})
inverseEv = {
t: 'replace',
config: importedResult.removedNodes
config: importedResult.removedNodes,
dirty: RED.nodes.dirty()
}
}
} else if (ev.t == 'add') {
inverseEv = {
t: "delete",
dirty: RED.nodes.dirty()
};
if (ev.nodes) {
inverseEv.nodes = [];
@@ -158,7 +160,8 @@ RED.history = (function() {
} else if (ev.t == "delete") {
inverseEv = {
t: "add"
t: "add",
dirty: RED.nodes.dirty()
};
if (ev.workspaces) {
inverseEv.workspaces = [];
@@ -300,11 +303,12 @@ RED.history = (function() {
} else if (ev.t == "move") {
inverseEv = {
t: 'move',
nodes: []
nodes: [],
dirty: RED.nodes.dirty()
};
for (i=0;i<ev.nodes.length;i++) {
var n = ev.nodes[i];
var rn = {n: n.n, ox: n.n.x, oy: n.n.y, dirty: true, moved: n.moved};
var rn = {n: n.n, ox: n.n.x, oy: n.n.y, dirty: true, moved: n.n.moved};
inverseEv.nodes.push(rn);
n.n.x = n.ox;
n.n.y = n.oy;
@@ -336,7 +340,9 @@ RED.history = (function() {
} else if (ev.t == "edit") {
inverseEv = {
t: "edit",
changes: {}
changes: {},
changed: ev.node.changed,
dirty: RED.nodes.dirty()
};
inverseEv.node = ev.node;
for (i in ev.changes) {
@@ -552,10 +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 = {
@@ -651,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();
@@ -658,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();
@@ -674,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

@@ -0,0 +1,156 @@
RED.hooks = (function() {
var VALID_HOOKS = [
]
var hooks = { }
var labelledHooks = { }
function add(hookId, callback) {
var parts = hookId.split(".");
var id = parts[0], label = parts[1];
// if (VALID_HOOKS.indexOf(id) === -1) {
// throw new Error("Invalid hook '"+id+"'");
// }
if (label && labelledHooks[label] && labelledHooks[label][id]) {
throw new Error("Hook "+hookId+" already registered")
}
var hookItem = {cb:callback, previousHook: null, nextHook: null }
var tailItem = hooks[id];
if (tailItem === undefined) {
hooks[id] = hookItem;
} else {
while(tailItem.nextHook !== null) {
tailItem = tailItem.nextHook
}
tailItem.nextHook = hookItem;
hookItem.previousHook = tailItem;
}
if (label) {
labelledHooks[label] = labelledHooks[label]||{};
labelledHooks[label][id] = hookItem;
}
}
function remove(hookId) {
var parts = hookId.split(".");
var id = parts[0], label = parts[1];
if ( !label) {
throw new Error("Cannot remove hook without label: "+hookId)
}
if (labelledHooks[label]) {
if (id === "*") {
// Remove all hooks for this label
var hookList = Object.keys(labelledHooks[label]);
for (var i=0;i<hookList.length;i++) {
removeHook(hookList[i],labelledHooks[label][hookList[i]])
}
delete labelledHooks[label];
} else if (labelledHooks[label][id]) {
removeHook(id,labelledHooks[label][id])
delete labelledHooks[label][id];
if (Object.keys(labelledHooks[label]).length === 0){
delete labelledHooks[label];
}
}
}
}
function removeHook(id,hookItem) {
var previousHook = hookItem.previousHook;
var nextHook = hookItem.nextHook;
if (previousHook) {
previousHook.nextHook = nextHook;
} else {
hooks[id] = nextHook;
}
if (nextHook) {
nextHook.previousHook = previousHook;
}
hookItem.removed = true;
if (!previousHook && !nextHook) {
delete hooks[id];
}
}
function trigger(hookId, payload, done) {
var hookItem = hooks[hookId];
if (!hookItem) {
if (done) {
done();
}
return;
}
function callNextHook(err) {
if (!hookItem || err) {
if (done) { done(err) }
return err;
}
if (hookItem.removed) {
hookItem = hookItem.nextHook;
return callNextHook();
}
var callback = hookItem.cb;
if (callback.length === 1) {
try {
let result = callback(payload);
if (result === false) {
// Halting the flow
if (done) { done(false) }
return result;
}
hookItem = hookItem.nextHook;
return callNextHook();
} catch(e) {
console.warn(e);
if (done) { done(e);}
return e;
}
} else {
// There is a done callback
try {
callback(payload,function(result) {
if (result === undefined) {
hookItem = hookItem.nextHook;
callNextHook();
} else {
if (done) { done(result)}
}
})
} catch(e) {
console.warn(e);
if (done) { done(e) }
return e;
}
}
}
return callNextHook();
}
function clear() {
hooks = {}
labelledHooks = {}
}
function has(hookId) {
var parts = hookId.split(".");
var id = parts[0], label = parts[1];
if (label) {
return !!(labelledHooks[label] && labelledHooks[label][id])
}
return !!hooks[id]
}
return {
has: has,
clear: clear,
add: add,
remove: remove,
trigger: trigger
}
})();

View File

@@ -18,53 +18,69 @@ RED.i18n = (function() {
var apiRootUrl;
function detectLanguage() {
return navigator.language
}
return {
init: function(options, done) {
apiRootUrl = options.apiRootUrl||"";
var preferredLanguage = localStorage.getItem("editor-language");
var preferredLanguage = localStorage.getItem("editor-language") || detectLanguage();
var opts = {
resGetPath: apiRootUrl+'locales/__ns__?lng=__lng__',
dynamicLoad: false,
load:'current',
ns: {
namespaces: ["editor","node-red","jsonata","infotips"],
defaultNs: "editor"
compatibilityJSON: 'v3',
backend: {
loadPath: apiRootUrl+'locales/__ns__?lng=__lng__',
},
lng: 'en-US',
// debug: true,
preload:['en-US'],
ns: ["editor","node-red","jsonata","infotips"],
defaultNS: "editor",
fallbackLng: ['en-US'],
useCookie: false,
returnObjectTrees: true
returnObjects: true,
keySeparator: ".",
nsSeparator: ":",
interpolation: {
unescapeSuffix: 'HTML',
escapeValue: false,
prefix: '__',
suffix: '__'
}
};
if (preferredLanguage) {
opts.lng = preferredLanguage;
}
i18n.init(opts,function() {
i18next.use(i18nextHttpBackend).init(opts,function() {
done();
});
jqueryI18next.init(i18next, $, { handleName: 'i18n' });
RED["_"] = function() {
var v = i18n.t.apply(null,arguments);
var v = i18next.t.apply(i18next,arguments);
if (typeof v === 'string') {
return v;
} else {
return arguments[0];
}
}
},
lang: function() {
// Gets the active message catalog language. This is based on what
// locale the editor is using and what languages are available.
//
var preferredLangs = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
var preferredLangs = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages);
var knownLangs = RED.settings.theme("languages")||["en-US"];
for (var i=0;i<preferredLangs.length;i++) {
if (knownLangs.indexOf(preferredLangs[i]) > -1) {
return preferredLangs[i]
}
}
return 'end-US'
return 'en-US'
},
loadNodeCatalog: function(namespace,done) {
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages);
var toLoad = languageList.length;
languageList.forEach(function(lang) {
$.ajax({
@@ -74,7 +90,7 @@ RED.i18n = (function() {
cache: false,
url: apiRootUrl+'nodes/'+namespace+'/messages?lng='+lang,
success: function(data) {
i18n.addResourceBundle(lang,namespace,data);
i18next.addResourceBundle(lang,namespace,data);
toLoad--;
if (toLoad === 0) {
done();
@@ -86,7 +102,7 @@ RED.i18n = (function() {
},
loadNodeCatalogs: function(done) {
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages);
var toLoad = languageList.length;
languageList.forEach(function(lang) {
@@ -99,7 +115,7 @@ RED.i18n = (function() {
success: function(data) {
var namespaces = Object.keys(data);
namespaces.forEach(function(ns) {
i18n.addResourceBundle(lang,ns,data[ns]);
i18next.addResourceBundle(lang,ns,data[ns]);
});
toLoad--;
if (toLoad === 0) {
@@ -111,7 +127,7 @@ RED.i18n = (function() {
},
loadPluginCatalogs: function(done) {
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
var languageList = [localStorage.getItem("editor-language")|| detectLanguage()].concat(i18next.languages);
var toLoad = languageList.length;
languageList.forEach(function(lang) {
@@ -124,7 +140,7 @@ RED.i18n = (function() {
success: function(data) {
var namespaces = Object.keys(data);
namespaces.forEach(function(ns) {
i18n.addResourceBundle(lang,ns,data[ns]);
i18next.addResourceBundle(lang,ns,data[ns]);
});
toLoad--;
if (toLoad === 0) {
@@ -133,6 +149,7 @@ RED.i18n = (function() {
}
});
})
}
},
detectLanguage: detectLanguage
}
})();

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

@@ -36,7 +36,7 @@ var RED = (function() {
}
function loadPlugins(done) {
loader.reportProgress(RED._("event.loadPlugins",{count:""}), 17)
var lang = localStorage.getItem("editor-language")||i18n.detectLanguage();
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
@@ -176,7 +176,7 @@ var RED = (function() {
function loadNodes() {
loader.reportProgress(RED._("event.loadNodes",{count:""}), 30)
var lang = localStorage.getItem("editor-language")||i18n.detectLanguage();
var lang = localStorage.getItem("editor-language")||RED.i18n.detectLanguage();
$.ajax({
headers: {
@@ -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();
}
@@ -711,7 +775,7 @@ var RED = (function() {
throw new Error("RED already initialised");
}
initialised = true;
ace.require("ace/ext/language_tools");
if(window.ace) { window.ace.require("ace/ext/language_tools"); }
options = options || {};
options.apiRootUrl = options.apiRootUrl || "";
if (options.apiRootUrl && !/\/$/.test(options.apiRootUrl)) {

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");
}
@@ -143,22 +159,13 @@ RED.settings = (function () {
console.groupCollapsed("Versions");
console.log("jQuery",$().jquery)
console.log("jQuery UI",$.ui.version);
console.log("ACE",ace.version);
if(window.ace) { console.log("ACE",ace.version); }
if(window.monaco) { console.log("MONACO",monaco.version || "unknown"); }
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) {
@@ -219,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") {
@@ -1186,22 +1208,6 @@ RED.clipboard = (function() {
}
}
function getNodeLabelText(n) {
var label = n.name || n.type+": "+n.id;
if (n._def.label) {
try {
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
} catch(err) {
console.log("Definition error: "+n.type+".label",err);
}
}
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
return label;
}
function getFlowLabel(n) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
@@ -1227,16 +1233,8 @@ RED.clipboard = (function() {
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html(n.type)
}
var div = $('<div>',{class:"red-ui-node-list-item"});
RED.utils.createNodeIcon(n,true).appendTo(div);
return div;
}
@@ -1284,22 +1282,27 @@ RED.clipboard = (function() {
hideDropTarget();
})
.on("drop",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
try {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
}
}
} catch(err) {
// Ensure any errors throw above doesn't stop the drop target from
// being hidden.
}
hideDropTarget();
event.preventDefault();

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

@@ -18,7 +18,7 @@
/**
* options:
* - addButton : boolean|string - text for add label, default 'add'
* - buttons : array - list of custom buttons (objects with fields 'label', 'icon', 'title', 'click')
* - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click')
* - height : number|'auto'
* - resize : function - called when list as a whole is resized
* - resizeItem : function(item) - called to resize individual item
@@ -71,7 +71,7 @@
var buttons = this.options.buttons || [];
if (this.options.addButton !== false) {
var addLabel, addTittle;
var addLabel, addTitle;
if (typeof this.options.addButton === 'string') {
addLabel = this.options.addButton
} else {
@@ -94,7 +94,7 @@
}
buttons.forEach(function(button) {
var element = $('<a href="#" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></a>')
var element = $('<button type="button" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></button>')
.appendTo(that.topContainer)
.on("click", function(evt) {
evt.preventDefault();
@@ -102,7 +102,10 @@
button.click(evt);
}
});
if (button.id) {
element.attr("id", button.id);
}
if (button.title) {
element.attr("title", button.title);
}
@@ -113,7 +116,7 @@
element.append($("<span></span>").text(" " + button.label));
}
});
if (this.element.css("position") === "absolute") {
["top","left","bottom","right"].forEach(function(s) {
var v = that.element.css(s);

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,17 +97,78 @@ 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;
if (options.scrollable) {
wrapper.addClass("red-ui-tabs-scrollable");
scrollContainer.addClass("red-ui-tabs-scroll-container");
scrollContainer.on("scroll",updateScroll);
scrollContainer.on("scroll",function(evt) {
// Generated by trackpads - not mousewheel
updateScroll(evt);
});
scrollContainer.on("wheel", function(evt) {
if (evt.originalEvent.deltaX === 0) {
// Prevent the scroll event from firing
evt.preventDefault();
// Assume this is wheel event which might not trigger
// the scroll event, so do things manually
var sl = scrollContainer.scrollLeft();
sl -= evt.originalEvent.deltaY;
scrollContainer.scrollLeft(sl);
}
})
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) {
@@ -322,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"});
@@ -347,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"));
}
@@ -363,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;
@@ -373,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
@@ -431,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();
@@ -471,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})
}
}
}
@@ -648,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")
@@ -657,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);
@@ -664,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);
}
@@ -768,19 +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()
},
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 = [];
@@ -312,7 +339,8 @@
}
if (child.depth !== parent.depth+1) {
child.depth = parent.depth+1;
var labelPaddingWidth = ((child.gutter?child.gutter.width()+2:0)+(child.depth*20));
// var labelPaddingWidth = ((child.gutter ? child.gutter[0].offsetWidth + 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({
@@ -348,6 +376,18 @@
that._selected.delete(item);
delete item.treeList;
delete that._items[item.id];
if(item.depth === 0) {
for(var key in that._items) {
if (that._items.hasOwnProperty(key)) {
var child = that._items[key];
if(child.parent && child.parent.id === item.id) {
delete that._items[key].treeList;
delete that._items[key];
}
}
}
that._data = that._data.filter(function(data) { return data.id !== item.id})
}
}
item.treeList.insertChildAt = function(newItem,position,select) {
newItem.parent = item;
@@ -449,6 +489,9 @@
container.addClass("expanded");
}
item.treeList.collapse = function() {
if (item.collapsible === false) {
return
}
if (!item.children) {
return;
}
@@ -480,7 +523,10 @@
if (item.treeList.container) {
$(item.element).remove();
$(element).appendTo(item.treeList.label);
var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(item.depth*20);
// using the JQuery Object, the gutter width will
// be wrong when the element is reattached the second time
var labelPaddingWidth = (item.gutter ? item.gutter[0].offsetWidth + 2 : 0) + (item.depth * 20);
$(element).css({
width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)"
})
@@ -516,9 +562,12 @@
}).appendTo(label)
}
var labelPaddingWidth = (item.gutter?item.gutter.width()+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);
@@ -564,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();
@@ -615,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) {
@@ -623,6 +712,7 @@
}
}
selectWrapper.appendTo(label)
item.treeList.radio = cb;
} else {
label.on("click", function(e) {
if (!that.options.multi) {
@@ -630,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);
}
@@ -781,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)
}
@@ -788,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

@@ -15,7 +15,7 @@
**/
(function($) {
var contextParse = function(v,defaultStore) {
var parts = RED.utils.parseContextKey(v, defaultStore);
var parts = RED.utils.parseContextKey(v, defaultStore&&defaultStore.value);
return {
option: parts.store,
value: parts.key
@@ -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", {
@@ -279,6 +400,14 @@
var contextStores = RED.settings.context.stores;
var contextOptions = contextStores.map(function(store) {
return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database"></i>'}
}).sort(function(A,B) {
if (A.value === RED.settings.context.default) {
return -1;
} else if (B.value === RED.settings.context.default) {
return 1;
} else {
return A.value.localeCompare(B.value);
}
})
if (contextOptions.length < 2) {
allOptions.flow.options = [];
@@ -290,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);
@@ -320,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");
@@ -372,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();
}
@@ -389,14 +524,19 @@
evt.stopPropagation();
}).on('focus', function() {
that.uiSelect.addClass('red-ui-typedInput-focus');
}).on('blur', function() {
var opt = that.typeMap[that.propertyType];
if (opt.hasValue === false) {
that.uiSelect.removeClass('red-ui-typedInput-focus');
}
})
// 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();
@@ -415,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);
}
@@ -438,7 +580,11 @@
});
this._showMenu(this.optionMenu,this.optionSelectTrigger);
var selectedOption = this.optionMenu.find("[value='"+this.optionValue+"']");
var targetValue = this.optionValue;
if (this.optionValue === null || this.optionValue === undefined) {
targetValue = this.value();
}
var selectedOption = this.optionMenu.find("[value='"+targetValue+"']");
if (selectedOption.length === 0) {
selectedOption = this.optionMenu.children(":first");
}
@@ -562,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) {
@@ -659,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') {
@@ -669,6 +816,13 @@
that.typeMap[result.value] = result;
return result;
});
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)
if (this.menu) {
@@ -676,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) {
@@ -690,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) {
@@ -698,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) {
@@ -743,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);
}
@@ -768,6 +1000,11 @@
if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) {
this.selectLabel.text(opt.label);
}
if (opt.label) {
this.selectTrigger.attr("title",opt.label);
} else {
this.selectTrigger.attr("title","");
}
if (opt.hasValue === false) {
this.selectTrigger.addClass("red-ui-typedInput-full-width");
} else {
@@ -809,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);
@@ -835,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)) {
@@ -879,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 {
@@ -911,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) {
@@ -925,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) {
@@ -1004,16 +1223,20 @@
this.uiSelect.hide();
},
disable: function(val) {
if(val === true) {
if(val === undefined || !!val ) {
this.uiSelect.attr("disabled", "disabled");
} else if (val === false) {
this.uiSelect.attr("disabled", null); //remove attr
} else {
this.uiSelect.attr("disabled", val); //user value
this.uiSelect.attr("disabled", null); //remove attr
}
},
enable: function() {
this.uiSelect.attr("disabled", null); //remove attr
},
disabled: function() {
return this.uiSelect.attr("disabled");
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

@@ -0,0 +1,107 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* @namespace RED.editor.codeEditor
*/
RED.editor.codeEditor = (function() {
const MONACO = "monaco";
const ACE = "ace";
const defaultEditor = ACE;
const DEFAULT_SETTINGS = { lib: defaultEditor, options: {} };
var selectedCodeEditor = null;
var initialised = false;
function init() {
var codeEditorSettings = RED.editor.codeEditor.settings;
var editorChoice = codeEditorSettings.lib === MONACO ? MONACO : ACE;
try {
var browser = RED.utils.getBrowserInfo();
selectedCodeEditor = RED.editor.codeEditor[editorChoice];
//fall back to default code editor if there are any issues
if (!selectedCodeEditor || (editorChoice === MONACO && (browser.ie || !window.monaco))) {
selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
}
initialised = selectedCodeEditor.init();
} catch (error) {
selectedCodeEditor = null;
console.warn("Problem initialising '" + editorChoice + "' code editor", error);
}
if(!initialised) {
selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
initialised = selectedCodeEditor.init();
}
}
function create(options) {
//TODO: (quandry - for consideration)
// Below, I had to create a hidden element if options.id || options.element is not in the DOM
// I have seen 1 node calling `this.editor = RED.editor.createEditor()` with an
// invalid (non existing html element selector) (e.g. node-red-contrib-components does this)
// This causes monaco to throw an error when attempting to hook up its events to the dom & the rest of the 'oneditperapre'
// code is thus skipped.
// In ACE mode, creating an ACE editor (with an invalid ID) allows the editor to be created (but obviously there is no UI)
// Because one (or more) contrib nodes have left this bad code in place, how would we handle this?
// For compatibility, I have decided to create a hidden element so that at least an editor is created & errors do not occur.
// IMO, we should warn and exit as it is a coding error by the contrib author.
if (!options) {
console.warn("createEditor() options are missing");
options = {};
}
if (this.editor.type === MONACO) {
// compatibility (see above note)
if (!options.element && !options.id) {
options.id = 'node-backwards-compatability-dummy-editor';
}
options.element = options.element || $("#" + options.id)[0];
if (!options.element) {
console.warn("createEditor() options.element or options.id is not valid", options);
$("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />');
}
return this.editor.create(options);
} else {
return this.editor.create(options);//fallback to ACE
}
}
return {
init: init,
/**
* Get editor settings object
* @memberof RED.editor.codeEditor
*/
get settings() {
return RED.settings.get('codeEditor') || DEFAULT_SETTINGS;
},
/**
* Get user selected code editor
* @return {string} Returns
* @memberof RED.editor.codeEditor
*/
get editor() {
return selectedCodeEditor;
},
/**
* Create a editor ui component
* @param {object} options - the editor options
* @memberof RED.editor.codeEditor
*/
create: create
}
})();

View File

@@ -0,0 +1,153 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* @namespace RED.editor.codeEditor.ace
*/
RED.editor.codeEditor.ace = (function() {
const type = "ace";
var initialised = false;
var initOptions = {};
function init(options) {
initOptions = options || {};
initialised = true;
return initialised;
}
function create(options) {
var editorSettings = RED.editor.codeEditor.settings || {};
var el = options.element || $("#"+options.id)[0];
var toolbarRow = $("<div>").appendTo(el);
el = $("<div>").appendTo(el).addClass("red-ui-editor-text-container")[0];
var editor = window.ace.edit(el);
editor.setTheme(editorSettings.theme || initOptions.theme || "ace/theme/tomorrow");
var session = editor.getSession();
session.on("changeAnnotation", function () {
var annotations = session.getAnnotations() || [];
var i = annotations.length;
var len = annotations.length;
while (i--) {
if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
}
if (len > annotations.length) { session.setAnnotations(annotations); }
});
if (options.mode) {
session.setMode(options.mode);
}
if (options.foldStyle) {
session.setFoldStyle(options.foldStyle);
} else {
session.setFoldStyle('markbeginend');
}
if (options.options) {
editor.setOptions(options.options);
} else {
editor.setOptions({
enableBasicAutocompletion:true,
enableSnippets:true,
tooltipFollowsMouse: false
});
}
if (options.readOnly) {
editor.setOption('readOnly',options.readOnly);
editor.container.classList.add("ace_read-only");
}
if (options.hasOwnProperty('lineNumbers')) {
editor.renderer.setOption('showGutter',options.lineNumbers);
}
editor.$blockScrolling = Infinity;
if (options.value) {
session.setValue(options.value,-1);
}
if (options.globals) {
setTimeout(function() {
if (!!session.$worker) {
session.$worker.send("setOptions", [{globals: options.globals, maxerr:1000}]);
}
},100);
}
if (options.mode === 'ace/mode/markdown') {
$(el).addClass("red-ui-editor-text-container-toolbar");
editor.toolbar = RED.editor.customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
if (options.expandable !== false) {
var expandButton = $('<button type="button" class="red-ui-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar);
RED.popover.tooltip(expandButton, RED._("markdownEditor.expand"));
expandButton.on("click", function(e) {
e.preventDefault();
var value = editor.getValue();
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: editor.getCursorPosition(),
complete: function(v,cursor) {
editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
editor.focus();
},300);
}
})
});
}
var helpButton = $('<button type="button" class="red-ui-editor-text-help red-ui-button red-ui-button-small"><i class="fa fa-question"></i></button>').appendTo($(el).parent());
RED.popover.create({
target: helpButton,
trigger: 'click',
size: "small",
direction: "left",
content: RED._("markdownEditor.format"),
autoClose: 50
});
session.setUseWrapMode(true);
}
editor._destroy = editor.destroy;
editor.destroy = function() {
try {
this._destroy();
} catch (e) { }
$(el).remove();
$(toolbarRow).remove();
}
editor.type = type;
return editor;
}
return {
/**
* Editor type
* @memberof RED.editor.codeEditor.ace
*/
get type() { return type; },
/**
* Editor initialised
* @memberof RED.editor.codeEditor.ace
*/
get initialised() { return initialised; },
/**
* Initialise code editor
* @param {object} options - initialisation options
* @memberof RED.editor.codeEditor.ace
*/
init: init,
/**
* Create a code editor
* @param {object} options - the editor options
* @memberof RED.editor.codeEditor.ace
*/
create: create
}
})();

File diff suppressed because it is too large Load Diff

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

@@ -121,73 +121,75 @@
var currentFunctionMarker = null;
expressionEditor.getSession().setValue(value||"",-1);
expressionEditor.on("changeSelection", function() {
var c = expressionEditor.getCursorPosition();
var token = expressionEditor.getSession().getTokenAt(c.row,c.column);
if (token !== currentToken || (token && /paren/.test(token.type) && c.column !== currentTokenPos)) {
currentToken = token;
var r,p;
var scopedFunction = null;
if (token && token.type === 'keyword') {
r = c.row;
scopedFunction = token;
} else {
var depth = 0;
var next = false;
if (token) {
if (token.type === 'paren.rparen') {
// If this is a block of parens ')))', set
// depth to offset against the cursor position
// within the block
currentTokenPos = c.column;
depth = c.column - (token.start + token.value.length);
}
//ace only (monaco has jsonata tokeniser)
if(expressionEditor.type == "ace") {
expressionEditor.on("changeSelection", function() {
var c = expressionEditor.getCursorPosition();
var token = expressionEditor.getSession().getTokenAt(c.row,c.column);
if (token !== currentToken || (token && /paren/.test(token.type) && c.column !== currentTokenPos)) {
currentToken = token;
var r,p;
var scopedFunction = null;
if (token && token.type === 'keyword') {
r = c.row;
p = token.index;
scopedFunction = token;
} else {
r = c.row-1;
p = -1;
}
while ( scopedFunction === null && r > -1) {
var rowTokens = expressionEditor.getSession().getTokens(r);
if (p === -1) {
p = rowTokens.length-1;
var depth = 0;
var next = false;
if (token) {
if (token.type === 'paren.rparen') {
// If this is a block of parens ')))', set
// depth to offset against the cursor position
// within the block
currentTokenPos = c.column;
depth = c.column - (token.start + token.value.length);
}
r = c.row;
p = token.index;
} else {
r = c.row-1;
p = -1;
}
while (p > -1) {
var type = rowTokens[p].type;
if (next) {
if (type === 'keyword') {
scopedFunction = rowTokens[p];
// console.log("HIT",scopedFunction);
break;
while ( scopedFunction === null && r > -1) {
var rowTokens = expressionEditor.getSession().getTokens(r);
if (p === -1) {
p = rowTokens.length-1;
}
while (p > -1) {
var type = rowTokens[p].type;
if (next) {
if (type === 'keyword') {
scopedFunction = rowTokens[p];
// console.log("HIT",scopedFunction);
break;
}
next = false;
}
next = false;
if (type === 'paren.lparen') {
depth-=rowTokens[p].value.length;
} else if (type === 'paren.rparen') {
depth+=rowTokens[p].value.length;
}
if (depth < 0) {
next = true;
depth = 0;
}
// console.log(r,p,depth,next,rowTokens[p]);
p--;
}
if (type === 'paren.lparen') {
depth-=rowTokens[p].value.length;
} else if (type === 'paren.rparen') {
depth+=rowTokens[p].value.length;
if (!scopedFunction) {
r--;
}
if (depth < 0) {
next = true;
depth = 0;
}
// console.log(r,p,depth,next,rowTokens[p]);
p--;
}
if (!scopedFunction) {
r--;
}
}
expressionEditor.session.removeMarker(currentFunctionMarker);
if (scopedFunction) {
//console.log(token,.map(function(t) { return t.type}));
funcSelect.val(scopedFunction.value).trigger("change");
}
}
expressionEditor.session.removeMarker(currentFunctionMarker);
if (scopedFunction) {
//console.log(token,.map(function(t) { return t.type}));
funcSelect.val(scopedFunction.value).trigger("change");
}
}
});
});
}
dialogForm.i18n();
$("#red-ui-editor-type-expression-func-insert").on("click", function(e) {
e.preventDefault();
@@ -343,6 +345,7 @@
}
expressionEditor.destroy();
testDataEditor.destroy();
testResultEditor.destroy();
},
show: function() {}
}

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

@@ -602,10 +602,10 @@
},
close: function() {
// expressionEditor.destroy();
if (options.onclose) {
options.onclose();
}
expressionEditor.destroy();
},
show: function() {}
}

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

@@ -73,6 +73,7 @@ RED.eventLog = (function() {
var trayBody = tray.find('.red-ui-tray-body');
var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor');
eventLogEditor = RED.editor.createEditor({
mode:"ace/mode/shell",
id: 'red-ui-event-log-editor',
value: backlog.join("\n"),
lineNumbers: false,

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

@@ -119,7 +119,7 @@ RED.keyboard = (function() {
} else {
mergedKeymap[action] = [{
scope: themeKeymap[action].scope || "*",
key: [themeKeymap[action].key],
key: themeKeymap[action].key,
user: false
}]
if (mergedKeymap[action][0].scope === "workspace") {
@@ -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

@@ -27,7 +27,7 @@ RED.library = (function() {
'<div class="red-ui-panel" id="red-ui-library-dialog-load-browser"></div>'+
'<div class="red-ui-panel">'+
'<div id="red-ui-library-dialog-load-preview">'+
'<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-text"></div>'+
'<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-text" style="position:relative; height: 50%; overflow-y: hidden;"></div>'+
'<div class="red-ui-panel" id="red-ui-library-dialog-load-preview-details">'+
'<table id="red-ui-library-dialog-load-preview-details-table" class="red-ui-info-table"></table>'+
'</div>'+
@@ -216,21 +216,27 @@ RED.library = (function() {
{ id:'node-input-'+options.type+'-menu-open-library',
label: RED._("library.openLibrary"),
onselect: function() {
libraryEditor = ace.edit('red-ui-library-dialog-load-preview-text',{
useWorker: false
});
libraryEditor.setTheme("ace/theme/tomorrow");
if (options.mode) {
libraryEditor.getSession().setMode(options.mode);
}
libraryEditor.setOptions({
var editorOpts = {
id: 'red-ui-library-dialog-load-preview-text',
mode: options.mode,
readOnly: true,
highlightActiveLine: false,
highlightGutterLine: false
});
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
libraryEditor.$blockScrolling = Infinity;
highlightGutterLine: false,
contextmenu: false
}
libraryEditor = RED.editor.createEditor(editorOpts); //use red.editor
if(libraryEditor.isACE) {
if (options.mode) {
libraryEditor.getSession().setMode(options.mode);
}
libraryEditor.setOptions({
readOnly: true,
highlightActiveLine: false,
highlightGutterLine: false
});
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
libraryEditor.$blockScrolling = Infinity;
}
activeLibrary = options;
var listing = [];
@@ -530,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);
@@ -809,6 +815,7 @@ RED.library = (function() {
open: function(e) {
RED.keyboard.disable();
$(this).parent().find(".ui-dialog-titlebar-close").hide();
libraryEditor.resize();
},
close: function(e) {
RED.keyboard.enable();

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) === 1) {
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

@@ -320,12 +320,12 @@ RED.palette = (function() {
var paletteNode = getPaletteNode(nt);
ui.originalPosition.left = paletteNode.offset().left;
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
if (!groupTimer) {
groupTimer = setTimeout(function() {
mouseX /= RED.view.scale();
mouseY /= RED.view.scale();
var group = RED.view.getGroupAtPoint(mouseX,mouseY);
var mx = mouseX / RED.view.scale();
var my = mouseY / RED.view.scale();
var group = RED.view.getGroupAtPoint(mx,my);
if (group !== hoverGroup) {
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
@@ -357,23 +357,20 @@ RED.palette = (function() {
svgRect.width = 1;
svgRect.height = 1;
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
mouseX /= RED.view.scale();
mouseY /= RED.view.scale();
} else {
// Firefox doesn't do getIntersectionList and that
// makes us sad
mouseX /= RED.view.scale();
mouseY /= RED.view.scale();
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
}
var mx = mouseX / RED.view.scale();
var my = mouseY / RED.view.scale();
for (var i=0;i<nodes.length;i++) {
var node = d3.select(nodes[i]);
if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
var length = nodes[i].getTotalLength();
for (var j=0;j<length;j+=10) {
var p = nodes[i].getPointAtLength(j);
var d2 = ((p.x-mouseX)*(p.x-mouseX))+((p.y-mouseY)*(p.y-mouseY));
var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
if (d2 < 200 && d2 < bestDistance) {
bestDistance = d2;
bestLink = nodes[i];

View File

@@ -928,11 +928,11 @@ RED.projects.settings = (function() {
saveDisabled = isFlowInvalid || credFileLabelText.text()==="";
if (credentialSecretExistingInput.is(":visible")) {
if (credentialSecretExistingRow.is(":visible")) {
credentialSecretExistingInput.toggleClass("input-error", credentialSecretExistingInput.val() === "");
saveDisabled = saveDisabled || credentialSecretExistingInput.val() === "";
}
if (credentialSecretNewInput.is(":visible")) {
if (credentialSecretNewRow.is(":visible")) {
credentialSecretNewInput.toggleClass("input-error", credentialSecretNewInput.val() === "");
saveDisabled = saveDisabled || credentialSecretNewInput.val() === "";
}
@@ -1130,7 +1130,7 @@ RED.projects.settings = (function() {
}
if (credentialSecretResetButton.hasClass('selected') || credentialSecretEditButton.hasClass('selected')) {
payload.credentialSecret = credentialSecretNewInput.val();
if (credentialSecretExistingInput.is(":visible")) {
if (credentialSecretExistingRow.is(":visible")) {
payload.currentCredentialSecret = credentialSecretExistingInput.val();
}
}

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

@@ -199,7 +199,7 @@ RED.sidebar = (function() {
id = RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0]
}
if (id) {
if (!containsTab(id)) {
if (!containsTab(id) && knownTabs[id]) {
sidebar_tabs.addTab(knownTabs[id]);
}
sidebar_tabs.activateTab(id);

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,
@@ -230,14 +255,21 @@ RED.sidebar.help = (function() {
}
function getNodeLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
$('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.name||n.type).appendTo(contentDiv);
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 && n._def.paletteLabel) {
try {
label = (typeof n._def.paletteLabel === "function" ? n._def.paletteLabel.call(n._def) : n._def.paletteLabel)||"";
} catch (err) {
}
}
label = label || n.type;
$('<div>',{class:"red-ui-node-label"}).text(n.name||n.type).appendTo(icon);
return div;
}
function showHelp(nodeType) {
function showNodeTypeHelp(nodeType) {
helpSection.empty();
var helpText;
var title;
@@ -248,9 +280,17 @@ RED.sidebar.help = (function() {
title = subflowNode.name || nodeType;
} else {
helpText = RED.nodes.getNodeHelp(nodeType)||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
title = nodeType;
var _def = RED.nodes.registry.getNodeType(nodeType);
title = (_def && _def.paletteLabel)?_def.paletteLabel:nodeType;
if (typeof title === "function") {
try {
title = _def.paletteLabel.call(_def);
} catch(err) {
title = nodeType;
}
}
}
setInfoText(title, helpText, helpSection);
setInfoText(title, helpText);
var ratio = panels.ratio();
if (ratio > 0.7) {
@@ -267,7 +307,7 @@ RED.sidebar.help = (function() {
}
if (type) {
// hideTOC();
showHelp(type);
showNodeTypeHelp(type);
}
resizeStack();
}
@@ -283,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>')
@@ -301,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();
}
@@ -321,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

@@ -73,36 +73,11 @@ RED.sidebar.info.outliner = (function() {
return item;
}
function getNodeLabelText(n) {
var label = n.name || n.type+": "+n.id;
if (n._def.label) {
try {
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
} catch(err) {
console.log("Definition error: "+n.type+".label",err);
}
}
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
return label;
}
function getNodeLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
var div = $('<div>',{class:"red-ui-node-list-item red-ui-info-outline-item"});
RED.utils.createNodeIcon(n, true).appendTo(div);
div.find(".red-ui-node-label").addClass("red-ui-info-outline-item-label")
addControls(n, div);
return div;
}
@@ -147,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();
@@ -191,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);
@@ -219,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);
@@ -288,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"},
]
});
@@ -303,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:[]})
}
}
})
@@ -334,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,
@@ -392,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,
@@ -430,7 +449,7 @@ RED.sidebar.info.outliner = (function() {
var existingObject = objects[n.id];
var parent = n.g||n.z||"__global__";
var nodeLabelText = getNodeLabelText(n);
var nodeLabelText = RED.utils.getNodeLabel(n,n.name || (n.type+": "+n.id));
if (nodeLabelText) {
existingObject.element.find(".red-ui-info-outline-item-label").text(nodeLabelText);
} else {
@@ -547,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
@@ -477,7 +481,7 @@ RED.sidebar.info = (function() {
return;
}
}
while ((m=/(\[(.*?)\])/.exec(tip))) {
while ((m=/(\[([a-z]*?)\])/.exec(tip))) {
tip = tip.replace(m[1],RED.keyboard.formatKey(m[2]));
}
tipBox.html(tip).fadeIn(200);
@@ -499,7 +503,7 @@ RED.sidebar.info = (function() {
if (tipCount === -1) {
do {
tipCount++;
} while(RED._("infotips:info.tip"+tipCount)!=="infotips:info.tip"+tipCount);
} while(RED._("infotips:info.tip"+tipCount)!=="info.tip"+tipCount);
}
startTimeout = setTimeout(setTip,startDelay);
}

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

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.tray = (function() {
RED.tray = (function() {
var stack = [];
var editorStack;
@@ -166,6 +166,8 @@ RED.tray = (function() {
setTimeout(function() {
// Delay resetting the flag, so we don't close prematurely
openingTray = false;
raiseTrayZ();
handleWindowResize();//cause call to monaco layout
},200);
body.find(":focusable:first").trigger("focus");
@@ -206,6 +208,17 @@ RED.tray = (function() {
}
}
//raise tray z-index to prevent editor context menu being clipped by sidebar
function raiseTrayZ() {
setTimeout(function(){
$('#red-ui-editor-stack').css("zIndex","13");
},300);
}
//lower tray z-index back to original place for correct slide animation (related to fix for editor context menu clipped by sidebar)
function lowerTrayZ(){
$('#red-ui-editor-stack').css("zIndex","9");
}
return {
init: function init() {
editorStack = $("#red-ui-editor-stack");
@@ -221,6 +234,7 @@ RED.tray = (function() {
});
},
show: function show(options) {
lowerTrayZ();
if (!options) {
if (stack.length > 0) {
var tray = stack[stack.length-1];
@@ -246,12 +260,16 @@ RED.tray = (function() {
showTray(options);
},250)
} else {
if (stack.length > 0) {
stack[stack.length-1].tray.css("z-index", 0);
}
RED.events.emit("editor:open");
showTray(options);
}
},
hide: function hide() {
lowerTrayZ();
if (stack.length > 0) {
var tray = stack[stack.length-1];
tray.tray.css({
@@ -266,15 +284,16 @@ RED.tray = (function() {
},
resize: handleWindowResize,
close: function close(done) {
lowerTrayZ(); //lower tray z-index for correct animation
if (stack.length > 0) {
var tray = stack.pop();
tray.tray.css({
right: -(tray.tray.width()+10)+"px"
});
setTimeout(function() {
if (tray.options.close) {
tray.options.close();
}
try {
if (tray.options.close) { tray.options.close(); }
} catch (ex) { }
tray.tray.remove();
if (stack.length > 0) {
var oldTray = stack[stack.length-1];
@@ -284,6 +303,8 @@ RED.tray = (function() {
handleWindowResize();
oldTray.tray.css({right:0});
if (oldTray.options.show) {
raiseTrayZ();
handleWindowResize();//cause call to monaco layout
oldTray.options.show();
}
},0);
@@ -304,6 +325,8 @@ RED.tray = (function() {
$(".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>');
}
@@ -178,8 +260,14 @@ RED.utils = (function() {
RED.popover.tooltip(pinPath,RED._("node-red:debug.sidebar.pinPath"));
}
if (extraTools) {
extraTools.addClass("red-ui-debug-msg-tools-other");
extraTools.appendTo(tools);
var t = extraTools;
if (typeof t === 'function') {
t = t(key,msg);
}
if (t) {
t.addClass("red-ui-debug-msg-tools-other");
t.appendTo(tools);
}
}
}
function checkExpanded(strippedKey,expandPaths,minRange,maxRange) {
@@ -344,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;
}
@@ -354,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')) {
@@ -411,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');
@@ -476,7 +566,9 @@ RED.utils = (function() {
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -504,8 +596,9 @@ RED.utils = (function() {
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -525,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);
@@ -543,7 +642,7 @@ RED.utils = (function() {
}
}
subElements[newPath] = buildMessageElement(
obj[keys[i]],
data[keys[i]],
{
key: keys[i],
typeHint: false,
@@ -553,8 +652,9 @@ RED.utils = (function() {
rootPath: rootPath,
expandPaths: expandPaths,
ontoggle: ontoggle,
exposeApi: exposeApi
exposeApi: exposeApi,
// tools: tools // Do not pass tools down as we
// keep them attached to the top-level header
}
).appendTo(row);
}
@@ -566,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);
@@ -574,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);
}
@@ -856,6 +956,7 @@ RED.utils = (function() {
obj[key] = value;
}
}
function separateIconPath(icon) {
var result = {module: "", file: ""};
if (icon) {
@@ -863,10 +964,10 @@ RED.utils = (function() {
if (index === 0) {
icon = icon.substring((RED.settings.apiRootUrl+'icons/').length);
}
index = icon.indexOf('/');
if (index !== -1) {
result.module = icon.slice(0, index);
result.file = icon.slice(index + 1);
var match = /^((?:@[^/]+\/)?[^/]+)\/(.*)$/.exec(icon);
if (match) {
result.module = match[1];
result.file = match[2];
} else {
result.file = icon;
}
@@ -875,6 +976,7 @@ RED.utils = (function() {
}
function getDefaultNodeIcon(def,node) {
def = def || {};
var icon_url;
if (node && node.type === "subflow") {
icon_url = "node-red/subflow.svg";
@@ -912,6 +1014,7 @@ RED.utils = (function() {
}
function getNodeIcon(def,node) {
def = def || {};
if (node && node.type === '_selection_') {
return "font-awesome/fa-object-ungroup";
} else if (node && node.type === 'group') {
@@ -999,6 +1102,7 @@ RED.utils = (function() {
}
function getNodeColor(type, def) {
def = def || {};
var result = def.color;
var paletteTheme = RED.settings.theme('palette.theme') || [];
if (paletteTheme.length > 0) {
@@ -1051,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);
@@ -1125,9 +1229,11 @@ RED.utils = (function() {
imageIconElement.css("backgroundImage", "url("+iconUrl+")");
}
function createNodeIcon(node) {
function createNodeIcon(node, includeLabel) {
var container = $('<span class="red-ui-node-icon-container">');
var def = node._def;
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"})
var nodeDiv = $('<div>',{class:"red-ui-node-icon"})
if (node.type === "_selection_") {
nodeDiv.addClass("red-ui-palette-icon-selection");
} else if (node.type === "group") {
@@ -1147,9 +1253,20 @@ RED.utils = (function() {
}
var icon_url = RED.utils.getNodeIcon(def,node);
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
return nodeDiv;
RED.utils.createIconElement(icon_url, nodeDiv, true);
nodeDiv.appendTo(container);
if (includeLabel) {
var labelText = RED.utils.getNodeLabel(node,node.name || (node.type+": "+node.id));
var label = $('<div>',{class:"red-ui-node-label"}).appendTo(container);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
}
return container;
}
function getDarkerColor(c) {
@@ -1234,6 +1351,23 @@ RED.utils = (function() {
}
return false;
}
function getBrowserInfo() {
var r = {}
try {
var ua = navigator.userAgent;
r.ua = ua;
r.browser = /Edge\/\d+/.test(ua) ? 'ed' : /MSIE 9/.test(ua) ? 'ie9' : /MSIE 10/.test(ua) ? 'ie10' : /MSIE 11/.test(ua) ? 'ie11' : /MSIE\s\d/.test(ua) ? 'ie?' : /rv\:11/.test(ua) ? 'ie11' : /Firefox\W\d/.test(ua) ? 'ff' : /Chrom(e|ium)\W\d|CriOS\W\d/.test(ua) ? 'gc' : /\bSafari\W\d/.test(ua) ? 'sa' : /\bOpera\W\d/.test(ua) ? 'op' : /\bOPR\W\d/i.test(ua) ? 'op' : typeof MSPointerEvent !== 'undefined' ? 'ie?' : '';
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.ie = /MSIE \d|Trident.*rv:/.test(navigator.userAgent);
r.android = /android/i.test(navigator.userAgent);
} catch (error) { }
return r;
}
return {
createObjectElement: buildMessageElement,
getMessageProperty: getMessageProperty,
@@ -1255,6 +1389,7 @@ RED.utils = (function() {
createNodeIcon: createNodeIcon,
getDarkerColor: getDarkerColor,
parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed
checkModuleAllowed: checkModuleAllowed,
getBrowserInfo: getBrowserInfo
}
})();

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,19 +546,39 @@ 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) {
addWorkspace({type:"subflow",id:id,icon:"red/images/subflow_tab.svg",label:sf.name, closeable: true});
addWorkspace(
{type:"subflow",id:id,icon:"red/images/subflow_tab.svg",label:sf.name, closeable: true},
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

@@ -149,7 +149,7 @@ body {
.red-ui-font-code {
font-family: $monospace-font;
font-size: $primary-font-size;
color: $info-text-code-color;
color: $text-color-code;
white-space: nowrap;
}
@@ -158,7 +158,7 @@ body {
font-size: $primary-font-size;
padding: 0px;
margin: 1px;
color: $info-text-code-color;
color: $text-color-code;
white-space: nowrap;
}

View File

@@ -48,15 +48,22 @@ $tertiary-text-color: #aaa;//#90f;
// Heading text
$header-text-color: #444;//#f00;
$text-color-warning: #AD1625;
$text-color-green: #3a3;
$text-color-error: #AD1625;
$text-color-warning: #CAB200;
$text-color-success: #3a3;
$text-color-code: #AD1625;
$text-color-link: #0088cc;
$primary-border-color: #bbbbbb;//#f00;
$secondary-border-color: #dddddd;//#0f0;
$tertiary-border-color: #ccc;//#00f;
$form-background: $secondary-background;
$border-color-error: #DF2935;
$border-color-warning: #FFCF70;
$border-color-success: #4B8400;
$form-background: $secondary-background;
$form-placeholder-color: $tertiary-text-color;
$form-text-color: $primary-text-color;
$form-text-color-disabled: $secondary-text-color-disabled;
@@ -133,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;
@@ -189,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;
@@ -224,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;
@@ -257,16 +270,13 @@ $headerMenuCaret: #C7C7C7;
$vcCommitShaColor: #c38888;
$info-text-code-color: #AD1625;
$info-text-link-color: #0088cc;
$dnd-background: rgba(0,0,0,0.3);
$dnd-color: #fff;
$notification-border-default: #325C80;
$notification-border-success: #4B8400;
$notification-border-warning: #D74108;
$notification-border-error: $text-color-warning;
$notification-border-success: $border-color-success;
$notification-border-warning: $border-color-warning;
$notification-border-error: $border-color-error;
$debug-message-background: $secondary-background;
$debug-message-background-hover: $secondary-background-selected;
@@ -282,11 +292,19 @@ $debug-message-text-color-msg-type-number: #2033d6;
$debug-message-border: #eee;
$debug-message-border-hover: #999;
$debug-message-border-warning: #ffdf9d;
$debug-message-border-error: #f99;
$debug-message-border-warning: $border-color-warning;
$debug-message-border-error: $border-color-error;
$group-default-fill: none;
$group-default-fill-opacity: 1;
$group-default-stroke: #999;
$group-default-stroke-opacity: 1;
$group-default-label-color: #a4a4a4;
$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;
$info-text-link-color: $text-color-link;

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

@@ -500,7 +500,7 @@ ul.red-ui-deploy-dialog-confirm-list {
width: 30px;
margin-right: 10px;
&.fa-check {
color: $text-color-green;
color: $text-color-success;
}
&.fa-exclamation {
color: $secondary-text-color;

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) {
}
}
}
}

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