Compare commits

...

135 Commits

Author SHA1 Message Date
Nick O'Leary
7d7682b34e Merge pull request #3681 from node-red/30-release
3.0.0-beta.3 release
2022-06-16 17:23:01 +01:00
Nick O'Leary
83655a749c Update changelog 2022-06-16 17:01:11 +01:00
Nick O'Leary
b9444e8197 Merge pull request #3679 from node-red/update-deps
Update dependencies
2022-06-16 16:03:37 +01:00
Nick O'Leary
5ff70b2a36 Merge pull request #3680 from node-red/300-tour
Update tour for 3.0-beta.3
2022-06-16 16:03:18 +01:00
Nick O'Leary
4ed559af95 Update changelog and version for 3-beta.3 2022-06-16 15:44:51 +01:00
Nick O'Leary
ce529a9b9f Update module dependencies 2022-06-16 15:39:42 +01:00
Nick O'Leary
31e472145c Merge pull request #3678 from node-red/right-click-menu
Right click menu
2022-06-16 15:38:58 +01:00
Nick O'Leary
1664428429 Update tour for 3.0-beta.3 2022-06-16 15:37:07 +01:00
Nick O'Leary
1780cb9b91 Update dependencies 2022-06-16 14:47:11 +01:00
Nick O'Leary
ad32677263 Fix lint errors in contextMenu 2022-06-16 14:37:48 +01:00
Nick O'Leary
0eba4bdd61 Add right-click context menu to workspace 2022-06-16 13:59:14 +01:00
Nick O'Leary
28238eb5a7 Allow typeSearch to list actions
A proof-of-concept which needs a bit more UI polish, but capturing the
current code for the future. We do not add actions to the list, so the code
is unused.
2022-06-16 13:57:50 +01:00
Nick O'Leary
cce4f6f7f7 Add onpre/postselect, direction opts to menu and make id optional 2022-06-16 13:50:05 +01:00
Nick O'Leary
6bea3dabbb Allow popover position to be set via absolute pos rather than relative 2022-06-16 13:49:25 +01:00
Nick O'Leary
ea46947054 Add RED.actions.getLabel to retrieve action i18n label 2022-06-16 13:48:36 +01:00
Nick O'Leary
22879f8c23 Merge pull request #3677 from Steve-Mcl/file-node-filename-ux
Further simplify file node filename entry UX (v3)
2022-06-16 13:13:43 +01:00
Nick O'Leary
77c4423059 Merge pull request #3676 from Steve-Mcl/nodesdir-feature-parity
Scanning of `nodesdir` to be more aligned (feature wise) with `coreNodeDir` and `userDir`
2022-06-16 13:11:36 +01:00
Steve-Mcl
62a2a4a9f5 Further simplify file node filename entry UX (v3)
fixes #3668
2022-06-16 11:38:19 +01:00
Nick O'Leary
59690328e2 Merge pull request #3674 from node-red-hitachi/fix-cursor-position-function-node
Fix initial cursor position of init/finalize tab of function node
2022-06-16 11:21:49 +01:00
Nick O'Leary
e50ecd613f Merge pull request #3671 from node-red-hitachi/fix-disable-junction
Fix disable junction
2022-06-16 11:20:18 +01:00
Nick O'Leary
d873a6138a Merge pull request #3672 from kazuhitoyokoi/v2.x-addjpn
Add Japanese translations for v2.2.3
2022-06-16 11:17:22 +01:00
Nick O'Leary
8093ae8570 Merge pull request #3643 from node-red/fix-drag-hidden
Reset mouse state when switching tabs
2022-06-16 11:09:29 +01:00
Steve-Mcl
98ebb02763 improve tests for nodeDir
Adds new resources (loose files, non NR pkgs, NR modules, NR Plugins)
Adds new tests
#getNodeFiles - new tests below
  √ Finds nodes and icons only in nodesDir with files, icons and valid node-red packages
  √ Should not find node-red node in nodesDir with files, icons and valid node-red packages
  √ Should not find node-red node in nodesDir when regular package and valid node-red packages
#getModuleFiles - new tests below
  √ gets a nodes module files
  √ Finds only 1 node-red node in nodesDir amongst legacy nodes and regular nodes
  √ Finds a node-red node in nodesDir with a sub dir containing valid node-red package
  √ Finds 2 node-red node and 1 plugin in nodesDir (in root of dir)
  √ Finds 2 node-red node and 1 plugin in nodesDir pointing to a node_modules dir
2022-06-16 11:00:31 +01:00
Steve-Mcl
ba22b07dce align functionality of nodesDir with coreNodesDir and userDir 2022-06-16 10:57:29 +01:00
Hiroyasu Nishiyama
d1312703c5 fix initial cursor position of init/finalize 2022-06-16 14:16:40 +09:00
Kazuhito Yokoi
8d99a42307 Add Japanese translations (Backport #3576 to v2.x) 2022-06-16 00:32:20 +09:00
Hiroyasu Nishiyama
3ab93ecdd4 fix disable junction 2022-06-16 00:00:02 +09:00
Stephen McLaughlin
8762d0e164 Merge pull request #3666 from node-red-hitachi/fix-junction-to-subflow-2
Fix uncorrect fix of junction to subflow conversion
2022-06-14 09:34:56 +01:00
Hiroyasu Nishiyama
2b91edeb74 fix uncorrect fix of junction to subflow conversion 2022-06-14 16:49:14 +09:00
Nick O'Leary
6c49d1aa3f Merge pull request #3653 from node-red-hitachi/fix-undo-junction-to-subflow
Fix undoing junction to subflow
2022-06-13 21:09:20 +01:00
Nick O'Leary
f07f086d62 Merge pull request #3652 from node-red-hitachi/fix-junction-to-subflow
Fix conversion of junction to subflow
2022-06-13 21:09:11 +01:00
Nick O'Leary
c28862f8c7 Merge pull request #3650 from node-red-hitachi/fix-export-junction
Fix to include junction to exported nodes
2022-06-13 21:06:33 +01:00
Nick O'Leary
9cfaf567be Merge pull request #3649 from kazuhitoyokoi/master-fixshade4palette
Fix z-index value for shade to cover nodes in palette
2022-06-13 21:04:52 +01:00
Nick O'Leary
53f453bf34 Merge pull request #3647 from node-red-hitachi/fix-subflow-category-name
Fix to extend escaped subflow category characters
2022-06-13 21:04:13 +01:00
Nick O'Leary
a16032a8ed Track mouse release outside workspace so current action completes 2022-06-13 21:01:34 +01:00
Nick O'Leary
8cec0d8fcd Merge pull request #3645 from node-red/fix-esm
Fix ESM module loading in Function node
2022-06-13 08:10:28 -05:00
Hiroyasu Nishiyama
6f6f67829b fix undoing junction to subflow 2022-06-13 14:12:04 +09:00
Hiroyasu Nishiyama
94471b6d07 fix conversion of junction to subflow 2022-06-13 08:47:41 +09:00
Hiroyasu Nishiyama
7eed9c0584 include junction to exported nodes 2022-06-12 10:08:13 +09:00
Kazuhito Yokoi
3e717862a4 Fix z-index value for shade to cover nodes in palette 2022-06-11 20:52:58 +09:00
Hiroyasu Nishiyama
30d88bbe7e extend escaped subflow category characters 2022-06-11 09:50:28 +09:00
Nick O'Leary
551e73adef Merge pull request #3646 from node-red-hitachi/fix-tab-name-sanitize
Fix to sanitize tab name
2022-06-10 06:56:40 -05:00
Hiroyasu Nishiyama
99d32c4758 sanitize tab name 2022-06-10 20:22:09 +09:00
Nick O'Leary
9e52c15829 Merge pull request #3644 from bonanitech/fix-selector-placement
Fix selector placement
2022-06-09 22:14:09 -05:00
Nick O'Leary
71a272f0a6 Fix ESM module loading in Function node
Fixes #3627
2022-06-09 22:11:48 -05:00
Mauricio Bonani
64061c3440 Fix selector placement 2022-06-09 17:44:11 -04:00
Nick O'Leary
3e34d0badb Merge pull request #3622 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v3.0-beta.2
2022-06-09 16:03:43 -05:00
Nick O'Leary
b2e8474df3 Merge pull request #3624 from node-red-hitachi/fix-buffer-parse-error
fix buffer parse error message of evaluateNodeProperty
2022-06-09 16:02:48 -05:00
Nick O'Leary
73ff7e2de4 Merge pull request #3632 from node-red-hitachi/fix-inject-of-JSONata
Fix JSONata evaluation of inject button
2022-06-09 16:00:23 -05:00
Nick O'Leary
b5d8d34718 Merge pull request #3633 from node-red-hitachi/fix-new-folder-save-to-library
Fix new folder menu of save to library dialog
2022-06-09 15:59:34 -05:00
Nick O'Leary
ac44d22cee Merge pull request #3638 from node-red-hitachi/fix-palette-node-layer
Fix layer of palette node
2022-06-09 15:55:46 -05:00
Nick O'Leary
6e8fa12172 Merge pull request #3637 from node-red-hitachi/fix-node-placement
Fix to place a node dragged from palette within the workspace
2022-06-09 15:54:32 -05:00
Nick O'Leary
deb9c4ecc0 Reset mouse state when switching tabs
Fixes #3639
2022-06-09 15:47:16 -05:00
Hiroyasu Nishiyama
3046798ec5 fix layer of palette node 2022-06-08 14:50:14 +09:00
Hiroyasu Nishiyama
c4332658ba place node dragged from palette within workspace 2022-06-08 09:36:32 +09:00
Hiroyasu Nishiyama
50e3da0849 fix new folder menu of save to library dialog 2022-05-28 20:16:53 +09:00
Stephen McLaughlin
62cd3b2061 Merge pull request #3630 from Steve-Mcl/fix-tcp-sockets-crash
Dont delete TCP socket twice
2022-05-27 07:37:28 +01:00
Hiroyasu Nishiyama
7e6dfa7b92 update test for inject node 2022-05-27 12:11:53 +09:00
Hiroyasu Nishiyama
7fbebbf361 fix JSONata evaluation of inject button 2022-05-27 11:44:56 +09:00
Kazuhito Yokoi
121372802f Use built-in type in typedInput 2022-05-26 22:50:52 +09:00
Nick O'Leary
7924907384 Merge pull request #3626 from node-red/noproxy-not-defined
MQTT Node: define noproxy variable
2022-05-26 10:43:39 +01:00
Stephen McLaughlin
51d429f9ae Dont delete TCP socket twice 2022-05-26 10:18:34 +01:00
Stephen McLaughlin
267aebb9cb Merge pull request #3628 from bonanitech/patch-1
Fix typo in CSS
2022-05-25 16:44:26 +01:00
Mauricio Bonani
3f9ebb588e Fix typo 2022-05-25 11:15:29 -04:00
Kazuhito Yokoi
f424f07e54 Fix typos in message catalog 2022-05-25 22:56:58 +09:00
Kazuhito Yokoi
e5a21a3261 Merge branch 'node-red:master' into master-addjpn 2022-05-25 19:07:57 +09:00
Stephen McLaughlin
6f0de7c80e define noproxy variable 2022-05-24 20:43:29 +01:00
Stephen McLaughlin
3ace7eeafd Merge pull request #3623 from node-red-hitachi/i18n-debug-sidebar-node-label
i18n debug sidebar node label
2022-05-24 07:03:14 +01:00
Stephen McLaughlin
eb9f15e4e4 Merge pull request #3615 from bonanitech/ace-editor-gutter-color
Use the correct variable for the gutter text color
2022-05-23 17:33:51 +01:00
Hiroyasu Nishiyama
bc80569fe9 fix buffer parse error message of evaluateNodeProperty 2022-05-22 17:59:27 +09:00
Hiroyasu Nishiyama
a147458120 i18n debug sidebar node label 2022-05-22 16:28:11 +09:00
Kazuhito Yokoi
6182e22d18 Add string label to typedInput in file nodes 2022-05-22 11:56:21 +09:00
Kazuhito Yokoi
dda84c5cd5 Fix indents in other code except message catalog 2022-05-22 11:18:40 +09:00
Kazuhito Yokoi
6c15fb6978 Fix indents in message catalog 2022-05-22 10:51:35 +09:00
Kazuhito Yokoi
b17b68c44b Add Japanese translations for v3.0-beta.2 2022-05-22 10:37:22 +09:00
Mauricio Bonani
afdb15dc58 Use the correct variable for the gutter text color 2022-05-18 10:28:36 -04:00
Nick O'Leary
98b4b0dce0 Merge pull request #3612 from node-red/3beta2
Update for 3.0.0-beta.2
2022-05-16 11:47:07 +01:00
Nick O'Leary
70f26e0bea Update for 3.0.0-beta.2 2022-05-16 11:10:31 +01:00
Nick O'Leary
b6ad396a6c Merge pull request #3611 from node-red/fix-ace-worker-opts
Ensure ACE worker options are set
2022-05-16 10:59:59 +01:00
Nick O'Leary
e44bb57b0e Merge pull request #3609 from Steve-Mcl/slicing-button
Change slicing / slice-junction operations over to mouse button 0 (Left Mouse Button)
2022-05-16 09:59:41 +01:00
Stephen McLaughlin
5c10b16b65 Ensure ACE worker options are set
fixes #3610
2022-05-15 13:19:28 +01:00
Steve-Mcl
7ec1d42808 Change slicing op to mouse button 0
fixes #3582
2022-05-13 16:43:27 +01:00
Stephen McLaughlin
03e9e89558 Merge pull request #3608 from node-red/filter-virtual-on-slice
Do not slice-junction link node wires
2022-05-13 11:13:14 +01:00
Nick O'Leary
3057035dec Do not slice-junction link node wires 2022-05-12 14:57:16 +01:00
Nick O'Leary
4af72cc7ba Merge pull request #3607 from node-red/junction-rework
Rework Junctions to be more node like in their event handling
2022-05-12 10:12:38 +01:00
Nick O'Leary
f6aee81651 Hide junction ports whilst dragging nodes 2022-05-12 10:02:35 +01:00
Nick O'Leary
a22f569ca0 Rework Junctions to be more node like in their event handling 2022-05-11 23:13:12 +01:00
Nick O'Leary
8043f5d865 Merge pull request #3606 from node-red/dev
Merge minor dev branch fixes and "Search flow:active" V3 item into master
2022-05-10 11:19:03 +01:00
Stephen McLaughlin
2ef50ab71f Merge pull request #3604 from node-red/fix-many-to-one-junction-slice
Handle many-to-one slicing of wires
2022-05-10 08:09:00 +01:00
Stephen McLaughlin
192a4f5e7f Merge pull request #3605 from node-red/fix-ungroup-history
Remove duplicate history add of ungroup event
2022-05-10 08:08:45 +01:00
Nick O'Leary
d2fab7fddd Remove duplicate history add of ungroup event
Fixes #3581
2022-05-09 20:51:17 +01:00
Nick O'Leary
1818b0281d Handle many-to-one slicing of wires 2022-05-09 20:39:34 +01:00
Nick O'Leary
d549a9ad92 Merge pull request #3603 from node-red-hitachi/fix-select-width-of-switch-node-rule
use text width instead of number of characters for deciding select fi…
2022-05-09 17:18:49 +01:00
Stephen McLaughlin
0385c72a8f Merge pull request #3594 from PhilDay-CT/issue-3593
Handle removal of event handlers to allow mqtt client.end() to work
2022-05-09 16:50:48 +01:00
Stephen McLaughlin
e223b20cbd Remove unnecessary call to clientRemoveListeners
Also, merge the non JSDOC comment into the JSDOC comment
2022-05-09 16:37:25 +01:00
Phil Day
a0f7e92e40 call client.end with force=true on timeout 2022-05-09 16:29:39 +01:00
Phil Day
c87ff3ca26 Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-05-09 16:22:50 +01:00
Phil Day
82672a825d Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-05-09 16:20:44 +01:00
Phil Day
98d524e82d Update packages/node_modules/@node-red/nodes/core/network/10-mqtt.js
Co-authored-by: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com>
2022-05-09 16:20:27 +01:00
Phil Day
3d3090a8f2 Updated to cover the removal of individual event handlers 2022-05-09 15:39:12 +01:00
Stephen McLaughlin
9ed96de237 Merge pull request #3602 from node-red-hitachi/i18n-switch-rule-selector
I18n switch rule selector
2022-05-08 14:28:36 +01:00
Hiroyasu Nishiyama
abb81a0bac use text width instead of number of characters for deciding select field width of switch node rule 2022-05-08 12:32:07 +09:00
Hiroyasu Nishiyama
05eb055b8c add Japanese translation of switch rules 2022-05-08 08:59:07 +09:00
Hiroyasu Nishiyama
e8ddd6d16d i18n switch group label & add Japanese message 2022-05-08 08:44:35 +09:00
Stephen McLaughlin
8706998c8c Merge pull request #3600 from node-red-hitachi/fix-link-call-info-jp
update Japanese info of link call node reflecting update of English info
2022-05-07 14:28:05 +01:00
Hiroyasu Nishiyama
06e0869767 update Japanese info of link call node reflecting update of English info 2022-05-07 22:09:54 +09:00
Nick O'Leary
7841fc6d3e Merge pull request #3580 from bonanitech/typedinput-label
Fix typedInput label not visible on themes
2022-05-06 19:25:54 +01:00
Nick O'Leary
1f7311deeb Merge pull request #3597 from node-red-hitachi/fix-link-call-info
update link-call node info according to current behavior
2022-05-06 19:25:10 +01:00
Hiroyasu Nishiyama
07a9e69e7b update link-call node info according to current behavior 2022-05-06 23:38:58 +09:00
Phil Day
9bc8adc715 Revent change of grunt version 2022-05-06 15:34:25 +01:00
Phil Day
7845ebffc5 Track which event handlers we add to the mqtt client so we can removed them cleanly 2022-05-06 15:29:42 +01:00
Nick O'Leary
b985de6df2 Merge pull request #3595 from Steve-Mcl/fix-project-switching
Fix project switching when junctions are present
2022-05-05 20:48:54 +01:00
Steve-Mcl
11f6491889 fix project switching when junctions are present
fixes #3588
2022-05-05 16:56:28 +01:00
Phil Day
b2ec040a8d Add Force parameter mqtt client.end() when called in disconnect 2022-05-05 16:12:28 +01:00
Stephen McLaughlin
424a53da4e Merge pull request #3576 from kazuhitoyokoi/master-addjpn
Add Japanese translations for v3.0-beta.1
2022-05-05 15:16:36 +01:00
Stephen McLaughlin
963c289af7 Merge pull request #3592 from kazuhitoyokoi/master-fixiconpath
Fix image paths where `red/image/typedInput/XXXX.png` should be `red/image/typedInput/XXXX.svg`
2022-05-05 14:57:49 +01:00
Kazuhito Yokoi
c5af71e0a2 Change icon path from png to svg 2022-05-05 20:38:55 +09:00
Nick O'Leary
329008bf6d Merge pull request #3591 from Steve-Mcl/fix-back-link-to-junc
Fix junction: when wiring from a regular nodes INPUT, backwards to a junction
2022-05-05 10:27:48 +01:00
Steve-Mcl
531dbc5f83 Fix junction: ensure sourcePort is not undefined
fixes #3587
2022-05-05 09:37:32 +01:00
Stephen McLaughlin
851a925956 Merge pull request #3585 from Steve-Mcl/fix-flow-prop-tab
fix error initialising flow tab editor
2022-05-05 08:25:15 +01:00
Stephen McLaughlin
5d4e01eea6 Merge pull request #3589 from node-red-hitachi/fix-system-info
fix error on system-info action
2022-05-05 08:24:24 +01:00
Stephen McLaughlin
7484dc5b4c Merge pull request #3584 from Steve-Mcl/fix-search-err
Fix browser console error Uncaught TypeError when searching certain terms
2022-05-05 08:14:26 +01:00
Hiroyasu Nishiyama
c513cff843 fix error on system-info action 2022-05-05 10:10:52 +09:00
Kazuhito Yokoi
bc5eafce66 Update translations in file and http request nodes 2022-05-05 01:28:42 +09:00
Kazuhito Yokoi
5fb811eb4c Change icon path from png to svg in typedInput 2022-05-05 01:20:41 +09:00
Kazuhito Yokoi
84a3884ffc Fix i18n in typedInput of header area 2022-05-04 23:54:23 +09:00
Kazuhito Yokoi
50ae29a08c Add Japanese translations for v3.0-beta.1 2022-05-04 23:29:40 +09:00
Steve-Mcl
bf8bfa582a fix error initialising flow tab editor
fixes #3577
2022-05-04 15:16:35 +01:00
Steve-Mcl
492d1ef30e declare undeclared loop var 2022-05-04 15:10:02 +01:00
Steve-Mcl
bd19c203e1 Prevent error when uses search term is used
fixes #3578
2022-05-04 15:08:52 +01:00
Mauricio Bonani
7955a17a17 Fix typedInput label not visible 2022-05-04 06:37:52 -04:00
Kazuhito Yokoi
58085e39d1 Add Japanese translations for welcome tour in 3.0-beta.1 2022-05-04 11:39:34 +09:00
Nick O'Leary
3841039728 Update gen publish script for 3.x 2022-05-03 14:29:25 +01:00
Nick O'Leary
f04d954882 Drop node 12 from test runs 2022-05-03 14:25:27 +01:00
Nick O'Leary
39602ff5f2 Update changelog 2022-05-03 13:58:02 +01:00
111 changed files with 2293 additions and 1005 deletions

View File

@@ -26,7 +26,7 @@ jobs:
path: 'node-red.github.io'
- uses: actions/setup-node@v1
with:
node-version: '12'
node-version: '16'
- run: node ./node-red/.github/scripts/update-node-red-docker.js
- name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12, 14, 16]
node-version: [14, 16]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}

View File

@@ -1,5 +1,85 @@
#### 3.0.0-beta.3: Beta Release
Editor
- Add Right-Click content menu (#3678) @knolleary
- Fix disable junction (#3671) @HiroyasuNishiyama
- Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi
- Reset mouse state when switching tabs (#3643) @knolleary
- Fix uncorrect fix of junction to subflow conversion (#3666) @HiroyasuNishiyama
- Fix undoing junction to subflow (#3653) @HiroyasuNishiyama
- Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama
- Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama
- Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi
- Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama
- Fix to sanitize tab name (#3646) @HiroyasuNishiyama
- Fix selector placement (#3644) @bonanitech
- Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi
- Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama
- Fix layer of palette node (#3638) @HiroyasuNishiyama
- Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama
- Fix typo in CSS (#3628) @bonanitech
- Use the correct variable for the gutter text color (#3615) @bonanitech
Runtime
- Support loading node modules from `nodesdir` (#3676) @Steve-Mcl
- fix buffer parse error message of evaluateNodeProperty (#3624) @HiroyasuNishiyama
Nodes
- File: Further simplify file node filename entry UX (v3) (#3677) @Steve-Mcl
- Function: Fix initial cursor position of init/finalize tab of function node (#3674) @HiroyasuNishiyama
- Function: Fix ESM module loading in Function node (#3645) @knolleary
- Inject: Fix JSONata evaluation of inject button (#3632) @HiroyasuNishiyama
- TCP: Dont delete TCP socket twice (#3630) @Steve-Mcl
- MQTT Node: define noproxy variable (#3626) @Steve-Mcl
- Debug: i18n debug sidebar node label (#3623) @HiroyasuNishiyama
#### 3.0.0-beta.2: Beta Release
**Migration from 2.x**
- The 'slice wires' action has changed from Ctrl-RightMouseButton to Alt-LeftMouseButton
Editor
- Rework Junctions to be more node like in their event handling (#3607) @knolleary
- Change slicing / slice-junction operations over to mouse button 0 (Left Mouse Button) (#3609) @Steve-Mcl
- Do not slice-junction link node wires (#3608) @knolleary
- Handle many-to-one slicing of wires (#3604) @knolleary
- Ensure ACE worker options are set (#3611) @Steve-Mcl
- Remove duplicate history add of ungroup event (#3605) @knolleary
- use text width instead of number of characters for deciding select fi… (#3603) @HiroyasuNishiyama
- Update Japanese info of link call node reflecting update of English info (#3600) @HiroyasuNishiyama
- Fix typedInput label not visible on themes (#3580) @bonanitech
- Fix project switching when junctions are present (#3595) @Steve-Mcl
- Fix junction: when wiring from a regular nodes INPUT, backwards to a junction (#3591) @Steve-Mcl
- Fix error initialising flow tab editor (#3585) @Steve-Mcl
- Add Japanese translations for v3.0-beta.1 (#3576) @kazuhitoyokoi
- Fix image paths where `red/image/typedInput/XXXX.png` should be `red/image/typedInput/XXXX.svg` (#3592) @kazuhitoyokoi
- Fix browser console error Uncaught TypeError when searching certain terms (#3584) @Steve-Mcl
Runtime
- fix error on system-info action (#3589) @HiroyasuNishiyama
Nodes
- I18n switch rule selector (#3602) @HiroyasuNishiyama
- Handle removal of event handlers to allow mqtt client.end() to work (#3594) @PhilDay-CT
- update link-call node info according to current behavior (#3597) @HiroyasuNishiyama
#### 3.0.0-beta.1: Beta Release
**Migration from 2.x**
- Node-RED now requires Node.js 14.x or later.
- New installs of Node-RED will default to the monaco editor.
Editor
- Add Junctions (#3462) @knolleary

View File

@@ -192,6 +192,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/search.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -42,14 +42,14 @@
"cronosjs": "1.7.1",
"denque": "2.0.1",
"express": "4.18.1",
"express-session": "1.17.2",
"express-session": "1.17.3",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"got": "11.8.3",
"hash-sum": "2.0.0",
"hpagent": "1.0.0",
"https-proxy-agent": "5.0.1",
"i18next": "21.6.16",
"i18next": "21.8.10",
"iconv-lite": "0.6.3",
"is-utf8": "0.2.1",
"js-yaml": "4.1.0",
@@ -62,7 +62,7 @@
"moment": "2.29.3",
"moment-timezone": "0.5.34",
"mqtt": "4.3.7",
"multer": "1.4.4",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"node-red-admin": "^3.0.0",
"node-watch": "0.7.3",
@@ -76,7 +76,7 @@
"semver": "7.3.7",
"tar": "6.1.11",
"tough-cookie": "4.0.0",
"uglify-js": "3.15.4",
"uglify-js": "3.16.0",
"uuid": "8.3.2",
"ws": "7.5.6",
"xml2js": "0.4.23"
@@ -85,8 +85,8 @@
"bcrypt": "5.0.1"
},
"devDependencies": {
"dompurify": "2.3.6",
"grunt": "1.5.2",
"dompurify": "2.3.8",
"grunt": "1.5.3",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3",
"grunt-concurrent": "3.0.0",
@@ -105,16 +105,16 @@
"grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1",
"i18next-http-backend": "1.4.0",
"i18next-http-backend": "1.4.1",
"jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "4.0.15",
"marked": "4.0.17",
"minami": "1.2.3",
"mocha": "9.2.2",
"node-red-node-test-helper": "^0.2.7",
"nodemon": "2.0.16",
"proxy": "^1.0.2",
"sass": "1.51.0",
"sass": "1.52.3",
"should": "13.2.3",
"sinon": "11.1.2",
"stoppable": "^1.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,17 +16,17 @@
}
],
"dependencies": {
"@node-red/util": "3.0.0-beta.1",
"@node-red/editor-client": "3.0.0-beta.1",
"@node-red/util": "3.0.0-beta.3",
"@node-red/editor-client": "3.0.0-beta.3",
"bcryptjs": "2.4.3",
"body-parser": "1.20.0",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.2",
"express-session": "1.17.3",
"express": "4.18.1",
"memorystore": "1.6.7",
"mime": "3.0.0",
"multer": "1.4.4",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"oauth2orize": "1.11.1",
"passport-http-bearer": "1.0.1",

View File

@@ -3,7 +3,7 @@
"label": {
"name": "Name",
"ok": "Ok",
"done":"Done",
"done": "Done",
"cancel": "Cancel",
"delete": "Delete",
"close": "Close",
@@ -66,7 +66,7 @@
"listSubflows": "List subflows",
"status": "Status",
"enabled": "Enabled",
"disabled":"Disabled",
"disabled": "Disabled",
"info": "Description",
"selectNodes": "Click nodes to select"
},
@@ -114,7 +114,7 @@
"keyboardShortcuts": "Keyboard shortcuts",
"login": "Login",
"logout": "Logout",
"editPalette":"Manage palette",
"editPalette": "Manage palette",
"other": "Other",
"showTips": "Show tips",
"showWelcomeTours": "Show guided tours for new versions",
@@ -130,19 +130,19 @@
"ungroupSelection": "Ungroup selection",
"groupMergeSelection": "Merge selection",
"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"
"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": {
@@ -176,10 +176,10 @@
"nodeActionDisabledSubflow": "node actions disabled within subflow",
"missing-types": "<p>Flows stopped due to missing node types.</p>",
"missing-modules": "<p>Flows stopped due to missing modules.</p>",
"safe-mode":"<p>Flows stopped in safe mode.</p><p>You can modify your flows and deploy the changes to restart.</p>",
"safe-mode": "<p>Flows stopped in safe mode.</p><p>You can modify your flows and deploy the changes to restart.</p>",
"restartRequired": "Node-RED must be restarted to enable upgraded modules",
"credentials_load_failed": "<p>Flows stopped as the credentials could not be decrypted.</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p>",
"credentials_load_failed_reset":"<p>Credentials could not be decrypted</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p><p>The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.</p>",
"credentials_load_failed_reset": "<p>Credentials could not be decrypted</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p><p>The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.</p>",
"missing_flow_file": "<p>Project flow file not found.</p><p>The project is not configured with a flow file.</p>",
"missing_package_file": "<p>Project package file not found.</p><p>The project is missing a package.json file.</p>",
"project_empty": "<p>The project is empty.</p><p>Do you want to create a default set of project files?<br/>Otherwise, you will have to manually add files to the project outside of the editor.</p>",
@@ -257,11 +257,11 @@
"recoveredNodesInfo": "The nodes on this flow were missing a valid flow id when they were imported. They have been added to this flow so you can either restore or delete them.",
"recoveredNodesNotification": "<p>Imported nodes without a valid flow id</p><p>They have been added to a new flow called '__flowName__'.</p>",
"export": {
"selected":"selected nodes",
"current":"current flow",
"all":"all flows",
"compact":"compact",
"formatted":"formatted",
"selected": "selected nodes",
"current": "current flow",
"all": "all flows",
"compact": "compact",
"formatted": "formatted",
"copy": "Copy to clipboard",
"export": "Export to library",
"exportAs": "Export as",
@@ -301,10 +301,10 @@
"successfulDeploy": "Successfully deployed",
"successfulRestart": "Successfully restarted flows",
"deployFailed": "Deploy failed: __message__",
"unusedConfigNodes":"You have some unused configuration nodes.",
"unusedConfigNodesButton":"Search unused config nodes",
"unknownNodesButton":"Search for unknown nodes",
"invalidNodesButton":"Search for invalid nodes",
"unusedConfigNodes": "You have some unused configuration nodes.",
"unusedConfigNodesButton": "Search unused config nodes",
"unknownNodesButton": "Search for unknown nodes",
"invalidNodesButton": "Search for invalid nodes",
"errors": {
"noResponse": "no response from server"
},
@@ -351,8 +351,8 @@
},
"nodeCount": "__count__ node",
"nodeCount_plural": "__count__ nodes",
"local":"Local changes",
"remote":"Remote changes",
"local": "Local changes",
"remote": "Remote changes",
"reviewChanges": "Review Changes",
"noBinaryFileShowed": "Cannot show binary file contents",
"viewCommitDiff": "View Commit Changes",
@@ -436,7 +436,7 @@
"inputType": "Input type",
"selectType": "select types...",
"loadCredentials": "Loading node credentials",
"inputs" : {
"inputs": {
"input": "input",
"select": "select",
"checkbox": "checkbox",
@@ -617,19 +617,19 @@
},
"confirm": {
"install": {
"body":"<p>Installing '__module__'</p><p>Before installing, please read the node's documentation. Some nodes have dependencies that cannot be automatically resolved and can require a restart of Node-RED.</p>",
"body": "<p>Installing '__module__'</p><p>Before installing, please read the node's documentation. Some nodes have dependencies that cannot be automatically resolved and can require a restart of Node-RED.</p>",
"title": "Install nodes"
},
"remove": {
"body":"<p>Removing '__module__'</p><p>Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.</p>",
"body": "<p>Removing '__module__'</p><p>Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.</p>",
"title": "Remove nodes"
},
"update": {
"body":"<p>Updating '__module__'</p><p>Updating the node will require a restart of Node-RED to complete the update. This must be done manually.</p>",
"body": "<p>Updating '__module__'</p><p>Updating the node will require a restart of Node-RED to complete the update. This must be done manually.</p>",
"title": "Update nodes"
},
"cannotUpdate": {
"body":"An update for this node is available, but it is not installed in a location that the palette manager can update.<br/><br/>Please refer to the documentation for how to update this node."
"body": "An update for this node is available, but it is not installed in a location that the palette manager can update.<br/><br/>Please refer to the documentation for how to update this node."
},
"button": {
"review": "Open node information",
@@ -663,14 +663,14 @@
"showMore": "show more",
"showLess": "show less",
"flow": "Flow",
"selection":"Selection",
"nodes":"__count__ nodes",
"selection": "Selection",
"nodes": "__count__ nodes",
"flowDesc": "Flow Description",
"subflowDesc": "Subflow Description",
"nodeHelp": "Node Help",
"none":"None",
"none": "None",
"arrayItems": "__count__ items",
"showTips":"You can open the tips from the settings panel",
"showTips": "You can open the tips from the settings panel",
"outline": "Outline",
"empty": "empty",
"globalConfig": "Global Configuration Nodes",
@@ -701,8 +701,8 @@
"filtered": "__count__ hidden"
},
"context": {
"name":"Context Data",
"label":"context",
"name": "Context Data",
"label": "context",
"none": "none selected",
"refresh": "refresh to load",
"empty": "empty",
@@ -740,9 +740,9 @@
"files": "Files",
"flow": "Flow",
"credentials": "Credentials",
"package":"Package",
"packageCreate":"File will be created when changes are saved",
"fileNotExist":"File does not exist",
"package": "Package",
"packageCreate": "File will be created when changes are saved",
"fileNotExist": "File does not exist",
"selectFile": "Select File",
"invalidEncryptionKey": "Invalid encryption key",
"encryptionEnabled": "Encryption enabled",
@@ -978,7 +978,7 @@
"title": "Buffer editor",
"modeString": "Handle as UTF-8 String",
"modeArray": "Handle as JSON array",
"modeDesc":"<h3>Buffer editor</h3><p>The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.</p><p>For example, a value of <code>Hello World</code> will be converted to the JSON array:<pre>[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]</pre></p>"
"modeDesc": "<h3>Buffer editor</h3><p>The Buffer type is stored as a JSON array of byte values. The editor will attempt to parse the entered value as a JSON array. If it is not valid JSON, it will be treated as a UTF-8 String and converted to an array of the individual character code points.</p><p>For example, a value of <code>Hello World</code> will be converted to the JSON array:<pre>[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]</pre></p>"
},
"projects": {
"config-git": "Configure Git client",
@@ -1140,7 +1140,7 @@
"no-empty": "Cannot create default file set on a non-empty project",
"git-error": "git error"
},
"errors" : {
"errors": {
"no-username-email": "Your Git client is not configured with a username/email.",
"unexpected": "An unexpected error occurred",
"code": "code"
@@ -1163,7 +1163,7 @@
"diagnostics": {
"title": "System Info"
},
"languages" : {
"languages": {
"de": "German",
"en-US": "English",
"ja": "Japanese",
@@ -1174,18 +1174,18 @@
},
"validator": {
"errors": {
"invalid-json": "Invalid JSON data: __error__",
"invalid-json-prop": "__prop__: invalid JSON data: __error__",
"invalid-prop": "Invalid property expression",
"invalid-prop-prop": "__prop__: invalid property expression",
"invalid-num": "Invalid number",
"invalid-num-prop": "__prop__: invalid number",
"invalid-regexp": "Invalid input pattern",
"invalid-regex-prop": "__prop__: invalid input pattern",
"missing-required-prop": "__prop__: property value missing",
"invalid-config": "__prop__: invalid configuration node",
"missing-config": "__prop__: missing configuration node",
"validation-error": "__prop__: validation error: __node__, __id__: __error__"
}
"invalid-json": "Invalid JSON data: __error__",
"invalid-json-prop": "__prop__: invalid JSON data: __error__",
"invalid-prop": "Invalid property expression",
"invalid-prop-prop": "__prop__: invalid property expression",
"invalid-num": "Invalid number",
"invalid-num-prop": "__prop__: invalid number",
"invalid-regexp": "Invalid input pattern",
"invalid-regex-prop": "__prop__: invalid input pattern",
"missing-required-prop": "__prop__: property value missing",
"invalid-config": "__prop__: invalid configuration node",
"missing-config": "__prop__: missing configuration node",
"validation-error": "__prop__: validation error: __node__, __id__: __error__"
}
}
}

View File

@@ -1,23 +1,23 @@
{
"info": {
"tip0" : "You can remove the selected nodes or links with {{core:delete-selection}}",
"tip1" : "Search for nodes using {{core:search}}",
"tip2" : "{{core:toggle-sidebar}} will toggle the view of this sidebar",
"tip3" : "You can manage your palette of nodes with {{core:manage-palette}}",
"tip4" : "Your flow configuration nodes are listed in the sidebar panel. It can be accessed from the menu or with {{core:show-config-tab}}",
"tip5" : "Enable or disable these tips from the option in the settings",
"tip6" : "Move the selected nodes using the [left] [up] [down] and [right] keys. Hold [shift] to nudge them further",
"tip7" : "Dragging a node onto a wire will splice it into the link",
"tip8" : "Export the selected nodes, or the current tab with {{core:show-export-dialog}}",
"tip9" : "Import a flow by dragging its JSON into the editor, or with {{core:show-import-dialog}}",
"tip10" : "[shift] [click] and drag on a node port to move all of the attached wires or just the selected one",
"tip11" : "Show the Info tab with {{core:show-info-tab}} or the Debug tab with {{core:show-debug-tab}}",
"tip12" : "[ctrl] [click] in the workspace to open the quick-add dialog",
"tip13" : "Hold down [ctrl] when you [click] on a node port to enable quick-wiring",
"tip14" : "Hold down [shift] when you [click] on a node to also select all of its connected nodes",
"tip15" : "Hold down [ctrl] when you [click] on a node to add or remove it from the current selection",
"tip16" : "Switch flow tabs with {{core:show-previous-tab}} and {{core:show-next-tab}}",
"tip17" : "You can confirm your changes in the node edit tray with {{core:confirm-edit-tray}} or cancel them with {{core:cancel-edit-tray}}",
"tip18" : "Pressing {{core:edit-selected-node}} will edit the first node in the current selection"
"tip0": "You can remove the selected nodes or links with {{core:delete-selection}}",
"tip1": "Search for nodes using {{core:search}}",
"tip2": "{{core:toggle-sidebar}} will toggle the view of this sidebar",
"tip3": "You can manage your palette of nodes with {{core:manage-palette}}",
"tip4": "Your flow configuration nodes are listed in the sidebar panel. It can be accessed from the menu or with {{core:show-config-tab}}",
"tip5": "Enable or disable these tips from the option in the settings",
"tip6": "Move the selected nodes using the [left] [up] [down] and [right] keys. Hold [shift] to nudge them further",
"tip7": "Dragging a node onto a wire will splice it into the link",
"tip8": "Export the selected nodes, or the current tab with {{core:show-export-dialog}}",
"tip9": "Import a flow by dragging its JSON into the editor, or with {{core:show-import-dialog}}",
"tip10": "[shift] [click] and drag on a node port to move all of the attached wires or just the selected one",
"tip11": "Show the Info tab with {{core:show-info-tab}} or the Debug tab with {{core:show-debug-tab}}",
"tip12": "[ctrl] [click] in the workspace to open the quick-add dialog",
"tip13": "Hold down [ctrl] when you [click] on a node port to enable quick-wiring",
"tip14": "Hold down [shift] when you [click] on a node to also select all of its connected nodes",
"tip15": "Hold down [ctrl] when you [click] on a node to add or remove it from the current selection",
"tip16": "Switch flow tabs with {{core:show-previous-tab}} and {{core:show-next-tab}}",
"tip17": "You can confirm your changes in the node edit tray with {{core:confirm-edit-tray}} or cancel them with {{core:cancel-edit-tray}}",
"tip18": "Pressing {{core:edit-selected-node}} will edit the first node in the current selection"
}
}

View File

@@ -52,52 +52,52 @@
"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":"$[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"
"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",
"desc":"Converts an ASCII string to a base 64 representation. Each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported."
"args": "string",
"desc": "Converts an ASCII string to a base 64 representation. Each character in the string is treated as a byte of binary data. This requires that all characters in the string are in the 0x00 to 0xFF range, which includes all characters in URI encoded strings. Unicode characters outside of that range are not supported."
},
"$base64decode": {
"args":"string",
"desc":"Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage."
"args": "string",
"desc": "Converts base 64 encoded bytes to a string, using a UTF-8 Unicode codepage."
},
"$number": {
"args": "arg",
"desc": "Casts the `arg` parameter to a number using the following casting rules:\n\n - Numbers are unchanged\n - Strings that contain a sequence of characters that represent a legal JSON number are converted to that number\n - All other values cause an error to be thrown."
},
"$abs": {
"args":"number",
"desc":"Returns the absolute value of the `number` parameter."
"args": "number",
"desc": "Returns the absolute value of the `number` parameter."
},
"$floor": {
"args":"number",
"desc":"Returns the value of `number` rounded down to the nearest integer that is smaller or equal to `number`."
"args": "number",
"desc": "Returns the value of `number` rounded down to the nearest integer that is smaller or equal to `number`."
},
"$ceil": {
"args":"number",
"desc":"Returns the value of `number` rounded up to the nearest integer that is greater than or equal to `number`."
"args": "number",
"desc": "Returns the value of `number` rounded up to the nearest integer that is greater than or equal to `number`."
},
"$round": {
"args":"number [, precision]",
"desc":"Returns the value of the `number` parameter rounded to the number of decimal places specified by the optional `precision` parameter."
"args": "number [, precision]",
"desc": "Returns the value of the `number` parameter rounded to the number of decimal places specified by the optional `precision` parameter."
},
"$power": {
"args":"base, exponent",
"desc":"Returns the value of `base` raised to the power of `exponent`."
"args": "base, exponent",
"desc": "Returns the value of `base` raised to the power of `exponent`."
},
"$sqrt": {
"args":"number",
"desc":"Returns the square root of the value of the `number` parameter."
"args": "number",
"desc": "Returns the square root of the value of the `number` parameter."
},
"$random": {
"args":"",
"desc":"Returns a pseudo random number greater than or equal to zero and less than one."
"args": "",
"desc": "Returns a pseudo random number greater than or equal to zero and less than one."
},
"$millis": {
"args":"",
"desc":"Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value."
"args": "",
"desc": "Returns the number of milliseconds since the Unix Epoch (1 January, 1970 UTC) as a number. All invocations of `$millis()` within an evaluation of an expression will all return the same value."
},
"$sum": {
"args": "array",
@@ -136,20 +136,20 @@
"desc": "Appends two arrays"
},
"$sort": {
"args":"array [, function]",
"desc":"Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values left and right. If the value of left should be placed after the value of right in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`."
"args": "array [, function]",
"desc": "Returns an array containing all the values in the `array` parameter, but sorted into order.\n\nIf a comparator `function` is supplied, then it must be a function that takes two parameters:\n\n`function(left, right)`\n\nThis function gets invoked by the sorting algorithm to compare two values left and right. If the value of left should be placed after the value of right in the desired sort order, then the function must return Boolean `true` to indicate a swap. Otherwise it must return `false`."
},
"$reverse": {
"args":"array",
"desc":"Returns an array containing all the values from the `array` parameter, but in reverse order."
"args": "array",
"desc": "Returns an array containing all the values from the `array` parameter, but in reverse order."
},
"$shuffle": {
"args":"array",
"desc":"Returns an array containing all the values from the `array` parameter, but shuffled into random order."
"args": "array",
"desc": "Returns an array containing all the values from the `array` parameter, but shuffled into random order."
},
"$zip": {
"args":"array, ...",
"desc":"Returns a convolved (zipped) array containing grouped arrays of values from the `array1` … `arrayN` arguments from index 0, 1, 2...."
"args": "array, ...",
"desc": "Returns a convolved (zipped) array containing grouped arrays of values from the `array1` … `arrayN` arguments from index 0, 1, 2...."
},
"$keys": {
"args": "object",
@@ -168,24 +168,24 @@
"desc": "Merges an array of `objects` into a single `object` containing all the key/value pairs from each of the objects in the input array. If any of the input objects contain the same key, then the returned `object` will contain the value of the last one in the array. It is an error if the input array contains an item that is not an object."
},
"$sift": {
"args":"object, function",
"desc":"Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`"
"args": "object, function",
"desc": "Returns an object that contains only the key/value pairs from the `object` parameter that satisfy the predicate `function` passed in as the second parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, key [, object]])`"
},
"$each": {
"args":"object, function",
"desc":"Returns an array containing the values return by the `function` when applied to each key/value pair in the `object`."
"args": "object, function",
"desc": "Returns an array containing the values return by the `function` when applied to each key/value pair in the `object`."
},
"$map": {
"args":"array, function",
"desc":"Returns an array containing the results of applying the `function` parameter to each value in the `array` parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`"
"args": "array, function",
"desc": "Returns an array containing the results of applying the `function` parameter to each value in the `array` parameter.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args":"array, function",
"desc":"Returns an array containing only the values in the `array` parameter that satisfy the `function` predicate.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`"
"args": "array, function",
"desc": "Returns an array containing only the values in the `array` parameter that satisfy the `function` predicate.\n\nThe `function` that is supplied as the second parameter must have the following signature:\n\n`function(value [, index [, array]])`"
},
"$reduce": {
"args":"array, function [, init]",
"desc":"Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`. The signature of `function` must be of the form: `myfunc($accumulator, $value[, $index[, $array]])`\n\nThe optional `init` parameter is used as the initial value in the aggregation."
"args": "array, function [, init]",
"desc": "Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`. The signature of `function` must be of the form: `myfunc($accumulator, $value[, $index[, $array]])`\n\nThe optional `init` parameter is used as the initial value in the aggregation."
},
"$flowContext": {
"args": "string[, string]",

View File

@@ -302,9 +302,9 @@
"successfulRestart": "フローの再起動が成功しました",
"deployFailed": "デプロイが失敗しました: __message__",
"unusedConfigNodes": "使われていない設定ノードがあります。",
"unusedConfigNodesButton":"未使用の構成ノードを検索",
"unknownNodesButton":"不明なノードを検索する",
"invalidNodesButton":"無効なノードを検索する",
"unusedConfigNodesButton": "未使用の構成ノードを検索",
"unknownNodesButton": "不明なノードを検索する",
"invalidNodesButton": "無効なノードを検索する",
"errors": {
"noResponse": "サーバの応答がありません"
},
@@ -907,7 +907,8 @@
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー",
"hiddenFlows": "非表示のフロー",
"modifiedNodes": "修正したノードやフロー"
"modifiedNodes": "修正したノードやフロー",
"thisFlow": "現在のフロー"
}
},
"expressionEditor": {
@@ -1159,6 +1160,9 @@
"start": "開始",
"next": "次へ"
},
"diagnostics": {
"title": "システム情報"
},
"languages": {
"de": "ドイツ語",
"en-US": "英語",
@@ -1168,6 +1172,22 @@
"zh-CN": "中国語(簡体)",
"zh-TW": "中国語(繁体)"
},
"validator": {
"errors": {
"invalid-json": "JSONデータが不正: __error__",
"invalid-json-prop": "__prop__: JSONデータが不正: __error__",
"invalid-prop": "プロパティ式が不正",
"invalid-prop-prop": "__prop__: プロパティ式が不正",
"invalid-num": "数値が不正",
"invalid-num-prop": "__prop__: 数値が不正",
"invalid-regexp": "入力パターンが不正",
"invalid-regex-prop": "__prop__: 入力パターンが不正",
"missing-required-prop": "__prop__: プロパティが未設定",
"invalid-config": "__prop__: 設定ノードが不正",
"missing-config": "__prop__: 設定ノードが存在しません",
"validation-error": "__prop__: チェックエラー: __node__, __id__: __error__"
}
},
"action-list": {
"toggle-show-tips": "ヒント表示切替",
"show-about": "Node-REDの説明を表示",
@@ -1305,21 +1325,5 @@
"zoom-reset": "ズームリセット",
"toggle-navigator": "ナビゲータ表示切替",
"show-system-info": "システムインフォメーション"
},
"validator": {
"errors": {
"invalid-json": "JSONデータが不正: __error__",
"invalid-json-prop": "__prop__: JSONデータが不正: __error__",
"invalid-prop": "プロパティ式が不正",
"invalid-prop-prop": "__prop__: プロパティ式が不正",
"invalid-num": "数値が不正",
"invalid-num-prop": "__prop__: 数値が不正",
"invalid-regexp": "入力パターンが不正",
"invalid-regex-prop": "__prop__: 入力パターンが不正",
"missing-required-prop": "__prop__: プロパティが未設定",
"invalid-config": "__prop__: 設定ノードが不正",
"missing-config": "__prop__: 設定ノードが存在しません",
"validation-error": "__prop__: チェックエラー: __node__, __id__: __error__"
}
}
}

View File

@@ -17,7 +17,7 @@
"tip14": "[shift] を押しながらノードを [click] すると、接続された全てのノードを選択できます。",
"tip15": "[ctrl] を押しながらノードを [click] すると、選択/非選択を切り替えできます。",
"tip16": "{{core:show-previous-tab}} や {{core:show-next-tab}} で、タブの切り替えができます。",
"tip17": "ノードのプロティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。",
"tip17": "ノードのプロティ設定画面にて {{core:confirm-edit-tray}} を押すと、変更を確定できます。また、 {{core:cancel-edit-tray}} を押すと、変更を取り消せます。",
"tip18": "ノードを選択し、 {{core:edit-selected-node}} を押すとプロパティ設定画面が表示されます。"
}
}

View File

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

View File

@@ -76,7 +76,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
(function() {
this.setOptions = function(options) {
this.options = {
o.options = {
// undef: true,
// unused: true,
esversion: 9,
@@ -98,7 +98,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
if (options) {
for (var opt in options) {
if (options.hasOwnProperty(opt)) {
this.options[opt] = options.opt;
o.options[opt] = options[opt];
}
}
}

View File

@@ -22,6 +22,14 @@ RED.history = (function() {
var undoHistory = [];
var redoHistory = [];
function nodeOrJunction(id) {
var node = RED.nodes.node(id);
if (node) {
return node;
}
return RED.nodes.junction(id);
}
function undoEvent(ev) {
var i;
var len;
@@ -514,6 +522,7 @@ RED.history = (function() {
var z = ev.activeWorkspace;
var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id});
fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id))
fullNodeList = fullNodeList.concat(RED.nodes.junctions(ev.subflow.subflow.id))
fullNodeList.forEach(function(n) {
n.x += ev.subflow.offsetX;
n.y += ev.subflow.offsetY;
@@ -523,7 +532,7 @@ RED.history = (function() {
});
inverseEv.subflows = [];
for (i=0;i<ev.nodes.length;i++) {
inverseEv.subflows.push(RED.nodes.node(ev.nodes[i]));
inverseEv.subflows.push(nodeOrJunction(ev.nodes[i]));
RED.nodes.remove(ev.nodes[i]);
}
}

View File

@@ -738,6 +738,10 @@ RED.nodes = (function() {
moveGroupToTab(node,z);
return;
}
if (node.type === "junction") {
moveJunctionToTab(node,z);
return;
}
var oldZ = node.z;
allNodes.moveNode(node,z);
var nl = nodeLinks[node.id];
@@ -772,6 +776,39 @@ RED.nodes = (function() {
RED.events.emit("groups:change",group);
}
function moveJunctionToTab(junction, z) {
var index = junctionsByZ[junction.z].indexOf(junction);
junctionsByZ[junction.z].splice(index,1);
junctionsByZ[z] = junctionsByZ[z] || [];
junctionsByZ[z].push(junction);
var oldZ = junction.z;
junction.z = z;
var nl = nodeLinks[junction.id];
if (nl) {
nl.in.forEach(function(l) {
var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) {
linkTabMap[oldZ].splice(idx, 1);
}
if ((l.source.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l);
}
});
nl.out.forEach(function(l) {
var idx = linkTabMap[oldZ].indexOf(l);
if (idx != -1) {
linkTabMap[oldZ].splice(idx, 1);
}
if ((l.target.z === z) && linkTabMap[z]) {
linkTabMap[z].push(l);
}
});
}
RED.events.emit("junctions:change",junction);
}
function removeLink(l) {
var index = links.indexOf(l);
if (index != -1) {
@@ -2462,6 +2499,8 @@ RED.nodes = (function() {
workspacesOrder = [];
groups = {};
groupsByZ = {};
junctions = {};
junctionsByZ = {};
var subflowIds = Object.keys(subflows);
subflowIds.forEach(function(id) {

View File

@@ -21,6 +21,34 @@ RED.actions = (function() {
function getAction(name) {
return actions[name].handler;
}
function getActionLabel(name) {
let def = actions[name]
if (!def) {
return ''
}
if (!def.label) {
var options = def.options;
var key = options ? options.label : undefined;
if (!key) {
key = "action-list." +name.replace(/^.*:/,"");
}
var label = RED._(key);
if (label === key) {
// no translation. convert `name` to description
label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
if (arguments[5] === 0) {
return arguments[2].toUpperCase();
} else {
return " "+arguments[4].toUpperCase();
}
});
}
def.label = label;
}
return def.label
}
function invokeAction() {
var args = Array.prototype.slice.call(arguments);
var name = args.shift();
@@ -31,7 +59,7 @@ RED.actions = (function() {
}
function listActions() {
var result = [];
var missing = [];
Object.keys(actions).forEach(function(action) {
var def = actions[action];
var shortcut = RED.keyboard.getShortcut(action);
@@ -42,28 +70,8 @@ RED.actions = (function() {
isUser = !!RED.keyboard.getUserShortcut(action);
}
if (!def.label) {
var name = action;
var options = def.options;
var key = options ? options.label : undefined;
if (!key) {
key = "action-list." +name.replace(/^.*:/,"");
}
var label = RED._(key);
if (label === key) {
// no translation. convert `name` to description
label = name.replace(/(^.+:([a-z]))|(-([a-z]))/g, function() {
if (arguments[5] === 0) {
return arguments[2].toUpperCase();
} else {
return " "+arguments[4].toUpperCase();
}
});
missing.push(key);
}
def.label = label;
def.label = getActionLabel(action)
}
//console.log("; missing:", missing);
result.push({
id:action,
scope:shortcut?shortcut.scope:undefined,
@@ -79,6 +87,7 @@ RED.actions = (function() {
add: addAction,
remove: removeAction,
get: getAction,
getLabel: getActionLabel,
invoke: invokeAction,
list: listActions
}

View File

@@ -709,6 +709,7 @@ RED.clipboard = (function() {
} else if (type === 'flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.groups(activeWorkspace);
nodes = nodes.concat(RED.nodes.junctions(activeWorkspace));
nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
RED.nodes.eachConfig(function(n) {
if (n.z === RED.workspaces.active() && n._def.hasUsers === false) {

View File

@@ -16,6 +16,7 @@
RED.menu = (function() {
var menuItems = {};
let menuItemCount = 0
function createMenuItem(opt) {
var item;
@@ -59,15 +60,16 @@ RED.menu = (function() {
item = $('<li class="red-ui-menu-divider"></li>');
} else {
item = $('<li></li>');
if (!opt.id) {
opt.id = 'red-ui-menu-item-'+(menuItemCount++)
}
if (opt.group) {
item.addClass("red-ui-menu-group-"+opt.group);
}
var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
if (opt.toggle) {
linkContent += '<i class="fa fa-square pull-left"></i>';
linkContent += '<i class="fa fa-check-square pull-left"></i>';
linkContent += '<i class="fa fa-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
linkContent += '<i class="fa fa-check-square'+(opt.direction!=='right'?" pull-left":"")+'"></i>';
}
if (opt.icon !== undefined) {
@@ -77,12 +79,15 @@ RED.menu = (function() {
linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
}
}
let label = opt.label
if (!opt.label && typeof opt.onselect === 'string') {
label = RED.actions.getLabel(opt.onselect)
}
if (opt.sublabel) {
linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+opt.label+'</span>'+
linkContent += '<span class="red-ui-menu-label-container"><span class="red-ui-menu-label">'+label+'</span>'+
'<span class="red-ui-menu-sublabel">'+opt.sublabel+'</span></span>'
} else {
linkContent += '<span class="red-ui-menu-label"><span>'+opt.label+'</span></span>'
linkContent += '<span class="red-ui-menu-label"><span>'+label+'</span></span>'
}
linkContent += '</a>';
@@ -126,10 +131,21 @@ RED.menu = (function() {
});
}
if (opt.options) {
item.addClass("red-ui-menu-dropdown-submenu pull-left");
item.addClass("red-ui-menu-dropdown-submenu"+(opt.direction!=='right'?" pull-left":""));
var submenu = $('<ul id="'+opt.id+'-submenu" class="red-ui-menu-dropdown"></ul>').appendTo(item);
for (var i=0;i<opt.options.length;i++) {
if (opt.options[i]) {
if (opt.onpreselect && opt.options[i].onpreselect === undefined) {
opt.options[i].onpreselect = opt.onpreselect
}
if (opt.onpostselect && opt.options[i].onpostselect === undefined) {
opt.options[i].onpostselect = opt.onpostselect
}
opt.options[i].direction = opt.direction
}
var li = createMenuItem(opt.options[i]);
if (li) {
li.appendTo(submenu);
@@ -147,7 +163,9 @@ RED.menu = (function() {
}
function createMenu(options) {
var topMenu = $("<ul/>",{class:"red-ui-menu red-ui-menu-dropdown pull-right"});
if (options.direction) {
topMenu.addClass("red-ui-menu-dropdown-direction-"+options.direction)
}
if (options.id) {
topMenu.attr({id:options.id+"-submenu"});
var menuParent = $("#"+options.id);
@@ -175,6 +193,15 @@ RED.menu = (function() {
var lastAddedSeparator = false;
for (var i=0;i<options.options.length;i++) {
var opt = options.options[i];
if (opt) {
if (options.onpreselect && opt.onpreselect === undefined) {
opt.onpreselect = options.onpreselect
}
if (options.onpostselect && opt.onpostselect === undefined) {
opt.onpostselect = options.onpostselect
}
opt.direction = options.direction || 'left'
}
if (opt !== null || !lastAddedSeparator) {
var li = createMenuItem(opt);
if (li) {
@@ -190,6 +217,9 @@ RED.menu = (function() {
function triggerAction(id, args) {
var opt = menuItems[id];
var callback = opt.onselect;
if (opt.onpreselect) {
opt.onpreselect.call(opt,args)
}
if (typeof opt.onselect === 'string') {
callback = RED.actions.get(opt.onselect);
}
@@ -198,6 +228,9 @@ RED.menu = (function() {
} else {
console.log("No callback for",id,opt.onselect);
}
if (opt.onpostselect) {
opt.onpostselect.call(opt,args)
}
}
function isSelected(id) {

View File

@@ -610,10 +610,13 @@ RED.popover = (function() {
var target = options.target;
var align = options.align || "right";
var offset = options.offset || [0,0];
var xPos = options.x;
var yPos = options.y;
var isAbsolutePosition = (xPos !== undefined && yPos !== undefined)
var pos = target.offset();
var targetWidth = target.width();
var targetHeight = target.outerHeight();
var pos = isAbsolutePosition?{left:xPos, top: yPos}:target.offset();
var targetWidth = isAbsolutePosition?0:target.width();
var targetHeight = isAbsolutePosition?0:target.outerHeight();
var panelHeight = panel.height();
var panelWidth = panel.width();

View File

@@ -828,7 +828,7 @@ RED.tabs = (function() {
}
// link.attr("title",tab.label);
RED.popover.tooltip(link,function() { return tab.label})
RED.popover.tooltip(link,function() { return RED.utils.sanitize(tab.label); });
if (options.onadd) {
options.onadd(tab);

View File

@@ -1026,10 +1026,7 @@
$(opt.icon).prependTo(this.selectLabel);
}
else if (opt.icon.indexOf("/") !== -1) {
image = new Image();
image.name = opt.icon;
image.src = mapDeprecatedIcon(opt.icon);
$('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
$('<i>',{class:"red-ui-typedInput-icon", style:"mask-image: url("+opt.icon+"); -webkit-mask-image: url("+opt.icon+"); margin-right: 4px;height: 18px;width:13px"}).prependTo(this.selectLabel);
}
else {
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel);

View File

@@ -0,0 +1,175 @@
RED.contextMenu = (function() {
let menu;
function createMenu() {
// menu = RED.popover.menu({
// options: [
// {
// label: 'delete selection',
// onselect: function() {
// RED.actions.invoke('core:delete-selection')
// RED.view.focus()
// }
// },
// { label: 'world' }
// ],
// width: 200,
// })
}
function disposeMenu() {
$(document).off("mousedown.red-ui-workspace-context-menu");
if (menu) {
menu.remove();
}
menu = null;
}
function show(options) {
if (menu) {
menu.remove()
}
const selection = RED.view.selection()
const hasSelection = (selection.nodes && selection.nodes.length > 0);
const hasMultipleSelection = hasSelection && selection.nodes.length > 1;
const hasLinks = selection.links && selection.links.length > 0;
const isSingleLink = !hasSelection && hasLinks && selection.links.length === 1
const isMultipleLinks = !hasSelection && hasLinks && selection.links.length > 1
const canDelete = hasSelection || hasLinks
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
const menuItems = [
{ onselect: 'core:show-action-list', onpostselect: function() {} },
{
label: 'Insert',
options: [
{
label: 'Node',
onselect: function() {
RED.view.showQuickAddDialog({
position: [ options.x - offset.left, options.y - offset.top ],
touchTrigger: true,
splice: isSingleLink?selection.links[0]:undefined,
// spliceMultiple: isMultipleLinks
})
}
},
{
label: 'Junction',
onselect: 'core:split-wires-with-junctions',
disabled: hasSelection || !hasLinks
},
{
label: 'Link Nodes',
onselect: 'core:split-wire-with-link-nodes',
disabled: hasSelection || !hasLinks
}
]
}
]
// menuItems.push(
// {
// label: (isSingleLink || isMultipleLinks)?'Insert into wire...':'Add node...',
// onselect: function() {
// RED.view.showQuickAddDialog({
// position: [ options.x - offset.left, options.y - offset.top ],
// touchTrigger: true,
// splice: isSingleLink?selection.links[0]:undefined,
// spliceMultiple: isMultipleLinks
// })
// }
// },
// )
// if (hasLinks && !hasSelection) {
// menuItems.push({ onselect: 'core:split-wires-with-junctions', label: 'Insert junction'})
// }
menuItems.push(
null,
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
null,
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection},
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
{ onselect: 'core:delete-selection', disabled: !canDelete },
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
{ onselect: 'core:select-all-nodes' }
)
if (hasSelection) {
menuItems.push(
null,
isGroup ?
{ onselect: 'core:ungroup-selection', disabled: !isGroup }
: { onselect: 'core:group-selection', disabled: !hasSelection }
)
if (canRemoveFromGroup) {
menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
}
}
const offset = $("#red-ui-workspace-chart").offset()
menu = RED.menu.init({
direction: 'right',
onpreselect: function() {
disposeMenu()
},
onpostselect: function() {
RED.view.focus()
},
options: menuItems
});
menu.attr("id","red-ui-workspace-context-menu");
menu.css({
position: "absolute"
})
menu.appendTo("body");
// TODO: prevent the menu from overflowing the window.
var top = options.y
var left = options.x
if (top+menu.height()-$(document).scrollTop() > $(window).height()) {
top -= (top+menu.height())-$(window).height() + 22;
}
if (left+menu.width()-$(document).scrollLeft() > $(window).width()) {
left -= (left+menu.width())-$(window).width() + 18;
}
menu.css({
top: top+"px",
left: left+"px"
})
$(".red-ui-menu.red-ui-menu-dropdown").hide();
$(document).on("mousedown.red-ui-workspace-context-menu", function(evt) {
if (menu && menu[0].contains(evt.target)) {
return
}
disposeMenu()
});
menu.show();
// menu.show({
// target: $('#red-ui-main-container'),
// x: options.x,
// y: options.y
// })
}
return {
show: show
}
})()

View File

@@ -311,8 +311,8 @@
types:[
'str','num','bool',
{value:"null",label:RED._("common.type.null"),hasValue:false},
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.png"}
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.svg"},
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.svg"}
],
default: valType
});

View File

@@ -19,7 +19,6 @@
this.tabflowEditor = RED.editor.createEditor({
id: 'node-input-info',
mode: 'ace/mode/markdown',
stateId: options.stateId,
value: ""
});

View File

@@ -323,9 +323,6 @@ RED.group = (function() {
groups: [ ],
dirty: RED.nodes.dirty()
}
RED.history.push(historyEvent);
groups.forEach(function(g) {
newSelection = newSelection.concat(ungroup(g))
historyEvent.groups.push(g);

View File

@@ -363,106 +363,112 @@ RED.library = (function() {
options.onconfirm(item);
}
});
var itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
.on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
var elementPos = menuButton.offset();
var menuOptionMenu = RED.menu.init({id:"red-ui-library-browser-menu",
options: [
{id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
var defaultFolderName = "new-folder";
var defaultFolderNameMatches = {};
var selected = dirList.treeList('selected');
if (!selected.children) {
selected = selected.parent;
}
var complete = function() {
selected.children.forEach(function(c) {
if (/^new-folder/.test(c.label)) {
defaultFolderNameMatches[c.label] = true
}
});
var folderIndex = 2;
while(defaultFolderNameMatches[defaultFolderName]) {
defaultFolderName = "new-folder-"+(folderIndex++)
}
selected.treeList.expand();
var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
var newItem = {
icon: "fa fa-folder-o",
children:[],
path: selected.path,
element: input
}
var confirmAdd = function() {
var val = input.val().trim();
if (val === "") {
cancelAdd();
return;
} else {
for (var i=0;i<selected.children.length;i++) {
if (selected.children[i].label === val) {
cancelAdd();
return;
}
}
}
newItem.treeList.remove();
var finalItem = {
library: selected.library,
type: selected.type,
icon: "fa fa-folder",
children:[],
label: val,
path: newItem.path+val+"/"
}
selected.treeList.addChild(finalItem,true);
}
var cancelAdd = function() {
newItem.treeList.remove();
}
input.on('keydown', function(evt) {
evt.stopPropagation();
if (evt.keyCode === 13) {
confirmAdd();
} else if (evt.keyCode === 27) {
cancelAdd();
}
})
input.on("blur", function() {
confirmAdd();
})
selected.treeList.addChild(newItem);
setTimeout(function() {
input.trigger("focus");
input.select();
},400);
}
selected.treeList.expand(complete);
} },
// null,
// {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
// {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
]
}).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
.on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
.appendTo("body");
menuOptionMenu.css({
position: "absolute",
top: elementPos.top+"px",
left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
}).show();
}).appendTo(itemTools);
var itemTools = null;
if (options.folderTools) {
dirList.on('treelistselect', function(event, item) {
if (item.writable !== false && item.treeList) {
if (itemTools) {
itemTools.remove();
}
itemTools = $("<div>").css({position: "absolute",bottom:"6px",right:"8px"});
var menuButton = $('<button class="red-ui-button red-ui-button-small" type="button"><i class="fa fa-ellipsis-h"></i></button>')
.on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
var elementPos = menuButton.offset();
var menuOptionMenu
= RED.menu.init({id:"red-ui-library-browser-menu",
options: [
{id:"red-ui-library-browser-menu-addFolder",label:RED._("library.newFolder"), onselect: function() {
var defaultFolderName = "new-folder";
var defaultFolderNameMatches = {};
var selected = dirList.treeList('selected');
if (!selected.children) {
selected = selected.parent;
}
var complete = function() {
selected.children.forEach(function(c) {
if (/^new-folder/.test(c.label)) {
defaultFolderNameMatches[c.label] = true
}
});
var folderIndex = 2;
while(defaultFolderNameMatches[defaultFolderName]) {
defaultFolderName = "new-folder-"+(folderIndex++)
}
selected.treeList.expand();
var input = $('<input type="text" class="red-ui-treeList-input">').val(defaultFolderName);
var newItem = {
icon: "fa fa-folder-o",
children:[],
path: selected.path,
element: input
}
var confirmAdd = function() {
var val = input.val().trim();
if (val === "") {
cancelAdd();
return;
} else {
for (var i=0;i<selected.children.length;i++) {
if (selected.children[i].label === val) {
cancelAdd();
return;
}
}
}
newItem.treeList.remove();
var finalItem = {
library: selected.library,
type: selected.type,
icon: "fa fa-folder",
children:[],
label: val,
path: newItem.path+val+"/"
}
selected.treeList.addChild(finalItem,true);
}
var cancelAdd = function() {
newItem.treeList.remove();
}
input.on('keydown', function(evt) {
evt.stopPropagation();
if (evt.keyCode === 13) {
confirmAdd();
} else if (evt.keyCode === 27) {
cancelAdd();
}
})
input.on("blur", function() {
confirmAdd();
})
selected.treeList.addChild(newItem);
setTimeout(function() {
input.trigger("focus");
input.select();
},400);
}
selected.treeList.expand(complete);
} },
// null,
// {id:"red-ui-library-browser-menu-rename",label:"Rename", onselect: function() {} },
// {id:"red-ui-library-browser-menu-delete",label:"Delete", onselect: function() {} }
]
}).on('mouseleave', function(){ $(this).remove(); dirList.focus() })
.on('mouseup', function() { var self = $(this);self.hide(); dirList.focus(); setTimeout(function() { self.remove() },100)})
.appendTo("body");
menuOptionMenu.css({
position: "absolute",
top: elementPos.top+"px",
left: (elementPos.left - menuOptionMenu.width() + 20)+"px"
}).show();
}).appendTo(itemTools);
itemTools.appendTo(item.treeList.label);
}
});

View File

@@ -208,7 +208,7 @@ RED.palette = (function() {
}
function escapeCategory(category) {
return category.replace(/[ /.]/g,"_");
return category.replace(/[\x00-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]/g,"_");
}
function addNodeType(nt,def) {
if (getPaletteNode(nt).length) {

View File

@@ -148,7 +148,7 @@ RED.search = (function() {
var key = keys[i];
var kpos = keys[i].indexOf(val);
if (kpos > -1) {
var ids = Object.keys(index[key]);
var ids = Object.keys(index[key]||{});
for (j=0;j<ids.length;j++) {
var node = index[key][ids[j]];
var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
@@ -264,7 +264,7 @@ RED.search = (function() {
}
currentResults = search(value);
if (currentResults.length > 0) {
for (i=0;i<Math.min(currentResults.length,25);i++) {
for (let i=0;i<Math.min(currentResults.length,25);i++) {
searchResults.editableList('addItem',currentResults[i])
}
if (currentResults.length > 25) {

View File

@@ -604,6 +604,14 @@ RED.subflow = (function() {
return x;
}
function nodeOrJunction(id) {
var node = RED.nodes.node(id);
if (node) {
return node;
}
return RED.nodes.junction(id);
}
function convertToSubflow() {
var selection = RED.view.selection();
if (!selection.nodes) {
@@ -792,14 +800,15 @@ RED.subflow = (function() {
subflow.in.forEach(function(input) {
input.wires.forEach(function(wire) {
var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) }
var link = {source: input, sourcePort: 0, target: nodeOrJunction(wire.id) }
new_links.push(link);
RED.nodes.addLink(link);
});
});
subflow.out.forEach(function(output,i) {
output.wires.forEach(function(wire) {
var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output }
var link = {source: nodeOrJunction(wire.id), sourcePort: wire.port , target: output }
new_links.push(link);
RED.nodes.addLink(link);
});
@@ -815,7 +824,7 @@ RED.subflow = (function() {
n.links = n.links.filter(function(id) {
var isLocalLink = nodes.hasOwnProperty(id);
if (!isLocalLink) {
var otherNode = RED.nodes.node(id);
var otherNode = nodeOrJunction(id);
if (otherNode && otherNode.links) {
var i = otherNode.links.indexOf(n.id);
if (i > -1) {
@@ -831,7 +840,6 @@ RED.subflow = (function() {
RED.nodes.moveNodeToTab(n, subflow.id);
}
var historyEvent = {
t:'createSubflow',
nodes:[subflowInstance.id],

View File

@@ -104,7 +104,9 @@ RED.typeSearch = (function() {
var index = Math.max(0,selected);
if (index < children.length) {
var n = $(children[index]).find(".red-ui-editableList-item-content").data('data');
typesUsed[n.type] = Date.now();
if (!/^_action_:/.test(n.type)) {
typesUsed[n.type] = Date.now();
}
if (n.def.outputs === 0) {
confirm(n);
} else {
@@ -173,6 +175,8 @@ RED.typeSearch = (function() {
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
if (object.type === "junction") {
nodeDiv.addClass("red-ui-palette-icon-junction");
} else if (/^_action_:/.test(object.type)) {
nodeDiv.addClass("red-ui-palette-icon-junction")
} else {
var colour = RED.utils.getNodeColor(object.type,def);
nodeDiv.css('backgroundColor',colour);
@@ -182,11 +186,14 @@ RED.typeSearch = (function() {
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, false);
if (object.type !== "junction" && def.inputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
}
if (object.type !== "junction" && def.outputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
if (!/^_action_:/.test(object.type) && object.type !== "junction") {
if (def.inputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
}
if (def.outputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
}
}
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
@@ -207,7 +214,9 @@ RED.typeSearch = (function() {
}
function confirm(def) {
hide();
typesUsed[def.type] = Date.now();
if (!/^_action_:/.test(def.type)) {
typesUsed[def.type] = Date.now();
}
addCallback(def.type);
}
@@ -316,6 +325,7 @@ RED.typeSearch = (function() {
function applyFilter(filter,type,def) {
return !filter ||
(
(!filter.spliceMultiple) &&
(!filter.type || type === filter.type) &&
(!filter.input || type === 'junction' || def.inputs > 0) &&
(!filter.output || type === 'junction' || def.outputs > 0)
@@ -330,6 +340,13 @@ RED.typeSearch = (function() {
'inject','debug','function','change','switch','junction'
].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
// if (opts.filter && opts.filter.input && opts.filter.output && !opts.filter.type) {
// if (opts.filter.spliceMultiple) {
// common.push('_action_:core:split-wires-with-junctions')
// }
// common.push('_action_:core:split-wire-with-link-nodes')
// }
var recentlyUsed = Object.keys(typesUsed);
recentlyUsed.sort(function(a,b) {
return typesUsed[b]-typesUsed[a];
@@ -354,6 +371,8 @@ RED.typeSearch = (function() {
var itemDef = RED.nodes.getType(common[i]);
if (common[i] === 'junction') {
itemDef = { inputs:1, outputs: 1, label: 'junction', type: 'junction'}
} else if (/^_action_:/.test(common[i]) ) {
itemDef = { inputs:1, outputs: 1, label: common[i], type: common[i]}
}
if (itemDef) {
item = {

View File

@@ -1032,6 +1032,8 @@ RED.utils = (function() {
return "font-awesome/fa-circle-o"
} else if (def.category === 'config') {
return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
} else if ((node && /^_action_:/.test(node.type)) || /^_action_:/.test(def.type)) {
return "font-awesome/fa-cogs"
} else if (node && node.type === 'tab') {
return "red-ui-icons/red-ui-icons-flow"
// return RED.settings.apiRootUrl+"images/subflow_tab.svg"

View File

@@ -336,17 +336,17 @@ RED.view.tools = (function() {
}
function addNode() {
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
var selectedNode = selection.nodes[0];
RED.view.showQuickAddDialog([
selectedNode.x + selectedNode.w + 50,selectedNode.y
])
} else {
RED.view.showQuickAddDialog();
}
}
// function addNode() {
// var selection = RED.view.selection();
// if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].outputs > 0) {
// var selectedNode = selection.nodes[0];
// RED.view.showQuickAddDialog([
// selectedNode.x + selectedNode.w + 50,selectedNode.y
// ])
// } else {
// RED.view.showQuickAddDialog();
// }
// }
function gotoNearestNode(direction) {
@@ -815,6 +815,9 @@ RED.view.tools = (function() {
*/
function splitWiresWithLinkNodes(wires) {
let wiresToSplit = wires || RED.view.selection().links;
if (!wiresToSplit) {
return
}
if (!Array.isArray(wiresToSplit)) {
wiresToSplit = [wiresToSplit];
}
@@ -1047,6 +1050,135 @@ RED.view.tools = (function() {
}
}
function addJunctionsToWires(wires) {
let wiresToSplit = wires || RED.view.selection().links;
if (!wiresToSplit) {
return
}
if (!Array.isArray(wiresToSplit)) {
wiresToSplit = [wiresToSplit];
}
if (wiresToSplit.length === 0) {
return;
}
var removedLinks = new Set()
var addedLinks = []
var addedJunctions = []
var groupedLinks = {}
wiresToSplit.forEach(function(l) {
var sourceId = l.source.id+":"+l.sourcePort
groupedLinks[sourceId] = groupedLinks[sourceId] || []
groupedLinks[sourceId].push(l)
groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
groupedLinks[l.target.id].push(l)
});
var linkGroups = Object.keys(groupedLinks)
linkGroups.sort(function(A,B) {
return groupedLinks[B].length - groupedLinks[A].length
})
linkGroups.forEach(function(gid) {
var links = groupedLinks[gid]
var junction = {
_def: {defaults:{}},
type: 'junction',
z: RED.workspaces.active(),
id: RED.nodes.id(),
x: 0,
y: 0,
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
}
links = links.filter(function(l) { return !removedLinks.has(l) })
if (links.length === 0) {
return
}
let pointCount = 0
links.forEach(function(l) {
if (l._sliceLocation) {
junction.x += l._sliceLocation.x
junction.y += l._sliceLocation.y
delete l._sliceLocation
pointCount++
} else {
junction.x += l.source.x + l.source.w/2 + l.target.x - l.target.w/2
junction.y += l.source.y + l.target.y
pointCount += 2
}
})
junction.x = Math.round(junction.x/pointCount)
junction.y = Math.round(junction.y/pointCount)
if (RED.view.snapGrid) {
let gridSize = RED.view.gridSize()
junction.x = (gridSize*Math.round(junction.x/gridSize));
junction.y = (gridSize*Math.round(junction.y/gridSize));
}
var nodeGroups = new Set()
RED.nodes.addJunction(junction)
addedJunctions.push(junction)
let newLink
if (gid === links[0].source.id+":"+links[0].sourcePort) {
newLink = {
source: links[0].source,
sourcePort: links[0].sourcePort,
target: junction
}
} else {
newLink = {
source: junction,
sourcePort: 0,
target: links[0].target
}
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
links.forEach(function(l) {
removedLinks.add(l)
RED.nodes.removeLink(l)
let newLink
if (gid === l.target.id) {
newLink = {
source: l.source,
sourcePort: l.sourcePort,
target: junction
}
} else {
newLink = {
source: junction,
sourcePort: 0,
target: l.target
}
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
nodeGroups.add(l.source.g || "__NONE__")
nodeGroups.add(l.target.g || "__NONE__")
})
if (nodeGroups.size === 1) {
var group = nodeGroups.values().next().value
if (group !== "__NONE__") {
RED.group.addToGroup(RED.nodes.group(group), junction)
}
}
})
if (addedJunctions.length > 0) {
RED.history.push({
t: 'add',
links: addedLinks,
junctions: addedJunctions,
removedLinks: Array.from(removedLinks)
})
RED.nodes.dirty(true)
}
RED.view.redraw(true);
}
return {
init: function() {
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
@@ -1109,6 +1241,7 @@ RED.view.tools = (function() {
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });
RED.actions.add("core:generate-node-names", generateNodeNames )

View File

@@ -172,7 +172,8 @@ RED.view = (function() {
length: function() { return set.length},
get: function(i) { return set[i] },
forEach: function(func) { set.forEach(func) },
nodes: function() { return set.map(function(n) { return n.n })}
nodes: function() { return set.map(function(n) { return n.n })},
has: function(node) { return setIds.has(node.id) }
}
return api;
})();
@@ -204,13 +205,16 @@ RED.view = (function() {
function init() {
// setTimeout(function() {
// function snap(p) { return RED.view.gridSize() * Math.round(p/RED.view.gridSize())}; for (var i = 0;i<10;i++) {
// RED.nodes.addJunction({_def:{defaults:{}}, type:'junction', z:"0ccdc1d81f2729cc",id:RED.nodes.id(),x:snap(Math.floor(Math.random()*600)),y:snap(Math.floor(Math.random()*600)), w:0,h:0})
// } ; RED.view.redraw(true)
// },2000)
chart = $("#red-ui-workspace-chart");
chart.on('contextmenu', function(evt) {
evt.preventDefault()
evt.stopPropagation()
RED.contextMenu.show({
x:evt.clientX-5,
y:evt.clientY-5
})
return false
})
outer = d3.select("#red-ui-workspace-chart")
.append("svg:svg")
.attr("width", space_width)
@@ -234,6 +238,7 @@ RED.view = (function() {
.on("mousedown", canvasMouseDown)
.on("mouseup", canvasMouseUp)
.on("mouseenter", function() {
d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
if (lasso) {
if (d3.event.buttons !== 1) {
lasso.remove();
@@ -249,6 +254,7 @@ RED.view = (function() {
}
}
})
.on("mouseleave", canvasMouseLeave)
.on("touchend", function() {
d3.event.preventDefault();
clearTimeout(touchStartTime);
@@ -389,6 +395,9 @@ RED.view = (function() {
drag_lines = [];
RED.events.on("workspace:change",function(event) {
// Just in case the mouse left the workspace whilst doing an action,
// put us back into default mode so the refresh works
mouse_mode = 0
if (event.old !== 0) {
workspaceScrollPositions[event.old] = {
left:chart.scrollLeft(),
@@ -530,6 +539,23 @@ RED.view = (function() {
nn.x = mousePos[0];
nn.y = mousePos[1];
var minX = nn.w/2 -5;
if (nn.x < minX) {
nn.x = minX;
}
var minY = nn.h/2 -5;
if (nn.y < minY) {
nn.y = minY;
}
var maxX = space_width -nn.w/2 +5;
if (nn.x > maxX) {
nn.x = maxX;
}
var maxY = space_height -nn.h +5;
if (nn.y > maxY) {
nn.y = maxY;
}
if (snapGrid) {
var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn);
nn.x -= gridOffset.x;
@@ -959,8 +985,9 @@ RED.view = (function() {
}
function canvasMouseDown() {
if (RED.view.DEBUG) { console.warn("canvasMouseDown", mouse_mode); }
var point;
if (RED.view.DEBUG) {
console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
}
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
@@ -973,49 +1000,56 @@ RED.view = (function() {
scroll_position = [chart.scrollLeft(),chart.scrollTop()];
return;
}
if (!mousedown_node && !mousedown_link && !mousedown_group) {
if (d3.event.button === 2) {
return
}
if (!mousedown_node && !mousedown_link && !mousedown_group && !d3.event.shiftKey) {
selectedLinks.clear();
updateSelection();
}
if (mouse_mode === 0) {
if (lasso) {
lasso.remove();
lasso = null;
}
if (mouse_mode === 0 && lasso) {
lasso.remove();
lasso = null;
}
if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.touches || d3.event.button === 0) && (d3.event.metaKey || d3.event.ctrlKey)) {
// Trigger quick add dialog
d3.event.stopPropagation();
clearSelection();
point = d3.mouse(this);
var clickedGroup = getGroupAt(point[0],point[1]);
if (drag_lines.length > 0) {
clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
if (d3.event.touches || d3.event.button === 0) {
if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.metaKey || d3.event.ctrlKey) && !(d3.event.altKey || d3.event.shiftKey)) {
// Trigger quick add dialog
d3.event.stopPropagation();
clearSelection();
const point = d3.mouse(this);
var clickedGroup = getGroupAt(point[0], point[1]);
if (drag_lines.length > 0) {
clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g)
}
showQuickAddDialog({ position: point, group: clickedGroup });
} else if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) {
// CTRL not being held
if (!d3.event.altKey) {
// ALT not held (shift is allowed) Trigger lasso
if (!touchStartTime) {
const point = d3.mouse(this);
lasso = eventLayer.append("rect")
.attr("ox", point[0])
.attr("oy", point[1])
.attr("rx", 1)
.attr("ry", 1)
.attr("x", point[0])
.attr("y", point[1])
.attr("width", 0)
.attr("height", 0)
.attr("class", "nr-ui-view-lasso");
d3.event.preventDefault();
}
} else if (d3.event.altKey) {
//Alt [+shift] held - Begin slicing
clearSelection();
mouse_mode = (d3.event.shiftKey) ? RED.state.SLICING_JUNCTION : RED.state.SLICING;
const point = d3.mouse(this);
slicePath = eventLayer.append("path").attr("class", "nr-ui-view-slice").attr("d", `M${point[0]} ${point[1]}`)
slicePathLast = point;
RED.view.redraw();
}
}
showQuickAddDialog({position:point, group:clickedGroup});
} else if (mouse_mode === 0 && (d3.event.touches || d3.event.button === 0) && !(d3.event.metaKey || d3.event.ctrlKey)) {
// Tigger lasso
if (!touchStartTime) {
point = d3.mouse(this);
lasso = eventLayer.append("rect")
.attr("ox",point[0])
.attr("oy",point[1])
.attr("rx",1)
.attr("ry",1)
.attr("x",point[0])
.attr("y",point[1])
.attr("width",0)
.attr("height",0)
.attr("class","nr-ui-view-lasso");
d3.event.preventDefault();
}
} else if (mouse_mode === 0 && d3.event.button === 2 && (d3.event.metaKey || d3.event.ctrlKey || d3.event.shiftKey)) {
clearSelection();
mouse_mode = (d3.event.metaKey || d3.event.ctrlKey)?RED.state.SLICING : RED.state.SLICING_JUNCTION;
point = d3.mouse(this);
slicePath = eventLayer.append("path").attr("class","nr-ui-view-slice").attr("d",`M${point[0]} ${point[1]}`)
slicePathLast = point;
RED.view.redraw();
}
}
@@ -1023,6 +1057,7 @@ RED.view = (function() {
options = options || {};
var point = options.position || lastClickPosition;
var spliceLink = options.splice;
var spliceMultipleLinks = options.spliceMultiple
var targetGroup = options.group;
var touchTrigger = options.touchTrigger;
@@ -1035,6 +1070,10 @@ RED.view = (function() {
var ox = point[0];
var oy = point[1];
const offset = $("#red-ui-workspace-chart").offset()
var clientX = ox + offset.left
var clientY = oy + offset.top
if (RED.settings.get("editor").view['view-snap-grid']) {
// eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red')
point[0] = Math.round(point[0] / gridSize) * gridSize;
@@ -1086,8 +1125,12 @@ RED.view = (function() {
}
hideDragLines();
}
if (spliceLink) {
filter = {input:true, output:true}
if (spliceLink || spliceMultipleLinks) {
filter = {
input:true,
output:true,
spliceMultiple: spliceMultipleLinks
}
}
var rebuildQuickAddLink = function() {
@@ -1112,8 +1155,8 @@ RED.view = (function() {
var lastAddedWidth;
RED.typeSearch.show({
x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]),
y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
x:clientX-mainPos.left-node_width/2 - (ox-point[0]),
y:clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]),
disableFocus: touchTrigger,
filter: filter,
move: function(dx,dy) {
@@ -1141,7 +1184,7 @@ RED.view = (function() {
hideDragLines();
redraw();
},
add: function(type,keepAdding) {
add: function(type, keepAdding) {
if (touchTrigger) {
keepAdding = false;
resetMouseVars();
@@ -1149,7 +1192,13 @@ RED.view = (function() {
var nn;
var historyEvent;
if (type === 'junction') {
if (/^_action_:/.test(type)) {
const actionName = type.substring(9)
quickAddActive = false;
ghostNode.remove();
RED.actions.invoke(actionName)
return
} else if (type === 'junction') {
nn = {
_def: {defaults:{}},
type: 'junction',
@@ -1715,10 +1764,19 @@ RED.view = (function() {
redraw();
}
}
function canvasMouseLeave() {
if (mouse_mode !== 0 && d3.event.buttons !== 0) {
d3.select(document).on('mouseup.red-ui-workspace-tracker', function() {
d3.select(document).on('mouseup.red-ui-workspace-tracker', null)
canvasMouseUp.call(this)
})
}
}
function canvasMouseUp() {
lastClickPosition = [d3.event.offsetX/scaleFactor,d3.event.offsetY/scaleFactor];
if (RED.view.DEBUG) { console.warn("canvasMouseUp", mouse_mode); }
if (RED.view.DEBUG) {
console.warn("canvasMouseUp", { mouse_mode, point: d3.mouse(this), event: d3.event });
}
var i;
var historyEvent;
if (mouse_mode === RED.state.PANNING) {
@@ -1812,8 +1870,20 @@ RED.view = (function() {
}
}
})
activeLinks.forEach(function(link) {
if (!link.selected) {
var sourceY = link.source.y
var targetY = link.target.y
var sourceX = link.source.x+(link.source.w/2) + 10
var targetX = link.target.x-(link.target.w/2) - 10
if (
sourceX > x && sourceX < x2 && sourceY > y && sourceY < y2 &&
targetX > x && targetX < x2 && targetY > y && targetY < y2
) {
selectedLinks.add(link);
}
}
})
// var selectionChanged = false;
// do {
@@ -1861,86 +1931,118 @@ RED.view = (function() {
slicePath = null;
RED.view.redraw(true);
} else if (mouse_mode == RED.state.SLICING_JUNCTION) {
var removedLinks = []
var addedLinks = []
var addedJunctions = []
var groupedLinks = {}
selectedLinks.forEach(function(l) {
var sourceId = l.source.id+":"+l.sourcePort
groupedLinks[sourceId] = groupedLinks[sourceId] || []
groupedLinks[sourceId].push(l)
});
var linkGroups = Object.keys(groupedLinks)
linkGroups.forEach(function(gid) {
var links = groupedLinks[gid]
var junction = {
_def: {defaults:{}},
type: 'junction',
z: RED.workspaces.active(),
id: RED.nodes.id(),
x: 0,
y: 0,
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
}
links.forEach(function(l) {
junction.x += l._sliceLocation.x
junction.y += l._sliceLocation.y
})
junction.x = Math.round(junction.x/links.length)
junction.y = Math.round(junction.y/links.length)
if (snapGrid) {
junction.x = (gridSize*Math.round(junction.x/gridSize));
junction.y = (gridSize*Math.round(junction.y/gridSize));
}
var nodeGroups = new Set()
RED.nodes.addJunction(junction)
addedJunctions.push(junction)
var newLink = {
source: links[0].source,
sourcePort: links[0].sourcePort,
target: junction
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
links.forEach(function(l) {
removedLinks.push(l)
RED.nodes.removeLink(l)
var newLink = {
source: junction,
sourcePort: 0,
target: l.target
}
addedLinks.push(newLink)
RED.nodes.addLink(newLink)
nodeGroups.add(l.source.g || "__NONE__")
nodeGroups.add(l.target.g || "__NONE__")
})
if (nodeGroups.size === 1) {
var group = nodeGroups.values().next().value
if (group !== "__NONE__") {
RED.group.addToGroup(RED.nodes.group(group), junction)
}
}
})
RED.actions.invoke("core:split-wires-with-junctions")
slicePath.remove();
slicePath = null;
if (addedJunctions.length > 0) {
RED.history.push({
t: 'add',
links: addedLinks,
junctions: addedJunctions,
removedLinks: removedLinks
})
RED.nodes.dirty(true)
}
RED.view.redraw(true);
// var removedLinks = new Set()
// var addedLinks = []
// var addedJunctions = []
//
// var groupedLinks = {}
// selectedLinks.forEach(function(l) {
// var sourceId = l.source.id+":"+l.sourcePort
// groupedLinks[sourceId] = groupedLinks[sourceId] || []
// groupedLinks[sourceId].push(l)
//
// groupedLinks[l.target.id] = groupedLinks[l.target.id] || []
// groupedLinks[l.target.id].push(l)
// });
// var linkGroups = Object.keys(groupedLinks)
// linkGroups.sort(function(A,B) {
// return groupedLinks[B].length - groupedLinks[A].length
// })
// linkGroups.forEach(function(gid) {
// var links = groupedLinks[gid]
// var junction = {
// _def: {defaults:{}},
// type: 'junction',
// z: RED.workspaces.active(),
// id: RED.nodes.id(),
// x: 0,
// y: 0,
// w: 0, h: 0,
// outputs: 1,
// inputs: 1,
// dirty: true
// }
// links = links.filter(function(l) { return !removedLinks.has(l) })
// if (links.length === 0) {
// return
// }
// links.forEach(function(l) {
// junction.x += l._sliceLocation.x
// junction.y += l._sliceLocation.y
// })
// junction.x = Math.round(junction.x/links.length)
// junction.y = Math.round(junction.y/links.length)
// if (snapGrid) {
// junction.x = (gridSize*Math.round(junction.x/gridSize));
// junction.y = (gridSize*Math.round(junction.y/gridSize));
// }
//
// var nodeGroups = new Set()
//
// RED.nodes.addJunction(junction)
// addedJunctions.push(junction)
// let newLink
// if (gid === links[0].source.id+":"+links[0].sourcePort) {
// newLink = {
// source: links[0].source,
// sourcePort: links[0].sourcePort,
// target: junction
// }
// } else {
// newLink = {
// source: junction,
// sourcePort: 0,
// target: links[0].target
// }
// }
// addedLinks.push(newLink)
// RED.nodes.addLink(newLink)
// links.forEach(function(l) {
// removedLinks.add(l)
// RED.nodes.removeLink(l)
// let newLink
// if (gid === l.target.id) {
// newLink = {
// source: l.source,
// sourcePort: l.sourcePort,
// target: junction
// }
// } else {
// newLink = {
// source: junction,
// sourcePort: 0,
// target: l.target
// }
// }
// addedLinks.push(newLink)
// RED.nodes.addLink(newLink)
// nodeGroups.add(l.source.g || "__NONE__")
// nodeGroups.add(l.target.g || "__NONE__")
// })
// if (nodeGroups.size === 1) {
// var group = nodeGroups.values().next().value
// if (group !== "__NONE__") {
// RED.group.addToGroup(RED.nodes.group(group), junction)
// }
// }
// })
// slicePath.remove();
// slicePath = null;
//
// if (addedJunctions.length > 0) {
// RED.history.push({
// t: 'add',
// links: addedLinks,
// junctions: addedJunctions,
// removedLinks: Array.from(removedLinks)
// })
// RED.nodes.dirty(true)
// }
// RED.view.redraw(true);
}
if (mouse_mode == RED.state.MOVING_ACTIVE) {
if (movingSet.length() > 0) {
@@ -2917,7 +3019,7 @@ RED.view = (function() {
} else if (drag_line.portType === PORT_TYPE_INPUT) {
src = mouseup_node;
dst = drag_line.node;
src_port = portIndex;
src_port = portIndex || 0;
}
var link = {source: src, sourcePort:src_port, target: dst};
if (drag_line.virtualLink) {
@@ -3208,52 +3310,16 @@ RED.view = (function() {
port.classed("red-ui-flow-port-hovered",false);
}
function junctionMouseOver(junction, d) {
junction.classed("red-ui-flow-junction-hovered",true);
function junctionMouseOver(junction, d, portType) {
var active = (portType === undefined) ||
(mouse_mode !== RED.state.JOINING && mouse_mode !== RED.state.QUICK_JOINING) ||
(drag_lines.length > 0 && drag_lines[0].portType !== portType && !drag_lines[0].virtualLink)
junction.classed("red-ui-flow-junction-hovered", active);
}
function junctionMouseOut(junction, d) {
junction.classed("red-ui-flow-junction-hovered",false);
}
function junctionMouseDown(junction, d, evt) {
if (RED.view.DEBUG) { console.warn("junctionMouseDown", d); }
evt = evt || d3.event;
d3.event = evt
if (evt === 1) {
return;
}
if (mouse_mode === RED.state.SELECTING_NODE) {
evt.stopPropagation();
return;
}
if (mouse_mode == RED.state.QUICK_JOINING) {
d3.event.stopPropagation();
return;
}
// mousedown_node = d;
// mousedown_port_type = portType;
// mousedown_port_index = portIndex || 0;
if (mouse_mode !== RED.state.QUICK_JOINING && (evt.ctrlKey || evt.metaKey)) {
mouse_mode = RED.state.QUICK_JOINING;
document.body.style.cursor = "crosshair";
showDragLines([{node:d,port:0,portType: PORT_TYPE_OUTPUT}]);
$(window).on('keyup',disableQuickJoinEventHandler);
} else if (event.button != 2) {
nodeMouseDown.call(junction[0][0],d)
// clearSelection();
// movingSet.add(d);
// mousedown_node = d;
// mouse_mode = RED.state.MOVING;
// var mouse = d3.touches(junction[0][0])[0]||d3.mouse(junction[0][0]);
// mouse[0] += d.x-d.w/2;
// mouse[1] += d.y-d.h/2;
// prepareDrag(mouse);
}
evt.stopPropagation();
evt.preventDefault();
}
function prepareDrag(mouse) {
mouse_mode = RED.state.MOVING;
// Called when movingSet should be prepared to be dragged
@@ -3413,6 +3479,9 @@ RED.view = (function() {
return;
} else if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
if (d.type === 'junction') {
return
}
if (selectNodesOptions.single) {
selectNodesOptions.done(d);
return;
@@ -3439,12 +3508,12 @@ RED.view = (function() {
var now = Date.now();
clickElapsed = now-clickTime;
clickTime = now;
dblClickPrimed = (lastClickNode == mousedown_node &&
dblClickPrimed = lastClickNode == mousedown_node &&
(d3.event.touches || d3.event.button === 0) &&
!d3.event.shiftKey && !d3.event.altKey &&
clickElapsed < dblClickInterval
)
lastClickNode = mousedown_node;
clickElapsed < dblClickInterval &&
d.type !== 'junction'
lastClickNode = mousedown_node;
if (!d.selected && d.g /*&& !RED.nodes.group(d.g).selected*/) {
var nodeGroup = RED.nodes.group(d.g);
@@ -3570,9 +3639,9 @@ RED.view = (function() {
clearSelection();
}
var clickPosition = (d3.event.offsetX/scaleFactor - mousedown_node.x)
var edgeDelta = (mousedown_node.w/2) - Math.abs(clickPosition);
var edgeDelta = ((mousedown_node.w||10)/2) - Math.abs(clickPosition);
var cnodes;
var targetEdgeDelta = mousedown_node.w > 30 ? 25 : 8;
var targetEdgeDelta = mousedown_node.w > 30 ? 25 : (mousedown_node.w > 0 ? 8 : 3);
if (edgeDelta < targetEdgeDelta) {
if (clickPosition < 0) {
cnodes = [mousedown_node].concat(RED.nodes.getAllUpstreamNodes(mousedown_node));
@@ -3717,12 +3786,13 @@ RED.view = (function() {
function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); }
function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__) }
function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__, this.__portType__) }
function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) }
function junctionMouseDownProxy(e) { junctionMouseDown(d3.select(this), this.__data__, e) }
function junctionMouseUpProxy(e) { junctionMouseUp(d3.select(this), this.__data__) }
function linkMouseDown(d) {
if (RED.view.DEBUG) {
console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event });
}
if (mouse_mode === RED.state.SELECTING_NODE) {
d3.event.stopPropagation();
return;
@@ -4871,22 +4941,56 @@ RED.view = (function() {
junctionBack.setAttribute("y",-5);
junctionBack.setAttribute("width",10);
junctionBack.setAttribute("height",10);
junctionBack.setAttribute("rx",5);
junctionBack.setAttribute("ry",5);
junctionBack.setAttribute("rx",3);
junctionBack.setAttribute("ry",3);
junctionBack.__data__ = d;
this.__junctionBack__ = junctionBack;
contents.appendChild(junctionBack);
var junctionInput = document.createElementNS("http://www.w3.org/2000/svg","rect");
junctionInput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-input");
junctionInput.setAttribute("x",-5);
junctionInput.setAttribute("y",-5);
junctionInput.setAttribute("width",10);
junctionInput.setAttribute("height",10);
junctionInput.setAttribute("rx",3);
junctionInput.setAttribute("ry",3);
junctionInput.__data__ = d;
junctionInput.__portType__ = PORT_TYPE_INPUT;
junctionInput.__portIndex__ = 0;
this.__junctionInput__ = junctionOutput;
contents.appendChild(junctionInput);
junctionInput.addEventListener("mouseup", portMouseUpProxy);
junctionInput.addEventListener("mousedown", portMouseDownProxy);
this.__junctionInput__ = junctionInput;
contents.appendChild(junctionInput);
var junctionOutput = document.createElementNS("http://www.w3.org/2000/svg","rect");
junctionOutput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-output");
junctionOutput.setAttribute("x",-5);
junctionOutput.setAttribute("y",-5);
junctionOutput.setAttribute("width",10);
junctionOutput.setAttribute("height",10);
junctionOutput.setAttribute("rx",3);
junctionOutput.setAttribute("ry",3);
junctionOutput.__data__ = d;
junctionOutput.__portType__ = PORT_TYPE_OUTPUT;
junctionOutput.__portIndex__ = 0;
this.__junctionOutput__ = junctionOutput;
contents.appendChild(junctionOutput);
junctionOutput.addEventListener("mouseup", portMouseUpProxy);
junctionOutput.addEventListener("mousedown", portMouseDownProxy);
junctionOutput.addEventListener("mouseover", junctionMouseOverProxy);
junctionOutput.addEventListener("mouseout", junctionMouseOutProxy);
junctionInput.addEventListener("mouseover", junctionMouseOverProxy);
junctionInput.addEventListener("mouseout", junctionMouseOutProxy);
junctionBack.addEventListener("mouseover", junctionMouseOverProxy);
junctionBack.addEventListener("mouseout", junctionMouseOutProxy);
junctionBack.addEventListener("mouseup", portMouseUpProxy);
junctionBack.addEventListener("mousedown", junctionMouseDownProxy);
// d3.select(junctionBack).on("mousedown", nodeMouseDown);
this.__portType__ = PORT_TYPE_INPUT
this.__portIndex__ = 0
// function portMouseUpProxy(e) { portMouseUp(this.__data__,this.__portType__,this.__portIndex__, e); }
// These handlers expect to be registered as d3 events
d3.select(junctionBack).on("mousedown", nodeMouseDown).on("mouseup", nodeMouseUp);
junction[0][0].appendChild(contents);
})
@@ -4895,6 +4999,7 @@ RED.view = (function() {
var junction = d3.select(this);
this.setAttribute("transform", "translate(" + (d.x) + "," + (d.y) + ")");
if (d.dirty) {
junction.classed("red-ui-flow-junction-dragging", mouse_mode === RED.state.MOVING_ACTIVE && movingSet.has(d))
junction.classed("selected", !!d.selected)
dirtyNodes[d.id] = d;
@@ -4935,10 +5040,11 @@ RED.view = (function() {
.on("touchstart",linkTouchStart)
.on("mousemove", function(d) {
if (mouse_mode === RED.state.SLICING) {
selectedLinks.add(d)
l.classed("red-ui-flow-link-splice",true)
redraw()
} else if (mouse_mode === RED.state.SLICING_JUNCTION) {
} else if (mouse_mode === RED.state.SLICING_JUNCTION && !d.link) {
if (!l.classed("red-ui-flow-link-splice")) {
// Find intersection point
var lineLength = pathLine.getTotalLength();
@@ -5684,7 +5790,12 @@ RED.view = (function() {
node.dirty = true;
node.dirtyStatus = true;
node.changed = true;
RED.events.emit("nodes:change",node);
if (node.type === "junction") {
RED.events.emit("junctions:change",node);
}
else {
RED.events.emit("nodes:change",node);
}
}
}
}

View File

@@ -28,7 +28,7 @@
border-radius: 1px;
}
.ace_gutter-cell {
color: $text-editor-color;
color: $text-editor-gutter-color;
}
.ace_gutter-active-line {
background: $text-editor-gutter-active-line-background;

View File

@@ -44,7 +44,7 @@ body {
#red-ui-palette-shade, #red-ui-editor-shade, #red-ui-header-shade, #red-ui-sidebar-shade {
@include shade;
z-index: 2;
z-index: 5;
}
#red-ui-sidebar-shade {
left: -8px;

View File

@@ -46,7 +46,7 @@
& > li > a,
& > li > a:focus {
display: block;
padding: 4px 12px 4px 32px;
padding: 4px 20px 4px 12px;
clear: both;
font-weight: normal;
line-height: 20px;
@@ -54,7 +54,10 @@
white-space: normal !important;
outline: none;
}
& > li.pull-left > a,
& > li.pull-left > a:focus {
padding: 4px 12px 4px 32px;
}
& > .active > a,
& > .active > a:hover,
& > .active > a:focus {
@@ -145,8 +148,8 @@
position: relative;
& > .red-ui-menu-dropdown {
top: 0;
left: 100%;
margin-top: -6px;
left: calc(100% - 5px);
margin-top: 0;
margin-left: -1px;
}
&.open > .red-ui-menu-dropdown,
@@ -175,10 +178,10 @@
}
}
.red-ui-menu-dropdown-submenu>a:after {
.red-ui-menu-dropdown-submenu.pull-left>a:after {
display: none;
}
.red-ui-menu-dropdown-submenu>a:before {
.red-ui-menu-dropdown-submenu.pull-left>a:before {
display: block;
float: left;
width: 0;
@@ -192,7 +195,25 @@
border-width: 5px 5px 5px 0;
content: " ";
}
.red-ui-menu-dropdown-direction-right {
.red-ui-menu-dropdown-submenu>a:after {
display: none;
}
.red-ui-menu-dropdown-submenu>a:before {
display: block;
float: right;
width: 0;
height: 0;
margin-top: 5px;
margin-right: -15px;
/* Caret Arrow */
border-color: transparent;
border-left-color: $menuCaret;
border-style: solid;
border-width: 5px 0 5px 5px;
content: " ";
}
}
.red-ui-menu-dropdown-submenu.disabled > a:before {
border-right-color: $menuCaret;
}

View File

@@ -379,11 +379,46 @@ g.red-ui-flow-link-unknown path.red-ui-flow-link-line {
white-space: pre;
@include disable-selection;
}
.red-ui-flow-junction-dragging {
.red-ui-flow-junction-background {
background: red !important
}
}
.red-ui-flow-junction:not(.red-ui-flow-junction-dragging):hover {
.red-ui-flow-junction-background {
transform: scale(1.4);
stroke-width: 0.6;
}
.red-ui-flow-junction-port {
opacity: 1;
pointer-events: auto;
}
.red-ui-flow-junction-port-input {
transform: translate(-10px,0)
}
.red-ui-flow-junction-port-output {
transform: translate(10px,0)
}
}
.red-ui-flow-junction-port {
stroke: $node-border;
stroke-width: 1;
fill: $node-port-background;
cursor: crosshair;
transition: transform 0.1s;
opacity: 0;
pointer-events: none;
}
.red-ui-flow-junction-background {
stroke: $node-border;
stroke-width: 1;
fill: $node-port-background;
cursor: crosshair;
transform: scale(1);
transition: transform 0.1s;
&:hover {
}
}
.red-ui-flow-junction-hovered {
stroke: $port-selected-color;

View File

@@ -27,10 +27,10 @@
}
.ui-widget.ui-widget-content {
border: 1px solid $tertiary-border-color;
border: 1px solid $tertiary-border-color;
}
.ui-widget-content {
border: 1px solid $secondary-border-color;
border: 1px solid $secondary-border-color;
}
.ui-widget-header {

View File

@@ -131,6 +131,7 @@
width: 120px;
background-size: contain;
position: relative;
z-index: 4;
&:not(.red-ui-palette-node-config):not(.red-ui-palette-node-small):first-child {
margin-top: 15px;
}

View File

@@ -87,8 +87,7 @@
display: inline-block;
cursor: ew-resize;
background-color: $primary-background;
}
&:before {
content: '';
display: block;
@@ -104,4 +103,5 @@
mask-repeat: no-repeat;
background-color: $grip-color;
}
}
}

View File

@@ -84,7 +84,7 @@
.red-ui-search-result-node-port {
position: absolute;
border-radius: 2px;
border: 1px solid $node-border;;
border: 1px solid $node-border;
width: 6px;
height: 7px;
top:4px;

View File

@@ -153,6 +153,16 @@ button.red-ui-typedInput-option-trigger
img {
max-width: none;
}
.red-ui-typedInput-icon:not(.fa) {
display: inline-block;
-webkit-mask-size: cover;
mask-size: cover;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
background-color: $primary-text-color;
}
}
&:not(.disabled):hover {

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@@ -1,48 +1,63 @@
export default {
version: "3.0.0-beta.1",
version: "3.0.0-beta.3",
steps: [
{
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 3.0 Beta 1!",
"ja": "Node-RED 3.0 Beta 1へようこそ!"
"en-US": "Welcome to Node-RED 3.0 Beta 3!",
"ja": "Node-RED 3.0 ベータ3へようこそ!"
},
description: {
"en-US": "<p>This is the first Beta release of Node-RED 3.0. It contains just about everything we have planned for the final release.</p><p>Let's take a moment to discover the new features in this release.</p>",
"ja": "本リリースの新機能を見つけてみましょう。"
"en-US": "<p>This is the final beta release of Node-RED 3.0.</p><p>Let's take a moment to discover the new features in this release.</p>",
// "ja": "<p>これはNode-RED 3.0の最初のベータリリースです。これには、最終リリースで計画しているほぼ全ての機能が含まれています。</p><p>本リリースの新機能を見つけてみましょう。</p>"
}
},
{
title: {
"en-US": "Context Menu"
},
image: 'images/context-menu.png',
description: {
"en-US": `<p>The editor now has its own context menu when you
right-click in the workspace.</p>
<p>This makes many of the built-in actions much easier
to access.</p>`
}
},
{
title: {
"en-US": "Wire Junctions",
// "ja": ""
"ja": "分岐点をワイヤーに追加"
},
image: 'images/junction-slice.gif',
description: {
"en-US": `<p>To make it easier to route wires around your flows, it is now possible to
add junction nodes that give you more control.</p>
<p>Junctions can be added to wires by holding the Shift key, then click and drag with
the right-hand mouse button across the wires.</p>`,
// "ja": ""
"en-US": `<p>To make it easier to route wires around your flows,
it is now possible to add junction nodes that give
you more control.</p>
<p>Junctions can be added to wires by holding the Alt key
then click and drag the mouse across the wires.</p>`,
// "ja": `<p>フローのワイヤーの経路をより制御しやすくするために、分岐点ノードを追加できるようになりました。</p>
// <p>シフトキーを押しながら、マウスの右ボタンをクリックし、ワイヤーを横切るようにドラッグすることで、分岐点を追加できます。</p>`
},
},
{
title: {
"en-US": "Wire Junctions",
// "ja": ""
"ja": "分岐点をワイヤーに追加"
},
image: 'images/junction-quick-add.png',
description: {
"en-US": `<p>Junctions can also be added using the quick-add dialog.</p>
<p>The dialog is opened by holding the Ctrl (or Cmd) key when
clicking in the workspace.</p>`,
// "ja": ""
"ja": `<p>クイック追加ダイアログを用いて、分岐点を追加することもできます。</p>
<p>本ダイアログを開くには、Ctrl(またはCmd)キーを押しながら、ワークスペース上でクリックします。</p>`
},
},
{
title: {
"en-US": "Debug Path Tooltip",
// "ja": ""
"ja": "デバッグパスのツールチップ"
},
image: 'images/debug-path-tooltip.png',
description: {
@@ -53,20 +68,22 @@ export default {
the message.</p>
<p>Clicking on any item in the list will reveal it in
the workspace.</p>`,
// "ja": ""
"ja": `<p>デバックサイドバー内のノード名の上にマウスカーソルを乗せると、新たにツールチップが表示され、ノードの場所が分かるようになっています。</p>
<p>これは、サブフローを用いる時に役立つ機能であり、メッセージがどのノードから出力されたかを正確に特定することが遥かに簡単になります。</p>
<p>本リスト内の要素をクリックすると、ワークスペース内にその要素が表示されます。</p>`
},
},
{
title: {
"en-US": "Continuous Search",
// "ja": ""
"ja": "連続した検索"
},
image: 'images/continuous-search.png',
description: {
"en-US": `<p>When searching for things in the editor, a new toolbar in
the workspace provides options to quickly jump between
the search results.</p>`,
// "ja": ""
"ja": `<p>ワークスペース内の新しいツールバーにあるオプションによって、エディタ内を検索する際に、検索結果の間を素早く移動できます。</p>`
},
},
{
@@ -81,13 +98,17 @@ export default {
<li><b><code>Split Wire With Link Nodes</code></b></li>
</ul>
<p>Actions can be accessed from the Action List in the main menu.</p>`,
// "ja": ``
"ja": `<p>ワイヤーを、接続されたLinkードのペアに置き換える動作が新たに追加されました:</p>
<ul>
<li><b><code>ワイヤーをlinkードで分割</code></b></li>
</ul>
<p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>`,
},
},
{
title: {
"en-US": "Default node names",
// "ja": ""
"ja": "標準ノードの名前"
},
// image: "images/",
description: {
@@ -100,7 +121,12 @@ export default {
<li><b><code>Generate Node Names</code></b></li>
</ul><p>Actions can be accessed from the Action List in the main menu.</p>
`,
// "ja": ``
"ja": `<p>一部のノードは、ワークスペース上に新インスタンスとして追加した際に、一意の名前を付けるよう変更されました。この変更は、<code>Debug</code>、<code>Function</code>、<code>Link</code>ノードに適用されています。</p>
<p>選択したノードに対して、標準の名前を生成する動作も新たに追加されました:</p>
<ul>
<li><b><code>ノード名を生成</code></b></li>
</ul><p>本アクションは、メインメニュー内の動作一覧から呼び出せます。</p>
`
}
},
{
@@ -115,7 +141,11 @@ export default {
<li>The Link Call node can use a message property to dynamically target the link it should call</li>
<li>The HTTP Request node can be preconfigured with HTTP headers</li>
</ul>`,
// "ja": ``
"ja": `<ul>
<li>Debugードは、受信したメッセージの数をカウントするよう設定できるようになりました。</li>
<li>Link Callードは、メッセージのプロパティによって、呼び出し対象のlinkを動的に指定できるようになりました。</li>
<li>HTTP Requestードは、HTTPヘッダを事前設定できるようになりました。</li>
</ul>`
}
}
]

View File

@@ -69,7 +69,7 @@
outputs:1, // set the number of outputs - 0 to n
color: "#ddd", // set icon color
// set the icon (held in icons dir below where you save the node)
icon: "myicon.png", // saved in icons/myicon.png
icon: "myicon.svg", // saved in icons/myicon.svg
label: function() { // sets the default label contents
return this.name||this.topic||"sample";
},

View File

@@ -109,9 +109,10 @@ module.exports = function(RED) {
if (!property) return;
if (valueType === "jsonata") {
if (p.exp) {
if (p.v) {
try {
var val = RED.util.evaluateJSONataExpression(p.exp, msg);
var exp = RED.util.prepareJSONataExpression(p.v, node);
var val = RED.util.evaluateJSONataExpression(exp, msg);
RED.util.setMessageProperty(msg, property, val, true);
}
catch (err) {

View File

@@ -499,7 +499,7 @@
types:['msg', fullType, "jsonata"],
typeField: $("#node-input-targetType")
});
if (this.targetType === "jsonata") {
if (this.targetType === "jsonata") {
var property = this.complete || "";
$("#node-input-typed-complete").typedInput('type','jsonata');
$("#node-input-typed-complete").typedInput('value',property);

View File

@@ -582,7 +582,7 @@ RED.debug = (function() {
$('<span class="red-ui-debug-msg-date">'+ getTimestamp()+'</span>').appendTo(metaRow);
if (sourceNode) {
var nodeLink = $('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text("node: "+(o.name||sourceNode.name||sourceNode.id))
var nodeLink = $('<a>',{href:"#",class:"red-ui-debug-msg-name"}).text(RED._("node-red:debug.node")+": "+(o.name||sourceNode.name||sourceNode.id))
.appendTo(metaRow)
.on("click", function(evt) {
evt.preventDefault();

View File

@@ -460,7 +460,7 @@
}
});
var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs) {
var buildEditor = function(id, stateId, focus, value, defaultValue, extraLibs, offset) {
var editor = RED.editor.createEditor({
id: id,
mode: 'ace/mode/nrjavascript',
@@ -484,14 +484,14 @@
extraLibs: extraLibs
});
if (defaultValue && value === "") {
editor.moveCursorTo(defaultValue.split("\n").length - 1, 0);
editor.moveCursorTo(defaultValue.split("\n").length +offset, 0);
}
editor.__stateId = stateId;
return editor;
}
this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize"))
this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || [])
this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize"))
this.initEditor = buildEditor('node-input-init-editor', this.id + "/" + "initEditor", false, $("#node-input-initialize").val(), RED._("node-red:function.text.initialize"), undefined, 0);
this.editor = buildEditor('node-input-func-editor', this.id + "/" + "editor", true, $("#node-input-func").val(), undefined, that.libs || [], undefined, -1);
this.finalizeEditor = buildEditor('node-input-finalize-editor', this.id + "/" + "finalizeEditor", false, $("#node-input-finalize").val(), RED._("node-red:function.text.finalize"), undefined, 0);
RED.library.create({
url:"functions", // where to get the data from

View File

@@ -146,13 +146,13 @@
function createTypeValueField(row, defaultType){
return $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"width: 100%;"}).appendTo(row).typedInput({default:defaultType || 'string',types:[
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.png"},
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.png"},
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.png"},
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"buffer",label:RED._("common.type.buffer"),hasValue:false,icon:"red/images/typedInput/bin.png"},
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"json",label:RED._("common.type.jsonString"),hasValue:false,icon:"red/images/typedInput/json.png"},
{value:"string",label:RED._("common.type.string"),hasValue:false,icon:"red/images/typedInput/az.svg"},
{value:"number",label:RED._("common.type.number"),hasValue:false,icon:"red/images/typedInput/09.svg"},
{value:"boolean",label:RED._("common.type.boolean"),hasValue:false,icon:"red/images/typedInput/bool.svg"},
{value:"array",label:RED._("common.type.array"),hasValue:false,icon:"red/images/typedInput/json.svg"},
{value:"buffer",label:RED._("common.type.buffer"),hasValue:false,icon:"red/images/typedInput/bin.svg"},
{value:"object",label:RED._("common.type.object"),hasValue:false,icon:"red/images/typedInput/json.svg"},
{value:"json",label:RED._("common.type.jsonString"),hasValue:false,icon:"red/images/typedInput/json.svg"},
{value:"undefined",label:RED._("common.type.undefined"),hasValue:false},
{value:"null",label:RED._("common.type.null"),hasValue:false}
]});
@@ -247,14 +247,16 @@
var row2 = $('<div/>',{style:"display: flex; padding-top: 5px; padding-left: 175px;"}).appendTo(inputRows);
var row3 = $('<div/>',{style:"display: flex; padding-top: 5px; align-items: center"}).appendTo(inputRows);
var row4 = $('<div/>',{style:"visibility: hidden; height: 0px;"}).appendTo(inputRows);
var textSpan = $("<span/>").appendTo(row4);
var selectField = $('<select/>',{style:"width:120px; text-align: center;"}).appendTo(row);
var group0 = $('<optgroup/>', { label: "value rules" }).appendTo(selectField);
var group0 = $('<optgroup/>', { label: RED._("node-red:switch.label.value-rules") }).appendTo(selectField);
for (var d in operators) {
if(operators[d].kind === 'V') {
group0.append($("<option></option>").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
}
}
var group1 = $('<optgroup/>', { label: "sequence rules" }).appendTo(selectField);
var group1 = $('<optgroup/>', { label: RED._("node-red:switch.label.sequence-rules") }).appendTo(selectField);
for (var d in operators) {
if(operators[d].kind === 'S') {
group1.append($("<option></option>").val(operators[d].v).text(/^switch/.test(operators[d].t)?node._(operators[d].t):operators[d].t));
@@ -340,9 +342,12 @@
row3.hide();
}
var selectedLabel = selectField.find("option:selected").text();
if (selectedLabel.length <= 5) {
textSpan.text(selectedLabel);
var width = textSpan.width();
if (width <= 30) {
selectField.outerWidth(60);
} else if (selectedLabel.length < 12) {
} else if (width <= 85) {
selectField.outerWidth(120);
} else {
selectField.width("auto")

View File

@@ -453,6 +453,7 @@ module.exports = function(RED) {
node.options = {};
node.queue = [];
node.subscriptions = {};
node.clientListeners = []
/** @type {mqtt.MqttClient}*/ this.client;
node.setOptions = function(opts, init) {
if(!opts || typeof opts !== "object") {
@@ -591,7 +592,7 @@ module.exports = function(RED) {
// Only for ws or wss, check if proxy env var for additional configuration
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
// check if proxy is set in env
let prox, noprox;
let prox, noprox, noproxy;
if (process.env.http_proxy) { prox = process.env.http_proxy; }
if (process.env.HTTP_PROXY) { prox = process.env.HTTP_PROXY; }
if (process.env.no_proxy) { noprox = process.env.no_proxy.split(","); }
@@ -718,11 +719,16 @@ module.exports = function(RED) {
setStatusConnecting(node, true);
try {
node.serverProperties = {};
if(node.client) {
//belt and braces to avoid left over clients
node.client.end(true);
node._clientRemoveListeners();
}
node.client = mqtt.connect(node.brokerurl, node.options);
node.client.setMaxListeners(0);
let callbackDone = false; //prevent re-connects causing node.client.on('connect' firing callback multiple times
let callbackDone = false; //prevent re-connects causing node._clientOn('connect' firing callback multiple times
// Register successful connect or reconnect handler
node.client.on('connect', function (connack) {
node._clientOn('connect', function (connack) {
node.closing = false;
node.connecting = false;
node.connected = true;
@@ -754,7 +760,7 @@ module.exports = function(RED) {
}
setStatusConnected(node, true);
// Remove any existing listeners before resubscribing to avoid duplicates in the event of a re-connection
node.client.removeAllListeners('message');
node._clientRemoveListeners('message');
// Re-subscribe to stored topics
for (var s in node.subscriptions) {
@@ -766,7 +772,7 @@ module.exports = function(RED) {
if (node.subscriptions[s].hasOwnProperty(r)) {
qos = Math.max(qos,node.subscriptions[s][r].qos);
_options = node.subscriptions[s][r].options;
node.client.on('message',node.subscriptions[s][r].handler);
node._clientOn('message',node.subscriptions[s][r].handler);
}
}
_options.qos = _options.qos || qos;
@@ -779,11 +785,11 @@ module.exports = function(RED) {
node.publish(node.birthMessage);
}
});
node.client.on("reconnect", function() {
node._clientOn("reconnect", function() {
setStatusConnecting(node, true);
});
//Broker Disconnect - V5 event
node.client.on("disconnect", function(packet) {
node._clientOn("disconnect", function(packet) {
//Emitted after receiving disconnect packet from broker. MQTT 5.0 feature.
const rc = (packet && packet.properties && packet.reasonCode) || packet.reasonCode;
const rs = packet && packet.properties && packet.properties.reasonString || "";
@@ -797,7 +803,7 @@ module.exports = function(RED) {
setStatusDisconnected(node, true);
});
// Register disconnect handlers
node.client.on('close', function () {
node._clientOn('close', function () {
if (node.connected) {
node.connected = false;
node.log(RED._("mqtt.state.disconnected",{broker:(node.clientid?node.clientid+"@":"")+node.brokerurl}));
@@ -809,7 +815,7 @@ module.exports = function(RED) {
// Register connect error handler
// The client's own reconnect logic will take care of errors
node.client.on('error', function (error) {
node._clientOn('error', function (error) {
});
}catch(err) {
console.log(err);
@@ -822,7 +828,7 @@ module.exports = function(RED) {
if(node.connected || node.connecting) {
setStatusDisconnected(node, true);
}
if(node.client) { node.client.removeAllListeners(); }
if(node.client) { node._clientRemoveListeners(); }
node.connecting = false;
node.connected = false;
callback && typeof callback == "function" && callback();
@@ -836,8 +842,12 @@ module.exports = function(RED) {
if(!client) {
resolve();
} else {
const t = setTimeout(reject, ms);
client.end(() => {
const t = setTimeout(() => {
//clean end() has exceeded WAIT_END, lets force end!
client && client.end(true);
reject();
}, ms);
client.end(() => {
clearTimeout(t);
resolve()
});
@@ -894,7 +904,7 @@ module.exports = function(RED) {
};
node.subscriptions[topic][ref] = sub;
if (node.connected) {
node.client.on('message',sub.handler);
node._clientOn('message',sub.handler);
node.client.subscribe(topic, options);
}
};
@@ -905,7 +915,7 @@ module.exports = function(RED) {
if (sub) {
if (sub[ref]) {
if(node.client) {
node.client.removeListener('message',sub[ref].handler);
node._clientRemoveListeners('message',sub[ref].handler);
}
delete sub[ref];
}
@@ -995,13 +1005,40 @@ module.exports = function(RED) {
node.on('close', function(done) {
node.disconnect(function() {
if(node.client) {
node.client.removeAllListeners();
}
done();
});
});
/**
* Add event handlers to the MQTT.js client and track them so that
* we do not remove any handlers that the MQTT client uses internally.
* Use {@link node._clientRemoveListeners `node._clientRemoveListeners`} to remove handlers
* @param {string} event The name of the event
* @param {function} handler The handler for this event
*/
node._clientOn = function(event, handler) {
node.clientListeners.push({event, handler})
node.client.on(event, handler)
}
/**
* Remove event handlers from the MQTT.js client & only the events
* that we attached in {@link node._clientOn `node._clientOn`}.
* * If `event` is omitted, then all events matching `handler` are removed
* * If `handler` is omitted, then all events named `event` are removed
* * If both parameters are omitted, then all events are removed
* @param {string} [event] The name of the event (optional)
* @param {function} [handler] The handler for this event (optional)
*/
node._clientRemoveListeners = function(event, handler) {
node.clientListeners = node.clientListeners.filter((l) => {
if (event && event !== l.event) { return true; }
if (handler && handler !== l.handler) { return true; }
node.client.removeListener(l.event, l.handler)
return false; //found and removed, filter out this one
})
}
}
RED.nodes.registerType("mqtt-broker",MQTTBrokerNode,{

View File

@@ -129,7 +129,7 @@
var headerTypes = [
{value:"content-type",label:"Content-Type",hasValue: false},
{value:"location",label:"Location",hasValue: false},
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.png"}
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.svg"}
]
var contentTypes = [
{value:"application/json",label:"application/json",hasValue: false},
@@ -139,7 +139,7 @@
{value:"text/plain",label:"text/plain",hasValue: false},
{value:"image/gif",label:"image/gif",hasValue: false},
{value:"image/png",label:"image/png",hasValue: false},
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.png"}
{value:"other",label:RED._("node-red:httpin.label.other"),icon:"red/images/typedInput/az.svg"}
];
RED.nodes.registerType('http response',{
@@ -180,7 +180,7 @@
var propertyValue = $('<input/>',{class:"node-input-header-value",type:"text",style:"width: 100%"})
.appendTo(propertyValueCell)
.typedInput({types:
header.h === 'content-type'?contentTypes:[{value:"other",label:"other",icon:"red/images/typedInput/az.png"}]
header.h === 'content-type'?contentTypes:[{value:"other",label:"other",icon:"red/images/typedInput/az.svg"}]
});
var matchedType = headerTypes.filter(function(ht) {
@@ -223,7 +223,7 @@
if (type === 'content-type') {
propertyValue.typedInput('types',contentTypes);
} else {
propertyValue.typedInput('types',[{value:"other",label:"other",icon:"red/images/typedInput/az.png"}]);
propertyValue.typedInput('types',[{value:"other",label:"other",icon:"red/images/typedInput/az.svg"}]);
}
});
},

View File

@@ -127,12 +127,14 @@
{ value: "Cache-Control", label: "Cache-Control", hasValue: false },
{ value: "User-Agent", label: "User-Agent", hasValue: false },
{ value: "Location", label: "Location", hasValue: false },
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
{ value: "other", label: RED._("node-red:httpin.label.other"),
hasValue: true, icon: "red/images/typedInput/az.svg" },
{ value: "msg", label: "msg.", hasValue: true },
]
const headerOptions = {};
const defaultOptions = [
{ value: "other", label: "other", hasValue: true, icon: "red/images/typedInput/az.png" },
{ value: "other", label: RED._("node-red:httpin.label.other"),
hasValue: true, icon: "red/images/typedInput/az.svg" },
{ value: "msg", label: "msg.", hasValue: true },
];
headerOptions["accept"] = [

View File

@@ -435,7 +435,7 @@ module.exports = function(RED) {
});
}
else {
var connectedSockets = [];
const connectedSockets = new Set();
node.status({text:RED._("tcpin.status.connections",{count:0})});
let srv = net;
let connOpts;
@@ -456,16 +456,16 @@ module.exports = function(RED) {
});
socket.on('close',function() {
node.log(RED._("tcpin.status.connection-closed",{host:socket.remoteAddress, port:socket.remotePort}));
connectedSockets.splice(connectedSockets.indexOf(socket),1);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
connectedSockets.delete(socket);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
});
socket.on('error',function() {
node.log(RED._("tcpin.errors.socket-error",{host:socket.remoteAddress, port:socket.remotePort}));
connectedSockets.splice(connectedSockets.indexOf(socket),1);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
connectedSockets.delete(socket);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
});
connectedSockets.push(socket);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.length})});
connectedSockets.add(socket);
node.status({text:RED._("tcpin.status.connections",{count:connectedSockets.size})});
});
node.on("input", function(msg, nodeSend, nodeDone) {
@@ -478,10 +478,10 @@ module.exports = function(RED) {
} else {
buffer = Buffer.from(""+msg.payload);
}
for (var i = 0; i < connectedSockets.length; i += 1) {
if (node.doend === true) { connectedSockets[i].end(buffer); }
else { connectedSockets[i].write(buffer); }
}
connectedSockets.forEach(soc => {
if (node.doend === true) { soc.end(buffer); }
else { soc.write(buffer); }
})
}
nodeDone();
});
@@ -498,12 +498,10 @@ module.exports = function(RED) {
} else {
node.log(RED._("tcpin.status.listening-port",{port:node.port}));
node.on('close', function() {
for (var c in connectedSockets) {
if (connectedSockets.hasOwnProperty(c)) {
connectedSockets[c].end();
connectedSockets[c].unref();
}
}
connectedSockets.forEach(soc => {
soc.end();
soc.unref();
})
server.close();
node.log(RED._("tcpin.status.stopped-listening",{port:node.port}));
});

View File

@@ -247,7 +247,7 @@
var jsonata_or_empty = {
value: "jsonata",
label: "expression",
icon: "red/images/typedInput/expr.png",
icon: "red/images/typedInput/expr.svg",
validate: function(v) {
try{
if(v !== "") {

View File

@@ -198,8 +198,8 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:"filename"},
filenameType: {value:"msg"},
filename: {value:""},
filenameType: {value:"str"},
appendNewline: {value:true},
createDir: {value:false},
overwriteFile: {value:"false"},
@@ -236,8 +236,8 @@
label: node._("file.encoding.setbymsg")
}).text(label).appendTo(encSel);
$("#node-input-filename").typedInput({
default: "msg",
types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"],
default: "str",
types: [{label:RED._("node-red:file.label.path"), value:"str", icon:""}, "msg", "jsonata", "env"],
typeField: $("#node-input-filenameType")
});
if(typeof node.filenameType == 'undefined') {
@@ -297,8 +297,8 @@
category: 'storage',
defaults: {
name: {value:""},
filename: {value:"filename"},
filenameType: {value:"msg"},
filename: {value:""},
filenameType: {value:"str"},
format: {value:"utf8"},
chunk: {value:false},
sendError: {value: false},
@@ -341,8 +341,8 @@
label: label
}).text(label).appendTo(encSel);
$("#node-input-filename").typedInput({
default: "msg",
types:[{ value: "str", label:"", icon:"red/images/typedInput/az.svg"}, "msg", "jsonata", "env"],
default: "str",
types: [{label:RED._("node-red:file.label.path"), value:"str", icon:""}, "msg", "jsonata", "env"],
typeField: $("#node-input-filenameType")
});
if(typeof node.filenameType == 'undefined') {

View File

@@ -54,9 +54,10 @@
<p>If no response is received within the configured timeout, default 30 seconds, the node
will log an error that can be caught using the <code>catch</code> node.</p>
<p>When the option <b>Link Type</b> is set to "Dynamic target" <code>msg.target</code> can be used to call a
<code>link in</code> by name. The target <code>link in</code> node must be named.
<code>link in</code> by name or id.
<ul>
<li>If there are 2 <code>link in</code> nodes with the same name, an error will be raised</li>
<li>If there is a <code>link in</code> nodes with the same id, it will be called</li>
<li>If there are two or more <code>link in</code> nodes with the same name, an error will be raised</li>
<li>A <code>link call</code> cannot call a <code>link in</code> node inside a subflow</li>
</ul>
</p>

214
packages/node_modules/@node-red/nodes/locales/en-US/messages.json vendored Executable file → Normal file
View File

@@ -137,6 +137,7 @@
"toConsole": "system console",
"toStatus": "node status (32 characters)",
"severity": "Level",
"node": "node",
"notification": {
"activated": "Successfully activated: __label__",
"deactivated": "Successfully deactivated: __label__"
@@ -193,22 +194,22 @@
"key": "Private Key",
"passphrase": "Passphrase",
"ca": "CA Certificate",
"verify-server-cert":"Verify server certificate",
"verify-server-cert": "Verify server certificate",
"servername": "Server Name",
"alpnprotocol": "ALPN Protocol"
},
"placeholder": {
"cert":"path to certificate (PEM format)",
"key":"path to private key (PEM format)",
"ca":"path to CA certificate (PEM format)",
"passphrase":"private key passphrase (optional)",
"servername":"for use with SNI",
"alpnprotocol":"for use with ALPN"
"cert": "path to certificate (PEM format)",
"key": "path to private key (PEM format)",
"ca": "path to CA certificate (PEM format)",
"passphrase": "private key passphrase (optional)",
"servername": "for use with SNI",
"alpnprotocol": "for use with ALPN"
},
"error": {
"missing-file": "No certificate/key file provided",
"invalid-cert": "Certificate not specified",
"invalid-key": "Private key not specified"
"invalid-cert": "Certificate not specified",
"invalid-key": "Private key not specified"
}
},
"exec": {
@@ -262,10 +263,10 @@
"moduleLoadError": "Failed to load module __module__: __error__",
"moduleNameError": "Invalid module variable name: __name__",
"moduleNameReserved": "Reserved variable name: __name__",
"inputListener":"Cannot add listener to 'input' event within Function",
"non-message-returned":"Function tried to send a message of type __type__",
"invalid-js": "Error in JavaScript code",
"missing-module": "Module __module__ missing"
"inputListener": "Cannot add listener to 'input' event within Function",
"non-message-returned": "Function tried to send a message of type __type__",
"invalid-js": "Error in JavaScript code",
"missing-module": "Module __module__ missing"
}
},
"template": {
@@ -319,35 +320,35 @@
"limit": "limit",
"limitTopic": "limit topic",
"random": "random",
"rate": "rate",
"random-first": "first random value",
"random-last": "last random value",
"units" : {
"rate": "rate",
"random-first": "first random value",
"random-last": "last random value",
"units": {
"second": {
"plural" : "Seconds",
"plural": "Seconds",
"singular": "Second"
},
"minute": {
"plural" : "Minutes",
"plural": "Minutes",
"singular": "Minute"
},
"hour": {
"plural" : "Hours",
"plural": "Hours",
"singular": "Hour"
},
"day": {
"plural" : "Days",
"plural": "Days",
"singular": "Day"
}
}
},
"errors": {
"too-many" : "too many pending messages in delay node",
"invalid-timeout": "Invalid delay value",
"invalid-rate": "Invalid rate value",
"invalid-rate-unit": "Invalid rate unit value",
"invalid-random-first": "Invalid first random value",
"invalid-random-last": "Invalid last random value"
"too-many": "too many pending messages in delay node",
"invalid-timeout": "Invalid delay value",
"invalid-rate": "Invalid rate value",
"invalid-rate-unit": "Invalid rate unit value",
"invalid-random-first": "Invalid first random value",
"invalid-random-last": "Invalid last random value"
}
},
"trigger": {
@@ -382,11 +383,11 @@
"trigger-block": "trigger & block",
"trigger-loop": "resend every",
"reset": "Reset the trigger if:",
"resetMessage":"msg.reset is set",
"resetPayload":"msg.payload equals",
"resetMessage": "msg.reset is set",
"resetPayload": "msg.payload equals",
"resetprompt": "optional",
"duration": "duration",
"topic": "topic"
"duration": "duration",
"topic": "topic"
}
},
"comment": {
@@ -411,8 +412,8 @@
"cleansession": "Use clean session",
"cleanstart": "Use clean start",
"use-tls": "Use TLS",
"tls-config":"TLS Configuration",
"verify-server-cert":"Verify server certificate",
"tls-config": "TLS Configuration",
"verify-server-cert": "Verify server certificate",
"compatmode": "Use legacy MQTT 3.1 support",
"userProperties": "User Properties",
"subscriptionIdentifier": "Subscription ID",
@@ -447,10 +448,10 @@
"auto-connect": "Connect automatically",
"auto-mode-depreciated": "This option is depreciated. Please use the new auto-detect mode."
},
"sections-label":{
"sections-label": {
"birth-message": "Message sent on connection (birth message)",
"will-message":"Message sent on an unexpected disconnection (will message)",
"close-message":"Message sent before disconnecting (close message)"
"will-message": "Message sent on an unexpected disconnection (will message)",
"close-message": "Message sent before disconnecting (close message)"
},
"tabs-label": {
"connection": "Connection",
@@ -459,7 +460,7 @@
},
"placeholder": {
"clientid": "Leave blank for auto generated",
"clientid-nonclean":"Must be set for non-clean sessions",
"clientid-nonclean": "Must be set for non-clean sessions",
"will-topic": "Leave blank to disable will message",
"birth-topic": "Leave blank to disable birth message",
"close-topic": "Leave blank to disable close message"
@@ -492,8 +493,7 @@
"invalid-action-action": "Invalid action specified",
"invalid-action-alreadyconnected": "Disconnect from broker before connecting",
"invalid-action-badsubscription": "msg.topic is missing or invalid",
"invalid-client-id": "Missing Client ID"
"invalid-client-id": "Missing Client ID"
}
},
"httpin": {
@@ -506,7 +506,7 @@
"status": "Status code",
"headers": "Headers",
"other": "other",
"paytoqs" : {
"paytoqs": {
"ignore": "Ignore",
"query": "Append to query-string parameters",
"body": "Send as request body"
@@ -520,7 +520,7 @@
"setby": "- set by msg.method -",
"basicauth": "Use authentication",
"use-tls": "Enable secure (SSL/TLS) connection",
"tls-config":"TLS Configuration",
"tls-config": "TLS Configuration",
"basic": "basic authentication",
"digest": "digest authentication",
"bearer": "bearer authentication",
@@ -545,8 +545,8 @@
"no-response": "No response object",
"json-error": "JSON parse error",
"no-url": "No url specified",
"deprecated-call":"Deprecated call to __method__",
"invalid-transport":"non-http transport requested",
"deprecated-call": "Deprecated call to __method__",
"invalid-transport": "non-http transport requested",
"timeout-isnan": "Timeout value is not a valid number, ignoring",
"timeout-isnegative": "Timeout value is negative, ignoring",
"invalid-payload": "Invalid payload",
@@ -584,8 +584,8 @@
"send-error": "An error occurred while sending: ",
"missing-conf": "Missing server configuration",
"duplicate-path": "Cannot have two WebSocket listeners on the same path: __path__",
"missing-server": "Missing server configuration",
"missing-client": "Missing client configuration"
"missing-server": "Missing server configuration",
"missing-client": "Missing client configuration"
}
},
"watch": {
@@ -645,7 +645,6 @@
"connection-closed": "connection closed from __host__:__port__",
"connections": "__count__ connection",
"connections_plural": "__count__ connections"
},
"errors": {
"connection-lost": "connection lost to __host__:__port__",
@@ -657,8 +656,8 @@
"connect-timeout": "connect timeout",
"connect-fail": "connect failed",
"bad-string": "failed to convert to string",
"invalid-host": "Invalid host",
"invalid-port": "Invalid port"
"invalid-host": "Invalid host",
"invalid-port": "Invalid port"
}
},
"udp": {
@@ -673,7 +672,7 @@
"toport": "to port",
"address": "Address",
"decode-base64": "Decode Base64 encoded payload?",
"port": "port"
"port": "port"
},
"placeholder": {
"interface": "(optional) local interface or address to bind to",
@@ -721,7 +720,7 @@
"port-invalid": "udp: port number not valid",
"alreadyused": "udp: port __port__ already in use",
"ifnotfound": "udp: interface __iface__ not found",
"invalid-group": "invalid multicast group"
"invalid-group": "invalid multicast group"
}
},
"switch": {
@@ -729,7 +728,9 @@
"label": {
"property": "Property",
"rule": "rule",
"repair": "recreate message sequences"
"repair": "recreate message sequences",
"value-rules": "value rules",
"sequence-rules": "sequence rules"
},
"previous": "previous value",
"and": "and",
@@ -786,8 +787,8 @@
"invalid-json": "Invalid 'to' JSON property",
"invalid-expr": "Invalid JSONata expression: __error__",
"no-override": "Cannot set property of non-object type: __property__",
"invalid-prop": "Invalid property expression: __property__",
"invalid-json-data": "Invalid JSON data: __error__"
"invalid-prop": "Invalid property expression: __property__",
"invalid-json-data": "Invalid JSON data: __error__"
}
},
"range": {
@@ -799,10 +800,10 @@
"from": "from",
"to": "to",
"roundresult": "Round result to the nearest integer?",
"minin": "input from",
"maxin": "input to",
"minout": "target from",
"maxout": "target to"
"minin": "input from",
"maxin": "input to",
"minout": "target from",
"maxout": "target to"
},
"placeholder": {
"min": "e.g. 0",
@@ -899,8 +900,8 @@
"property": "Property",
"actions": {
"toggle": "Convert between JSON String & Object",
"str":"Always convert to JSON String",
"obj":"Always convert to JavaScript Object"
"str": "Always convert to JSON String",
"obj": "Always convert to JavaScript Object"
}
}
},
@@ -927,6 +928,7 @@
"write": "write file",
"read": "read file",
"filename": "Filename",
"path": "path",
"action": "Action",
"addnewline": "Add newline (\\n) to each payload?",
"createdir": "Create directory if it doesn't exist?",
@@ -986,15 +988,15 @@
},
"split": {
"split": "split",
"intro":"Split <code>msg.payload</code> based on type:",
"object":"<b>Object</b>",
"objectSend":"Send a message for each key/value pair",
"strBuff":"<b>String</b> / <b>Buffer</b>",
"array":"<b>Array</b>",
"splitUsing":"Split using",
"splitLength":"Fixed length of",
"stream":"Handle as a stream of messages",
"addname":" Copy key to "
"intro": "Split <code>msg.payload</code> based on type:",
"object": "<b>Object</b>",
"objectSend": "Send a message for each key/value pair",
"strBuff": "<b>String</b> / <b>Buffer</b>",
"array": "<b>Array</b>",
"splitUsing": "Split using",
"splitLength": "Fixed length of",
"stream": "Handle as a stream of messages",
"addname": " Copy key to "
},
"join": {
"join": "join",
@@ -1027,7 +1029,7 @@
"complete": "After a message with the <code>msg.complete</code> property set",
"tip": "This mode assumes this node is either paired with a <i>split</i> node or the received messages will have a properly configured <code>msg.parts</code> property.",
"too-many": "too many pending messages in join node",
"message-prop": "message property",
"message-prop": "message property",
"merge": {
"topics-label": "Merged Topics",
"topics": "topics",
@@ -1046,51 +1048,51 @@
"invalid-type": "Cannot join __error__ to buffer"
}
},
"sort" : {
"sort": {
"sort": "sort",
"target" : "Sort",
"seq" : "message sequence",
"key" : "Key",
"elem" : "element value",
"order" : "Order",
"ascending" : "ascending",
"descending" : "descending",
"as-number" : "as number",
"invalid-exp" : "Invalid JSONata expression in sort node: __message__",
"too-many" : "Too many pending messages in sort node",
"clear" : "clear pending message in sort node"
"target": "Sort",
"seq": "message sequence",
"key": "Key",
"elem": "element value",
"order": "Order",
"ascending": "ascending",
"descending": "descending",
"as-number": "as number",
"invalid-exp": "Invalid JSONata expression in sort node: __message__",
"too-many": "Too many pending messages in sort node",
"clear": "clear pending message in sort node"
},
"batch" : {
"batch": {
"batch": "batch",
"mode": {
"label" : "Mode",
"num-msgs" : "Group by number of messages",
"interval" : "Group by time interval",
"concat" : "Concatenate sequences"
"label": "Mode",
"num-msgs": "Group by number of messages",
"interval": "Group by time interval",
"concat": "Concatenate sequences"
},
"count": {
"label" : "Number of messages",
"overlap" : "Overlap",
"count" : "count",
"invalid" : "Invalid count and overlap"
"label": "Number of messages",
"overlap": "Overlap",
"count": "count",
"invalid": "Invalid count and overlap"
},
"interval": {
"label" : "Interval",
"seconds" : "seconds",
"empty" : "send empty message when no message arrives"
"label": "Interval",
"seconds": "seconds",
"empty": "send empty message when no message arrives"
},
"concat": {
"topics-label": "Topics",
"topic" : "topic"
"topic": "topic"
},
"too-many" : "too many pending messages in batch node",
"unexpected" : "unexpected mode",
"no-parts" : "no parts property in message",
"error": {
"invalid-count": "Invalid count",
"invalid-overlap": "Invalid overlap",
"invalid-interval": "Invalid interval"
}
"too-many": "too many pending messages in batch node",
"unexpected": "unexpected mode",
"no-parts": "no parts property in message",
"error": {
"invalid-count": "Invalid count",
"invalid-overlap": "Invalid overlap",
"invalid-interval": "Invalid interval"
}
},
"rbe": {
"rbe": "filter",
@@ -1100,11 +1102,11 @@
"start": "Start value",
"name": "Name",
"septopics": "Apply mode separately for each ",
"gap": "value change",
"property": "property",
"topic": "topic"
"gap": "value change",
"property": "property",
"topic": "topic"
},
"placeholder":{
"placeholder": {
"bandgap": "e.g. 10 or 5%",
"start": "leave blank to use first data received"
},

View File

@@ -42,9 +42,10 @@
<p>本ノードはメッセージを受信するとメッセージを接続した <code>link in</code>
その後応答を待った後にメッセージを送信します</o>
<p>もし設定したタイムアウト(デフォルト30秒)以内に応答がない場合は<code>catch</code> </p>
<p><b>リンクの種類</b>""<code>link in</code><code>msg.target</code><code>link in</code>
<p><b>リンクの種類</b>""<code>link in</code>ID<code>msg.target</code>
<ul>
<li>もし同じ名前を付けた<code>link in</code>2</li>
<li>同じIDの<code>link in</code></li>
<li>もし同じ名前を付けた<code>link in</code>2</li>
<li><code>link call</code><code>link in</code></li>
</ul>
</p>

View File

@@ -86,10 +86,10 @@
"failed": "inject処理が失敗しました。詳細はログを確認してください。",
"toolong": "時間間隔が大き過ぎます",
"invalid-expr": "JSONata式が不正: __error__",
"invalid-jsonata": "__prop__: プロパティ式が不正: __error__",
"invalid-prop": "__prop__: プロパティ式が不正: __error__",
"invalid-json": "__prop__: JSONデータが不正: __error__",
"invalid-repeat": "繰り返し数が不正"
"invalid-jsonata": "__prop__: プロパティ式が不正: __error__",
"invalid-prop": "__prop__: プロパティ式が不正: __error__",
"invalid-json": "__prop__: JSONデータが不正: __error__",
"invalid-repeat": "繰り返し数が不正"
}
},
"catch": {
@@ -129,6 +129,7 @@
"msgprop": "メッセージプロパティ",
"msgobj": "msgオブジェクト全体",
"autostatus": "デバッグ出力と同じ",
"messageCount": "メッセージ数をカウント",
"to": "出力先",
"debtab": "デバッグタブ",
"tabcon": "デバッグタブとコンソール",
@@ -136,6 +137,7 @@
"toConsole": "システムコンソール",
"toStatus": "ノードステータス(32 文字)",
"severity": "Level",
"node": "ノード",
"notification": {
"activated": "有効化しました: __label__",
"deactivated": "無効化しました: __label__"
@@ -206,8 +208,8 @@
},
"error": {
"missing-file": "証明書と秘密鍵のファイルが設定されていません",
"invalid-cert": "証明書が指定されていません",
"invalid-key": "秘密鍵が指定されていません"
"invalid-cert": "証明書が指定されていません",
"invalid-key": "秘密鍵が指定されていません"
}
},
"exec": {
@@ -263,8 +265,8 @@
"moduleNameReserved": "予約された変数名です: __name__",
"inputListener": "コード内で'input'イベントのリスナを設定できません",
"non-message-returned": "Functionードが __type__ 型のメッセージ送信を試みました",
"invalid-js": "JavaScriptコードのエラー",
"missing-module": "モジュール __module__ が存在しません"
"invalid-js": "JavaScriptコードのエラー",
"missing-module": "モジュール __module__ が存在しません"
}
},
"template": {
@@ -318,9 +320,9 @@
"limit": "limit",
"limitTopic": "limit topic",
"random": "random",
"rate": "流量",
"random-first": "ランダム最小値",
"random-last": "ランダム最大値",
"rate": "流量",
"random-first": "ランダム最小値",
"random-last": "ランダム最大値",
"units": {
"second": {
"plural": "秒",
@@ -342,11 +344,11 @@
},
"errors": {
"too-many": "delayード内で保持しているメッセージが多すぎます",
"invalid-timeout": "遅延時間が不正",
"invalid-rate": "流量値が不正",
"invalid-rate-unit": "流量単位時間が不正",
"invalid-random-first": "ランダム最小値が不正",
"invalid-random-last": "ランダム最大値が不正"
"invalid-timeout": "遅延時間が不正",
"invalid-rate": "流量値が不正",
"invalid-rate-unit": "流量単位時間が不正",
"invalid-random-first": "ランダム最小値が不正",
"invalid-random-last": "ランダム最大値が不正"
}
},
"trigger": {
@@ -384,8 +386,8 @@
"resetMessage": "msg.resetを設定",
"resetPayload": "msg.payloadが次の値",
"resetprompt": "任意",
"duration": "時間間隔",
"topic": "トピック"
"duration": "時間間隔",
"topic": "トピック"
}
},
"comment": {
@@ -443,7 +445,8 @@
"action": "動作",
"staticTopic": "1つのトピックを購読",
"dynamicTopic": "動的な購読",
"auto-connect": "自動接続"
"auto-connect": "自動接続",
"auto-mode-depreciated": "本オプションは非推奨になりました。新しい自動判定モードを使用してください。"
},
"sections-label": {
"birth-message": "接続時の送信メッセージ(Birthメッセージ)",
@@ -490,7 +493,7 @@
"invalid-action-action": "指定された動作が不正です",
"invalid-action-alreadyconnected": "接続する前にブローカから切断してください",
"invalid-action-badsubscription": "msg.topicが存在しないか不正です",
"invalid-client-id": "クライアントIDが未指定"
"invalid-client-id": "クライアントIDが未指定"
}
},
"httpin": {
@@ -581,8 +584,8 @@
"send-error": "送信中にエラーが発生しました: ",
"missing-conf": "サーバ設定が不足しています",
"duplicate-path": "同じパスに対して2つのWebSocketリスナは指定できません: __path__",
"missing-server": "サーバが設定されていません",
"missing-client": "クライアントが設定されていません"
"missing-server": "サーバが設定されていません",
"missing-client": "クライアントが設定されていません"
}
},
"watch": {
@@ -611,7 +614,8 @@
"ms": "ミリ秒",
"chars": "文字",
"close": "終了",
"optional": "(任意)"
"optional": "(任意)",
"reattach": "区切り文字を再追加"
},
"type": {
"listen": "待ち受け",
@@ -652,8 +656,8 @@
"connect-timeout": "接続がタイムアウトしました",
"connect-fail": "接続に失敗しました",
"bad-string": "文字列への変換に失敗しました",
"invalid-host": "ホスト名が不正",
"invalid-port": "ポートが不正"
"invalid-host": "ホスト名が不正",
"invalid-port": "ポートが不正"
}
},
"udp": {
@@ -668,7 +672,7 @@
"toport": "ポート",
"address": "アドレス",
"decode-base64": "Base64形式のペイロードを復号",
"port": "ポート"
"port": "ポート"
},
"placeholder": {
"interface": "(任意) 使用するローカルインターフェイスもしくはアドレス",
@@ -716,7 +720,7 @@
"port-invalid": "udp: ポート番号が不正です",
"alreadyused": "udp: 既に__port__番ポートが使用されています",
"ifnotfound": "udp: インターフェイス __iface__ がありません",
"invalid-group": "マルチキャストグループが不正"
"invalid-group": "マルチキャストグループが不正"
}
},
"switch": {
@@ -724,7 +728,9 @@
"label": {
"property": "プロパティ",
"rule": "条件",
"repair": "メッセージ列の補正"
"repair": "メッセージ列の補正",
"value-rules": "値ルール",
"sequence-rules": "列ルール"
},
"previous": "前回の値",
"and": "",
@@ -732,22 +738,22 @@
"stopfirst": "最初に合致した条件で終了",
"ignorecase": "大文字、小文字を区別しない",
"rules": {
"btwn": "is between",
"cont": "contains",
"regex": "matches regex",
"true": "is true",
"false": "is false",
"null": "is null",
"nnull": "is not null",
"istype": "is of type",
"empty": "is empty",
"nempty": "is not empty",
"head": "head",
"tail": "tail",
"index": "index between",
"btwn": "範囲内である",
"cont": "要素に含む",
"regex": "正規表現にマッチ",
"true": "trueである",
"false": "falseである",
"null": "nullである",
"nnull": "nullでない",
"istype": "指定型である",
"empty": "空である",
"nempty": "空でない",
"head": "先頭要素である",
"tail": "末尾要素である",
"index": "指定添字範囲要素である",
"exp": "JSONata式",
"else": "その他",
"hask": "has key"
"hask": "キーを含む"
},
"errors": {
"invalid-expr": "不正な表現: __error__",
@@ -781,8 +787,8 @@
"invalid-json": "対象の値のJSONプロパティが不正",
"invalid-expr": "JSONata式が不正: __error__",
"no-override": "オブジェクト型でないプロパティを設定できません: __property__",
"invalid-prop": "プロパティ式が不正: __property__",
"invalid-json-data": "JSONデータが不正: __error__"
"invalid-prop": "プロパティ式が不正: __property__",
"invalid-json-data": "JSONデータが不正: __error__"
}
},
"range": {
@@ -794,10 +800,10 @@
"from": "最小値",
"to": "最大値",
"roundresult": "小数値を四捨五入し整数値へ変換",
"minin": "入力最小値",
"maxin": "入力最大値",
"minout": "出力最小値",
"maxout": "出力最大値"
"minin": "入力最小値",
"maxin": "入力最大値",
"minout": "出力最小値",
"maxout": "出力最大値"
},
"placeholder": {
"min": "例) 0",
@@ -1022,8 +1028,8 @@
"complete": "<code>msg.complete</code> プロパティが設定されたメッセージ受信後",
"tip": "このモードでは、本ノードが <i>split</i> ノードと組となるか、 <code>msg.parts</code> プロパティが設定されたメッセージを受け取ることが前提となります。",
"too-many": "joinード内部で保持しているメッセージが多すぎます",
"message-prop": "メッセージプロパティ",
"merge": {
"message-prop": "メッセージプロパティ",
"merge": {
"topics-label": "対象トピック",
"topics": "トピック",
"topic": "トピック",
@@ -1081,11 +1087,11 @@
"too-many": "batchード内で保持しているメッセージが多すぎます",
"unexpected": "想定外のモード",
"no-parts": "メッセージにpartsプロパティがありません",
"error": {
"invalid-count": "メッセージ数が不正",
"invalid-overlap": "オーバラップが不正",
"invalid-interval": "時間間隔が不正"
}
"error": {
"invalid-count": "メッセージ数が不正",
"invalid-overlap": "オーバラップが不正",
"invalid-interval": "時間間隔が不正"
}
},
"rbe": {
"rbe": "filter",
@@ -1095,9 +1101,9 @@
"start": "初期値",
"name": "名前",
"septopics": "個別に動作を適用",
"gap": "変化量",
"property": "プロパティ",
"topic": "トピック"
"gap": "変化量",
"property": "プロパティ",
"topic": "トピック"
},
"placeholder": {
"bandgap": "例:10、5%",

View File

@@ -24,7 +24,7 @@
<dt class="optional">method <span class="property-type">文字列</span></dt>
<dd>ノードの設定で指定していない場合このプロパティでリクエストに用いるHTTPメソッドを設定します<code>GET</code>, <code>PUT</code>, <code>POST</code>, <code>PATCH</code>, <code>DELETE</code></dd>
<dt class="optional">headers <span class="property-type">オブジェクト</span></dt>
<dd>リクエストのHTTPヘッダを指定します</dd>
<dd>リクエストのHTTPヘッダを指定します注釈: <code>msg.headers</code></dd>
<dt class="optional">cookies <span class="property-type">オブジェクト</span></dt>
<dd>設定するとリクエストと共にクッキーを送ることができます</dd>
<dt class="optional">payload</dt>

View File

@@ -15,11 +15,12 @@
-->
<script type="text/html" data-help-name="file">
<p><code>msg.payload</code></p>
<p><code>msg.payload</code></p>
<h3>入力</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>対象ファイル名をノード設定していない場合のプロパティでファイルを指定できます</dd>
<dd>更新するファイルの名前はノード設定やメッセージのプロパティで指定できますデフォルトでは<code>msg.filename</code>使
</dd>
<dt class="optional">encoding <span class="property-type">文字列</span></dt>
<dd>エンコーディングをmsgで設定する構成にした際はこの任意のプロパティでエンコーディングを設定できます</dt>
</dl>
@@ -30,7 +31,7 @@
<p><code>msg.filename</code>使</p>
<p>追記を行う代わりにファイル全体を上書きするように設定することもできます例えば画像のようなバイナリデータをファイルに書き出す場合はこのオプションを指定し改行を追記するオプションを指定しないようにします</p>
<p>ファイル出力の際のエンコーディングはエンコーディングリストから選択できます</p>
<p>の他ファイルの削除を行うことも可能です</p>
<p>の他ファイルの削除を行うことも可能です</p>
</script>
<script type="text/html" data-help-name="file in">
@@ -38,14 +39,15 @@
<h3>入力</h3>
<dl class="message-properties">
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>読み出し対象のファイル名をノードに設定していない場合のプロパティでファイルを指定できます</dd>
<dd>読み込むファイルの名前はノード設定やメッセージのプロパティで指定できますデフォルトでは<code>msg.filename</code>使
</dd>
</dl>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">文字列 | バッファ</span></dt>
<dd>ファイルの内容を文字列もしくはバッファで表現します</dd>
<dd>ファイルの内容を文字列もしくはバッファで表現します</dd>
<dt class="optional">filename <span class="property-type">文字列</span></dt>
<dd>読み出し対象のファイル名をノードに設定していない場合このプロパティでファイルを指定します</dd>
<dd>読み出し対象のファイル名をノードに設定していない場合この任意のプロパティでファイルの名前を指定します</dd>
</dl>
<h3>詳細</h3>
<p>ファイルネームは絶対パスでの指定を推奨します絶対パスを指定しない場合はNode-REDプロセスのワーキングディレクトリからの相対パスとして扱います</p>

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -36,7 +36,7 @@
"js-yaml": "4.1.0",
"media-typer": "1.1.0",
"mqtt": "4.3.7",
"multer": "1.4.4",
"multer": "1.4.5-lts.1",
"mustache": "4.2.0",
"node-watch": "0.7.3",
"on-headers": "1.0.2",

View File

@@ -11,6 +11,7 @@ const exec = require("@node-red/util").exec;
const log = require("@node-red/util").log;
const hooks = require("@node-red/util").hooks;
const url = require("url");
const { createRequire } = require("module");
const BUILTIN_MODULES = require('module').builtinModules;
@@ -139,10 +140,15 @@ function importModule(module) {
}
const externalModuleDir = getInstallDir();
const moduleDir = path.join(externalModuleDir,"node_modules",module);
// To handle both CJS and ESM we need to resolve the module to the
// specific file that is loaded when the module is required/imported
// As this won't be on the natural module search path, we use createRequire
// to access the module
const modulePath = createRequire(moduleDir).resolve(module)
// Import needs the full path to the module's main .js file
// It also needs to be a file:// url for Windows
const moduleFile = url.pathToFileURL(require.resolve(moduleDir));
return import(moduleFile);
const moduleUrl = url.pathToFileURL(modulePath);
return import(moduleUrl);
}
function parseModuleName(module) {

View File

@@ -88,9 +88,10 @@ function getLocalFile(file) {
/**
* Synchronously walks the directory looking for node files.
* @param dir the directory to search
* @param skipValidNodeRedModules a flag to skip lading icons & files if the directory a valid node-red module
* @return an array of fully-qualified paths to .js files
*/
function getLocalNodeFiles(dir) {
function getLocalNodeFiles(dir, skipValidNodeRedModules) {
dir = path.resolve(dir);
var result = [];
@@ -102,6 +103,14 @@ function getLocalNodeFiles(dir) {
return {files: [], icons: []};
}
files.sort();
// when loading local files, if the path is a valid node-red module
// dont include it (will be picked up in scanTreeForNodesModules)
if(skipValidNodeRedModules && files.indexOf("package.json") >= 0) {
const package = getPackageDetails(dir)
if(package.isNodeRedModule) {
return {files: [], icons: []};
}
}
files.forEach(function(fn) {
var stats = fs.statSync(path.join(dir,fn));
if (stats.isFile()) {
@@ -114,7 +123,7 @@ function getLocalNodeFiles(dir) {
} else if (stats.isDirectory()) {
// Ignore /.dirs/, /lib/ /node_modules/
if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
var subDirResults = getLocalNodeFiles(path.join(dir,fn));
var subDirResults = getLocalNodeFiles(path.join(dir,fn), skipValidNodeRedModules);
result = result.concat(subDirResults.files);
icons = icons.concat(subDirResults.icons);
} else if (fn === "icons") {
@@ -126,21 +135,30 @@ function getLocalNodeFiles(dir) {
return {files: result, icons: icons}
}
function scanDirForNodesModules(dir,moduleName) {
var results = [];
var scopeName;
function scanDirForNodesModules(dir,moduleName,package) {
let results = [];
let scopeName;
let files
try {
var files = fs.readdirSync(dir);
if (moduleName) {
var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
if (m) {
scopeName = m[1];
moduleName = m[2];
let isNodeRedModule = false
if(package) {
dir = path.join(package.moduleDir,'..')
files = [path.basename(package.moduleDir)]
moduleName = (package.package ? package.package.name : null) || moduleName
isNodeRedModule = package.isNodeRedModule
} else {
files = fs.readdirSync(dir);
if (moduleName) {
var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
if (m) {
scopeName = m[1];
moduleName = m[2];
}
}
}
for (var i=0;i<files.length;i++) {
var fn = files[i];
if (/^@/.test(fn)) {
for (let i=0;i<files.length;i++) {
let fn = files[i];
if (!isNodeRedModule && /^@/.test(fn)) {
if (scopeName && scopeName === fn) {
// Looking for a specific scope/module
results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
@@ -149,16 +167,18 @@ function scanDirForNodesModules(dir,moduleName) {
results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
}
} else {
if (isIncluded(fn) && !isExcluded(fn) && (!moduleName || fn == moduleName)) {
var pkgfn = path.join(dir,fn,"package.json");
if ((isNodeRedModule || (!moduleName || fn == moduleName)) && (isIncluded(fn) && !isExcluded(fn))) {
try {
var pkg = require(pkgfn);
if (pkg['node-red']) {
if (!registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)) {
log.debug("! Module: "+pkg.name+" "+pkg.version+ " *ignored due to denyList*");
const moduleDir = isNodeRedModule ? package.moduleDir : path.join(dir,fn);
const pkg = package || getPackageDetails(moduleDir)
if(pkg.error) {
throw pkg.error
}
if (pkg.isNodeRedModule) {
if (!pkg.allowed) {
log.debug("! Module: "+pkg.package.name+" "+pkg.package.version+ " *ignored due to denyList*");
} else {
var moduleDir = path.join(dir,fn);
results.push({dir:moduleDir,package:pkg});
results.push({dir:moduleDir,package:pkg.package});
}
}
} catch(err) {
@@ -182,11 +202,14 @@ function scanDirForNodesModules(dir,moduleName) {
* @param moduleName the name of the module to be found
* @return a list of node modules: {dir,package}
*/
function scanTreeForNodesModules(moduleName) {
var dir = settings.coreNodesDir;
var results = [];
var userDir;
function scanTreeForNodesModules(moduleName) {
let coreNodesDir = settings.coreNodesDir;
let results = [];
let userDir;
let nodesDir;
if(settings.nodesDir) {
nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
}
if (settings.userDir) {
packageList = getPackageList();
userDir = path.join(settings.userDir,"node_modules");
@@ -201,15 +224,46 @@ function scanTreeForNodesModules(moduleName) {
});
}
if (dir) {
var up = path.resolve(path.join(dir,".."));
while (up !== dir) {
var pm = path.join(dir,"node_modules");
if (coreNodesDir) {
var up = path.resolve(path.join(coreNodesDir,".."));
while (up !== coreNodesDir) {
var pm = path.join(coreNodesDir,"node_modules");
if (pm != userDir) {
results = results.concat(scanDirForNodesModules(pm,moduleName));
}
dir = up;
up = path.resolve(path.join(dir,".."));
coreNodesDir = up;
up = path.resolve(path.join(coreNodesDir,".."));
}
}
// scan nodesDir for any node-red modules
/*
1. if !exist(package.json) || !package.json.has(node-red) => look for node_modules
2. exist(package.json) && package.json.has(node-red) => load this only
3. in original scan of nodesDir, ignore if:(exist(package.json) && package.json.has(node-red))
*/
if (nodesDir) {
for (let dirIndex = 0; dirIndex < nodesDir.length; dirIndex++) {
const nodeDir = nodesDir[dirIndex];
const packageDetails = getPackageDetails(nodeDir)
if(packageDetails.isNodeRedModule) {
//we have found a node-red module, scan it
const nrModules = scanDirForNodesModules(nodeDir, packageDetails.package.name, packageDetails);
results = results.concat(nrModules);
} else if (packageDetails.has_node_modules) {
//If this dir has a `node_modues` dir, scan it
const nodeModulesDir = path.join(nodeDir, 'node_modules')
const nrModules = scanDirForNodesModules(nodeModulesDir, moduleName );
results = results.concat(nrModules);
} else {
//If this is not a node-red module AND it does NOT have a node_modules dir,
//it may be a directory of project directories or a node_modules dir?
//scan this instead
const nrModules = scanDirForNodesModules(nodeDir, moduleName);
results = results.concat(nrModules);
}
}
}
return results;
@@ -274,24 +328,26 @@ function getModuleNodeFiles(module) {
}
function getNodeFiles(disableNodePathScan) {
var dir;
// Find all of the nodes to load
var nodeFiles = [];
var results;
var dir;
var iconList = [];
let results;
let nodesDir;
if(settings.nodesDir) {
nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
}
let dir;
let nodeFiles = [];
let iconList = [];
if (settings.coreNodesDir) {
results = getLocalNodeFiles(path.resolve(settings.coreNodesDir));
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
var defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
let defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json");
}
if (settings.userDir) {
dir = path.join(settings.userDir,"lib","icons");
var icons = scanIconDir(dir);
let icons = scanIconDir(dir);
if (icons.length > 0) {
iconList.push({path:dir,icons:icons});
}
@@ -301,13 +357,9 @@ function getNodeFiles(disableNodePathScan) {
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
}
if (settings.nodesDir) {
dir = settings.nodesDir;
if (typeof settings.nodesDir == "string") {
dir = [dir];
}
for (var i=0;i<dir.length;i++) {
results = getLocalNodeFiles(dir[i]);
if (nodesDir) {
for (let i = 0; i < nodesDir.length; i++) {
results = getLocalNodeFiles(nodesDir[i], true);
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
}
@@ -479,7 +531,52 @@ function getPackageList() {
}
return list;
}
/**
* Gets the package json object for the supplied `dir`.
* If there is no package.json or the `node-red` section is missing, `result.isNodeRedModule` will be `false`.
* If there is no package.json `isPackage` will be `false`.
* If an error occurs, `result.error` will contain the error.
* @param {string} dir The directory to inspect
*/
function getPackageDetails(dir) {
const result = {
/** @type {string} The package directory */
moduleDir: dir,
/** @type {string} The full file path of package.json for this package */
packageFile: null,
/** @type {boolean} True if this is a valid node-red module */
isNodeRedModule: false,
/** @type {boolean} True if a package.json file is present */
isPackage: false,
/** @type {boolean} True if this a node-red module and passes the checks */
allowed: false,
/** @type {object} The contents of package.json */
package: null,
}
if (!dir) { return result }
try {
const packagefile = path.join(dir,'package.json')
result.has_node_modules = fs.existsSync(path.join(dir,'node_modules'))
if(!fs.existsSync(packagefile)) {
return result
}
result.packageFile = packagefile
const pkg = require(packagefile)
result.package = pkg
if(result.package) {
result.allowed = true
result.isPackage = true
result.isNodeRedModule = typeof result.package['node-red'] === 'object'
if(result.isNodeRedModule) {
result.isNodeRedModule = true;
result.allowed = registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)
}
}
} catch(err) {
result.error = err; // this is not a package we are interested in!
}
return result || result;
}
module.exports = {
init: init,
getNodeFiles: getNodeFiles,

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,11 +16,11 @@
}
],
"dependencies": {
"@node-red/util": "3.0.0-beta.1",
"@node-red/util": "3.0.0-beta.3",
"clone": "2.1.2",
"fs-extra": "10.1.0",
"semver": "7.3.7",
"tar": "6.1.11",
"uglify-js": "3.15.4"
"uglify-js": "3.16.0"
}
}

View File

@@ -165,7 +165,7 @@ function buildDiagnosticReport(scope, callback) {
/** gets a sanitised list containing only the module name */
function listContextModules() {
const keys = Object.keys(runtime.settings.contextStorage);
const keys = Object.keys(runtime.settings.contextStorage || {});
const result = {};
keys.forEach(e => {
result[e] = {

View File

@@ -8,7 +8,6 @@
"httpStatic": "HTTP Static : __path__"
}
},
"server": {
"loading": "Loading palette nodes",
"palette-editor": {
@@ -61,7 +60,6 @@
"function-required": "httpsRefreshInterval requires https property to be a function"
}
},
"api": {
"flows": {
"error-save": "Error saving flows: __message__",
@@ -79,18 +77,16 @@
"error-enable": "Failed to enable node:"
}
},
"comms": {
"error": "Communication channel error: __message__",
"error-server": "Communication server error: __message__",
"error-send": "Communication send error: __message__"
},
"settings": {
"user-not-available": "Cannot save user settings: __message__",
"not-available": "Settings not available",
"property-read-only": "Property '__prop__' is read-only",
"readonly-mode" : "Runtime in read-only mode. Changes will not be saved."
"readonly-mode": "Runtime in read-only mode. Changes will not be saved."
},
"library": {
"unknownLibrary": "Unknown library: __library__",
@@ -101,12 +97,12 @@
},
"nodes": {
"credentials": {
"error":"Error loading credentials: __message__",
"error-saving":"Error saving credentials: __message__",
"error": "Error loading credentials: __message__",
"error-saving": "Error saving credentials: __message__",
"not-registered": "Credential type '__type__' is not registered",
"system-key-warning": "\n\n---------------------------------------------------------------------\nYour flow credentials file is encrypted using a system-generated key.\n\nIf the system-generated key is lost for any reason, your credentials\nfile will not be recoverable, you will have to delete it and re-enter\nyour credentials.\n\nYou should set your own key using the 'credentialSecret' option in\nyour settings file. Node-RED will then re-encrypt your credentials\nfile using your chosen key the next time you deploy a change.\n---------------------------------------------------------------------\n",
"unencrypted" : "Using unencrypted credentials",
"encryptedNotFound" : "Encrypted credentials not found"
"unencrypted": "Using unencrypted credentials",
"encryptedNotFound": "Encrypted credentials not found"
},
"flows": {
"safe-mode": "Flows stopped in safe mode. Deploy to start.",
@@ -150,7 +146,6 @@
}
}
},
"storage": {
"index": {
"forbidden-flow-name": "forbidden flow name"
@@ -180,7 +175,6 @@
}
}
},
"context": {
"log-store-init": "Context store : '__name__' [__info__]",
"error-loading-module": "Error loading context store: __message__",
@@ -195,5 +189,4 @@
"error-write": "Error writing context: __message__"
}
}
}

View File

@@ -100,7 +100,9 @@
"error": "クレデンシャルの読み込みエラー: __message__",
"error-saving": "クレデンシャルの保存エラー: __message__",
"not-registered": "クレデンシャル '__type__' は登録されていません",
"system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n"
"system-key-warning": "\n\n---------------------------------------------------------------------\nフローのクレデンシャルファイルはシステム生成キーで暗号化されています。\n\nシステム生成キーを何らかの理由で失った場合、クレデンシャルファイルを\n復元することはできません。その場合、ファイルを削除してクレデンシャルを\n再入力しなければなりません。\n\n設定ファイル内で 'credentialSecret' オプションを使って独自キーを設定\nします。変更を次にデプロイする際、Node-REDは選択したキーを用いてクレ\nデンシャルを再暗号化します。 \n\n---------------------------------------------------------------------\n",
"unencrypted": "暗号化されていないクレデンシャルを使用",
"encryptedNotFound": "暗号化されたクレデンシャルが存在しません"
},
"flows": {
"safe-mode": "セーフモードでフローを停止しました。開始するためにはデプロイしてください",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/registry": "3.0.0-beta.1",
"@node-red/util": "3.0.0-beta.1",
"@node-red/registry": "3.0.0-beta.3",
"@node-red/util": "3.0.0-beta.3",
"async-mutex": "0.3.2",
"clone": "2.1.2",
"express": "4.18.1",

View File

@@ -641,7 +641,12 @@ function evaluateNodeProperty(value, type, node, msg, callback) {
result = Date.now();
} else if (type === 'bin') {
var data = JSON.parse(value);
result = Buffer.from(data);
if (Array.isArray(data) || (typeof(data) === "string")) {
result = Buffer.from(data);
}
else {
throw createError("INVALID_BUFFER_DATA", "Not string or array");
}
} else if (type === 'msg' && msg) {
try {
result = getMessageProperty(msg,value);

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -16,7 +16,7 @@
],
"dependencies": {
"fs-extra": "10.1.0",
"i18next": "21.6.16",
"i18next": "21.8.10",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.6",
"lodash.clonedeep": "^4.5.0",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.0.0-beta.1",
"version": "3.0.0-beta.3",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -31,10 +31,10 @@
"flow"
],
"dependencies": {
"@node-red/editor-api": "3.0.0-beta.1",
"@node-red/runtime": "3.0.0-beta.1",
"@node-red/util": "3.0.0-beta.1",
"@node-red/nodes": "3.0.0-beta.1",
"@node-red/editor-api": "3.0.0-beta.3",
"@node-red/runtime": "3.0.0-beta.3",
"@node-red/util": "3.0.0-beta.3",
"@node-red/nodes": "3.0.0-beta.3",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"express": "4.18.1",

View File

@@ -249,18 +249,18 @@ module.exports = {
* - externalModules
******************************************************************************/
/** Uncomment the following to run node-red in your preferred language.
* Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko
* Some languages are more complete than others.
*/
// lang: "de",
/** Uncomment the following to run node-red in your preferred language.
* Available languages include: en-US (default), ja, de, zh-CN, zh-TW, ru, ko
* Some languages are more complete than others.
*/
// lang: "de",
/** Configure diagnostics options
* - enabled: When `enabled` is `true` (or unset), diagnostics data will
* be available at http://localhost:1880/diagnostics
* - ui: When `ui` is `true` (or unset), the action `show-system-info` will
* be available to logged in users of node-red editor
*/
*/
diagnostics: {
/** enable or disable diagnostics endpoint. Must be set to `false` to disable */
enabled: true,
@@ -268,74 +268,74 @@ module.exports = {
ui: true,
},
/** Configure the logging output */
logging: {
/** Only console logging is currently supported */
console: {
/** Level of logging to be recorded. Options are:
* fatal - only those errors which make the application unusable should be recorded
* error - record errors which are deemed fatal for a particular request + fatal errors
* warn - record problems which are non fatal + errors + fatal errors
* info - record information about the general running of the application + warn + error + fatal errors
* debug - record information which is more verbose than info + info + warn + error + fatal errors
* trace - record very detailed logging + debug + info + warn + error + fatal errors
* off - turn off all logging (doesn't affect metrics or audit)
*/
level: "info",
/** Whether or not to include metric events in the log output */
metrics: false,
/** Whether or not to include audit events in the log output */
audit: false
}
},
/** Configure the logging output */
logging: {
/** Only console logging is currently supported */
console: {
/** Level of logging to be recorded. Options are:
* fatal - only those errors which make the application unusable should be recorded
* error - record errors which are deemed fatal for a particular request + fatal errors
* warn - record problems which are non fatal + errors + fatal errors
* info - record information about the general running of the application + warn + error + fatal errors
* debug - record information which is more verbose than info + info + warn + error + fatal errors
* trace - record very detailed logging + debug + info + warn + error + fatal errors
* off - turn off all logging (doesn't affect metrics or audit)
*/
level: "info",
/** Whether or not to include metric events in the log output */
metrics: false,
/** Whether or not to include audit events in the log output */
audit: false
}
},
/** Context Storage
* The following property can be used to enable context storage. The configuration
* provided here will enable file-based context that flushes to disk every 30 seconds.
* Refer to the documentation for further options: https://nodered.org/docs/api/context/
*/
//contextStorage: {
// default: {
// module:"localfilesystem"
// },
//},
/** Context Storage
* The following property can be used to enable context storage. The configuration
* provided here will enable file-based context that flushes to disk every 30 seconds.
* Refer to the documentation for further options: https://nodered.org/docs/api/context/
*/
//contextStorage: {
// default: {
// module:"localfilesystem"
// },
//},
/** `global.keys()` returns a list of all properties set in global context.
* This allows them to be displayed in the Context Sidebar within the editor.
* In some circumstances it is not desirable to expose them to the editor. The
* following property can be used to hide any property set in `functionGlobalContext`
* from being list by `global.keys()`.
* By default, the property is set to false to avoid accidental exposure of
* their values. Setting this to true will cause the keys to be listed.
*/
exportGlobalContextKeys: false,
/** `global.keys()` returns a list of all properties set in global context.
* This allows them to be displayed in the Context Sidebar within the editor.
* In some circumstances it is not desirable to expose them to the editor. The
* following property can be used to hide any property set in `functionGlobalContext`
* from being list by `global.keys()`.
* By default, the property is set to false to avoid accidental exposure of
* their values. Setting this to true will cause the keys to be listed.
*/
exportGlobalContextKeys: false,
/** Configure how the runtime will handle external npm modules.
* This covers:
* - whether the editor will allow new node modules to be installed
* - whether nodes, such as the Function node are allowed to have their
* own dynamically configured dependencies.
* The allow/denyList options can be used to limit what modules the runtime
* will install/load. It can use '*' as a wildcard that matches anything.
*/
externalModules: {
// autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */
// autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */
// palette: { /** Configuration for the Palette Manager */
// allowInstall: true, /** Enable the Palette Manager in the editor */
// allowUpdate: true, /** Allow modules to be updated in the Palette Manager */
// allowUpload: true, /** Allow module tgz files to be uploaded and installed */
// allowList: ['*'],
// denyList: [],
// allowUpdateList: ['*'],
// denyUpdateList: []
// },
// modules: { /** Configuration for node-specified modules */
// allowInstall: true,
// allowList: [],
// denyList: []
// }
},
/** Configure how the runtime will handle external npm modules.
* This covers:
* - whether the editor will allow new node modules to be installed
* - whether nodes, such as the Function node are allowed to have their
* own dynamically configured dependencies.
* The allow/denyList options can be used to limit what modules the runtime
* will install/load. It can use '*' as a wildcard that matches anything.
*/
externalModules: {
// autoInstall: false, /** Whether the runtime will attempt to automatically install missing modules */
// autoInstallRetry: 30, /** Interval, in seconds, between reinstall attempts */
// palette: { /** Configuration for the Palette Manager */
// allowInstall: true, /** Enable the Palette Manager in the editor */
// allowUpdate: true, /** Allow modules to be updated in the Palette Manager */
// allowUpload: true, /** Allow module tgz files to be uploaded and installed */
// allowList: ['*'],
// denyList: [],
// allowUpdateList: ['*'],
// denyUpdateList: []
// },
// modules: { /** Configuration for node-specified modules */
// allowInstall: true,
// allowList: [],
// denyList: []
// }
},
/*******************************************************************************

View File

@@ -4,7 +4,7 @@ const path = require("path");
const fs = require("fs-extra");
const should = require("should");
const LATEST = "2";
const LATEST = "3";
function generateScript() {
return new Promise((resolve, reject) => {

View File

@@ -906,6 +906,7 @@ describe('inject node', function() {
msg.should.have.property("str1", "1"); //injected prop
msg.should.have.property("num1", 1); //injected prop
msg.should.have.property("bool1", true); //injected prop
msg.should.have.property("jsonata1", "AB"); //injected prop
helper.clearFlows().then(function() {
done();
@@ -919,6 +920,7 @@ describe('inject node', function() {
{p:"str1", v:"1", vt:"str"}, //new prop
{p:"num1", v:"1", vt:"num"}, //new prop
{p:"bool1", v:"true", vt:"bool"}, //new prop
{p:"jsonata1", v:'"A" & "B"', vt:"jsonata"}, //new prop
]})
.expect(200).end(function(err) {
if (err) {

View File

@@ -14,26 +14,41 @@
* limitations under the License.
**/
var should = require("should");
var sinon = require("sinon");
var path = require("path");
const should = require("should");
const sinon = require("sinon");
const path = require("path");
var NR_TEST_UTILS = require("nr-test-utils");
const NR_TEST_UTILS = require("nr-test-utils");
var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem");
const localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem");
var resourcesDir = path.resolve(path.join(__dirname,"resources","local"));
var userDir = path.resolve(path.join(__dirname,"resources","userDir"));
var moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule"));
const resourcesDir = path.resolve(path.join(__dirname,"resources","local"));
const userDir = path.resolve(path.join(__dirname,"resources","userDir"));
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
const nodesDir1 = path.resolve(path.join(__dirname,"resources","nodesDir1"))
const nodesDir2 = path.resolve(path.join(__dirname,"resources","nodesDir2"))
const nodesDir3 =path.resolve(path.join(__dirname,"resources","nodesDir3"))
const moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule"));
const i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
describe("red/nodes/registry/localfilesystem",function() {
var stubs = [];
function stubPathJoin() {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
}
beforeEach(function() {
stubs.push(sinon.stub(i18n,"registerMessageCatalog").callsFake(function() { return Promise.resolve(); }));
})
var stubs = [];
afterEach(function() {
while(stubs.length) {
stubs.pop().restore();
@@ -129,16 +144,76 @@ describe("red/nodes/registry/localfilesystem",function() {
checkNodes(nm.nodes,['TestNode5'],['TestNode1']);
done();
});
it("Finds nodes and icons only in nodesDir with files, icons and valid node-red packages",function(done) {
localfilesystem.init({nodesDir:nodesDir1});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes");
nm.should.have.a.property("icons");
checkNodes(nm.nodes,['loose1', 'loose2'], []);
//1 icon in nodesDir1/icons/ - should be found
//2 icons in nodesDir1/loose2/icons/ - should be found
//1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found
//1 icons in nodesDir1/regular_module/icons/ - should NOT be found
//total icon sets 3, total icons 4
nm.icons.should.have.a.property("length", 3);
nm.icons[0].should.have.a.property("path")
nm.icons[0].should.have.a.property("icons", ['loose1.svg'])
nm.icons[1].should.have.a.property("path")
nm.icons[1].should.have.a.property("icons", ['loose2.svg', 'loose2b.svg'])
nm.icons[2].should.have.a.property("path")
nm.icons[2].should.have.a.property("icons", ['test.svg'])
done();
});
it("Should not find node-red node in nodesDir with files, icons and valid node-red packages",function(done) {
// path contains a regular node module and a node-red node module
localfilesystem.init({nodesDir:path.join(nodesDir1)});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes");
nm.nodes.should.have.a.property("loose1");
nm.nodes.should.have.a.property("loose2");
nm.nodes.should.not.have.a.property("regular_module");
nm.nodes.should.not.have.a.property("node-red-node-testnode");
for (let key of Object.keys(nm.nodes)) {
const n = nm.nodes[key];
n.file.indexOf("regular_module").should.eql(-1, `found icons in a node-red module`)
n.file.indexOf("node-red-node-testnode").should.eql(-1, `found icons in a node-red module`)
}
//1 icon in nodesDir1/icons/ - should be found
//2 icons in nodesDir1/loose2/icons/ - should be found
//1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found
//1 icons in nodesDir1/regular_module/icons/ - should NOT be found
//total icon sets 3, total icons 4
nm.should.have.a.property("icons");
nm.icons.should.have.a.property("length", 3);
let iconCount = 0;
for (let index = 0; index < nm.icons.length; index++) {
const iconDir = nm.icons[index];
iconCount += iconDir.icons.length
iconDir.path.indexOf("node-red-node-testnode").should.eql(-1, `should not find icons in a node-red module`)
}
should(iconCount).eql(4, "Should find only 4 icons")
done();
});
it("Should not find node-red node in nodesDir when regular package and valid node-red packages",function(done) {
localfilesystem.init({nodesDir:path.join(nodesDir1,"regular_module")});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes", {});
nm.should.have.a.property("icons");
nm.icons.should.have.a.property("length", 1); //should find 1 icons folder
nm.icons[0].should.have.a.property("icons", [ 'test.svg' ]); //should find 1 icon in regular package
done();
});
it("Finds nodes module path",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
var nodeList = localfilesystem.getNodeFiles();
nodeList.should.have.a.property("node-red");
@@ -166,8 +241,6 @@ describe("red/nodes/registry/localfilesystem",function() {
i18n.registerMessageCatalog.lastCall.args[1].should.eql(path.resolve(path.join(moduleDir,"locales")));
i18n.registerMessageCatalog.lastCall.args[2].should.eql('messages.json');
done();
});
it.skip("finds locales directory");
@@ -205,15 +278,7 @@ describe("red/nodes/registry/localfilesystem",function() {
});
describe("#getModuleFiles",function() {
it("gets a nodes module files",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
var nodeModule = localfilesystem.getModuleFiles('TestNodeModule');
nodeModule.should.have.a.property('TestNodeModule');
@@ -230,16 +295,87 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Finds only 1 node-red node in nodesDir amongst legacy nodes and regular nodes",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 1)
loaded.indexOf('node-red-node-testnode').should.greaterThan(-1, "Should load node-red-node-testnode")
nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode');
nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0');
nodeModule['node-red-node-testnode'].should.have.a.property('nodes');
nodeModule['node-red-node-testnode'].should.have.a.property('path');
nodeModule['node-red-node-testnode'].should.have.a.property('user', false);
checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode');
done();
});
it("Finds a node-red node in nodesDir with a sub dir containing valid node-red package",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode');
nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0');
nodeModule['node-red-node-testnode'].should.have.a.property('nodes');
nodeModule['node-red-node-testnode'].should.have.a.property('path');
nodeModule['node-red-node-testnode'].should.have.a.property('user', false);
checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode');
done();
});
it("Finds 2 node-red modules and 1 plugin in nodesDir (in root of dir)",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[nodesDir2]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 3)
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
loaded.indexOf('testnode2').should.greaterThan(-1, "Should load testnode2")
loaded.indexOf('test-theme2').should.greaterThan(-1, "Should load test-theme2")
nodeModule['@test/testnode'].should.have.a.property('name','@test/testnode');
nodeModule['@test/testnode'].should.have.a.property('version','1.0.0');
nodeModule['@test/testnode'].should.have.a.property('nodes');
nodeModule['@test/testnode'].should.have.a.property('path');
nodeModule['@test/testnode'].should.have.a.property('user', false);
nodeModule['testnode2'].should.have.a.property('name','testnode2');
nodeModule['testnode2'].should.have.a.property('version','1.0.0');
nodeModule['testnode2'].should.have.a.property('nodes');
nodeModule['testnode2'].should.have.a.property('path');
nodeModule['testnode2'].should.have.a.property('user', false);
nodeModule['test-theme2'].should.have.a.property('name','test-theme2');
nodeModule['test-theme2'].should.have.a.property('version','0.0.1');
nodeModule['test-theme2'].should.have.a.property('nodes', {});
nodeModule['test-theme2'].should.have.a.property('path');
nodeModule['test-theme2'].should.have.a.property('user', false);
nodeModule['test-theme2'].should.have.a.property('plugins');
nodeModule['test-theme2'].plugins.should.have.a.property('test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('name','test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('module','test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('version', '0.0.1');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('file');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('local', false);
done();
});
it("Finds 2 node-red modules and 1 plugin in nodesDir pointing to a node_modules dir",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir3, "node_modules")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 3)
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
loaded.indexOf('@test/test-theme3').should.greaterThan(-1, "Should load test-theme3")
loaded.indexOf('testnode3').should.greaterThan(-1, "Should load testnode3")
done();
});
it("throws an error if a node isn't found",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
/*jshint immed: false */
(function(){
@@ -250,15 +386,7 @@ describe("red/nodes/registry/localfilesystem",function() {
it.skip("finds locales directory");
it.skip("finds icon path directory");
it("scans icon files with a module file",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({
coreNodesDir: moduleDir
});

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M10.004 14.499h20M10.004 46.503h20M10.004 22.5h20M10.004 30.501h20M10.004 38.502h20" stroke="#fff" stroke-width="2.9997000000000003"/></svg>

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose1.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from loose1.js")
})()

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M7 38.98v3.983h11v12l13-23H19l-.463.017c-1.28 4.048-5.066 6.983-9.537 6.983zm12-11.017h12l-13-23v12H7V20.9l2 .064c4.467 0 8.25 2.93 9.534 6.972zM6.95 24.22a6 6 0 1 1-.083 11.456" fill="#fff" style="isolation:auto;mix-blend-mode:normal"/></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose2.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from loose2.js")
})()

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@@ -0,0 +1,19 @@
{
"name": "node-red-node-testnode",
"version": "1.0.0",
"description": "A node-red node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@@ -0,0 +1,14 @@
{
"name": "regular_node",
"version": "1.0.0",
"description": "A regular node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"test"
],
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from @test/testnode index.html")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from @test/testnode index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "@test/testnode",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from testnode2 index.js")
})()
</script>

View File

@@ -0,0 +1,4 @@
(function() {
console.log("hello from testnode2 index.js")
})()

View File

@@ -0,0 +1,20 @@
{
"name": "testnode2",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode2": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@@ -0,0 +1,3 @@
(function() {
console.log("Hi from test plugin client side")
})()

View File

@@ -0,0 +1,14 @@
module.exports = function (RED) {
RED.plugins.registerPlugin('test-theme', {
type: 'node-red-theme',
scripts: [
'files/clientside.js'
],
css: [
'files/theme.css',
],
monacoOptions: {
theme: "vs"
}
})
}

View File

@@ -0,0 +1 @@
:root{--red-ui-primary-background: #f2f3fb;}

View File

@@ -0,0 +1,24 @@
{
"name": "test-theme2",
"version": "0.0.1",
"description": "test theme for Node-RED",
"keywords": [
"node-red",
"plugin",
"theme"
],
"author": {
"name": "testy-McTesterson"
},
"license": "MIT",
"node-red": {
"version": ">=2.2.0",
"plugins": {
"test-theme2": "files/plugin.js"
}
},
"engines": {
"node": ">=12.x"
}
}

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