Compare commits

..

237 Commits

Author SHA1 Message Date
Nick O'Leary
fb0f12bb20 Bump to 0.20.0-beta.5 2019-02-08 10:41:22 +00:00
Nick O'Leary
e94b8d3e84 Update changelog 2019-02-08 10:41:01 +00:00
Nick O'Leary
8c00e1fdf4 Bump dependencies 2019-02-08 10:35:06 +00:00
Nick O'Leary
a31fa82284 Merge pull request #2056 from node-red-hitachi/update-logic-nodes-info-jp
Update Japanese info text of logic nodes
2019-02-08 09:38:17 +00:00
Nick O'Leary
5d0af45d8f Merge pull request #2055 from node-red-hitachi/update-io-nodes-info-jp
Update Japanese info text of io nodes
2019-02-08 09:38:04 +00:00
Hiroyasu Nishiyama
e9f248020e update Japanese info text of split node 2019-02-08 09:46:35 +09:00
Hiroyasu Nishiyama
a8e1058af6 fix typos in Japanese info text of range node 2019-02-08 09:35:02 +09:00
Hiroyasu Nishiyama
1a087fd799 update info text of switch node 2019-02-08 09:32:07 +09:00
Hiroyasu Nishiyama
50c81533e0 fix header level of switch node info text 2019-02-08 09:26:11 +09:00
Hiroyasu Nishiyama
5eab9aa4b1 fix typos in tcpin node info text 2019-02-08 09:14:32 +09:00
Hiroyasu Nishiyama
1970cbfe37 fix mismatched p-tag 2019-02-08 09:05:29 +09:00
Hiroyasu Nishiyama
6d736201f9 fix unmatched p-tag 2019-02-08 08:58:59 +09:00
Nick O'Leary
51ec52b573 Merge pull request #2053 from node-red-hitachi/update-node-messages-jp
Update Japanese message catalogue of core nodes
2019-02-07 22:00:55 +00:00
Nick O'Leary
d099387186 Merge pull request #2054 from node-red-hitachi/update-core-nodes-info-jp
Update Japanese info text of core nodes
2019-02-07 21:59:57 +00:00
Hiroyasu Nishiyama
3f91e4da66 update Japanese info text of template node 2019-02-07 23:35:09 +09:00
Hiroyasu Nishiyama
4124159378 update Japanese info text of function node 2019-02-07 23:24:12 +09:00
Hiroyasu Nishiyama
18f3789e29 update Japanese info text of catch node 2019-02-07 23:15:06 +09:00
Hiroyasu Nishiyama
7828af591e update Japanese message catalogue of core nodes 2019-02-07 20:03:16 +09:00
Nick O'Leary
d432dba726 Merge pull request #2052 from node-red-hitachi/update-editor-messages-jp
Update Japanese editor message catalogue
2019-02-07 09:30:13 +00:00
Nick O'Leary
72ae87857f Delete package-lock.json 2019-02-07 09:29:38 +00:00
Nick O'Leary
724acff591 Properly sanitize node names in deploy warning dialogs 2019-02-07 09:11:06 +00:00
Hiroyasu Nishiyama
482b432e2c update Japanese message catalogue for JSONata 2019-02-07 12:49:36 +09:00
Hiroyasu Nishiyama
351c0cb0a8 add missing colon 2019-02-07 12:33:36 +09:00
Hiroyasu Nishiyama
314a0fb5d6 update Japanese message catalog 2019-02-07 12:28:59 +09:00
Nick O'Leary
a301bf8bf5 Fix XSS issues in library ui code 2019-02-06 22:25:25 +00:00
Nick O'Leary
37b3601c47 Link Node - scroll to current flow in node list 2019-02-06 15:38:35 +00:00
Nick O'Leary
6e944485f0 Merge pull request #2030 from node-red-hitachi/scope-parent
Allow access of scope parent
2019-02-06 14:10:57 +00:00
Nick O'Leary
431266069e Merge pull request #2050 from node-red/subflow-props
Display parent subflow properties in subflow instance edit dialog
2019-02-06 14:06:22 +00:00
Nick O'Leary
d48a09e68b Add env type to subflow env var types
Also remove date and regex types
2019-02-06 13:58:31 +00:00
Nick O'Leary
2a8f0a4eab Display parent subflow properties in edit dialog 2019-02-05 23:08:39 +00:00
Nick O'Leary
79f3669fac Add 'catch uncaught only' mode to Catch node
Closes #1747

This was inspired by a PR from @mauriciom75 but implemented in a different way
due to some of the internal reworking done to Flow and Subflow in the dev branch
2019-02-05 14:29:50 +00:00
Nick O'Leary
aab0f2dcd5 Merge pull request #2047 from node-red-hitachi/fix-use-common-i18n-label
Fix use of i18n label
2019-02-05 08:31:38 +00:00
Nick O'Leary
a47831e278 Merge pull request #2049 from kazuhitoyokoi/dev-fixbug4outoputinsubflow
Fix direction value of subflow output
2019-02-05 08:31:04 +00:00
Kazuhito Yokoi
f1a5e8a42c Fix direction value of subflow output 2019-02-05 16:27:02 +09:00
Hiroyasu Nishiyama
723e9b3cba make $parent access without key return undefined 2019-02-05 14:47:30 +09:00
Hiroyasu Nishiyama
ff759a8074 use common i18 label for variable name placeholder 2019-02-05 13:12:21 +09:00
Nick O'Leary
4de1056d82 Tidy up HTTP Request payload to GET params work 2019-02-04 21:30:11 +00:00
Nick O'Leary
884b8da8bf Merge pull request #1981 from jonferreira/dev
Use payload properties as parameters on a GET request
2019-02-04 20:43:45 +00:00
Nick O'Leary
044ad77a4b Merge pull request #2044 from node-red-hitachi/cookie_encoding
Allow http request node to avoid encoding cookie
2019-02-04 20:39:05 +00:00
Nick O'Leary
1fe8b388a3 Allow subflow env-var list to resize with the dialog 2019-02-04 17:20:31 +00:00
Dave Conway-Jones
79fe7d684c Add parsed JSON output option to MQTT subscribe node 2019-02-04 16:35:42 +00:00
Dave Conway-Jones
c409af0ea8 Add local time display option to numerics in debug window 2019-02-04 15:51:42 +00:00
Nick O'Leary
5110eaff96 Merge branch 'dev' into pr_2042 2019-02-04 14:39:00 +00:00
Nick O'Leary
db3eee72b5 Do not convert falsey env vars to blank string
Only blank out undefined as that's what we've always done
2019-02-04 14:12:34 +00:00
Nick O'Leary
3bcff91328 Add Status Node to Subflow to allow subflow-specific status
Closes #597
2019-02-01 23:44:50 +00:00
Hiroyasu Nishiyama
e843f192ec convert subflow env vars to dict 2019-02-02 08:34:33 +09:00
Hiroki Uchikawa
f3d2053878 Make the encode option a boolean value to determine whether to encode 2019-02-01 17:15:07 +09:00
Nick O'Leary
efe8fbbd11 Better handling of multiple flow merges
Fixes #2039

Keeps better track of what was merged so a subsequent merge
properly identifies new-vs-old and doesn't remove thinks by mistake
2019-01-30 15:12:01 +00:00
Hiroyasu Nishiyama
ce507b3b52 simplified meta-data 2019-01-30 20:57:51 +09:00
Nick O'Leary
85de227003 Make Node._flow a writeable property
This is needed so an existing node constructor that does:

   Object.assign(this,config);

works when it tries to replace this._flow with config._flow.
2019-01-30 10:50:29 +00:00
Hiroki Uchikawa
7c6eb7c794 Allow http request node to change cookie value encoding 2019-01-30 19:33:23 +09:00
Hiroki Uchikawa
2037741b54 Revert cookie encoding behavior 2019-01-30 19:24:19 +09:00
Nick O'Leary
d534a8952d Do not propagate Flow.getNode to parent when called from outside flow 2019-01-29 21:49:20 +00:00
Hiroyasu Nishiyama
0b05b883cb add test cases 2019-01-30 00:04:41 +09:00
Hiroyasu Nishiyama
6937aa5ddd fix type of env values 2019-01-29 23:46:56 +09:00
Hiroyasu Nishiyama
8f6b24e0aa fixed to access last variable with same name 2019-01-29 21:46:50 +09:00
Hiroyasu Nishiyama
ba3b64a6c6 removed useless env setup & simplified env access in function node 2019-01-29 21:39:59 +09:00
Nick O'Leary
d23b32a830 Bump to 0.20.0-beta.4 2019-01-28 15:29:01 +00:00
Nick O'Leary
ceba08a801 Update dependencies and tidy up sentiment 2019-01-28 15:27:40 +00:00
Nick O'Leary
e0bb03a53f More api documentation updates 2019-01-28 14:40:42 +00:00
Hiroyasu Nishiyama
0881c6a20b update test cases 2019-01-28 23:14:49 +09:00
Hiroyasu Nishiyama
f88a4b1791 fixed comments from @knolleary 2019-01-28 22:14:08 +09:00
Hiroyasu Nishiyama
2b43e3ee23 add placeholder for env var name 2019-01-27 21:56:13 +09:00
Nick O'Leary
2e063f91bc Merge pull request #2041 from kazntree/default-chromedriver
Remove chromedriver from devDependencies
2019-01-26 21:48:25 +00:00
Nick O'Leary
79062e2034 Move nodes to top-left corner when converting to subflow 2019-01-26 20:49:22 +00:00
Hiroyasu Nishiyama
a413f3cded Add support of subflow env var 2019-01-26 23:15:20 +09:00
Nick O'Leary
4baaaa8d59 Propagate Status/Error events from global config nodes 2019-01-25 15:46:39 +00:00
Nick O'Leary
c99b35428b Ensure status/error events are propagated to parent properly 2019-01-25 13:35:02 +00:00
Nick O'Leary
3c8e4f8bbf Merge branch 'pr_2033' into dev 2019-01-23 16:29:14 +00:00
Nick O'Leary
aa9a37da38 Add placeholder node when in quick-add mode 2019-01-23 16:27:13 +00:00
Nick O'Leary
85efb48c1f Merge branch 'dev' into pr_2033 2019-01-22 16:13:26 +00:00
Nick O'Leary
888e7ee023 Merge pull request #2038 from kazuhitoyokoi/dev-fixbuginwebsocketnode
Fix bug in WebSocket configuration node
2019-01-22 16:13:06 +00:00
Kazuhito Yokoi
d7bbf8a8da Fix bug in websocket node 2019-01-22 12:03:30 +09:00
Dave Conway-Jones
e2ee88de84 offset menu so you can see node placement 2019-01-21 22:08:25 +00:00
Dave Conway-Jones
1d1ab5b7b2 don't pin new nodes to grid if not using grid 2019-01-21 16:15:38 +00:00
Nick O'Leary
54c863d48f Make Node._flow non-enumerable to avoid circular refs 2019-01-21 14:19:19 +00:00
Nick O'Leary
acc633b4b6 Don't collapse version control header when clicking refresh 2019-01-21 10:06:02 +00:00
Dave Conway-Jones
766ccf85c2 add fast entry via keyboard for string of nodes 2019-01-20 14:43:17 +00:00
kazntree
7ab5a2be47 remove chromedriver package, and instruct how to install it when running grunt test-ui 2019-01-20 19:10:54 +09:00
Nick O'Leary
7a6e1fe566 Check for undeployed change before showing open project dialog 2019-01-18 21:42:13 +00:00
Nick O'Leary
4749c92252 Add View Tools 2019-01-18 21:19:03 +00:00
Nick O'Leary
0e035e47df Avoid duplicate links when missing node type installed
Fixes #2032
2019-01-17 22:35:58 +00:00
Nick O'Leary
1359545e13 Allow debug edit expression to be sent to status 2019-01-17 17:15:53 +00:00
Nick O'Leary
5b2f24f842 Handle i18n properly when key is a valid sub-identifier
Fixes #2028
The i18n library will, be default, return a string containing
an error message about the key resolving to an object. We cannot
distinguish that string from others to handle ourselves.

The `returnObjectTrees` option will cause it to return the object
rather than error. We can then test for that and return the original
key if the object is returned - which is the desired result.
2019-01-17 14:44:41 +00:00
Nick O'Leary
bb73e30909 Merge pull request #2016 from jwende/dev
german translation v01
2019-01-17 14:25:15 +00:00
Nick O'Leary
490903ca25 Tidy up when usage in Flow and Node 2019-01-17 13:18:26 +00:00
Nick O'Leary
901b32297e Restore RED.auth to node-red module api 2019-01-16 23:41:44 +00:00
Nick O'Leary
dd72046922 Add some comments to Flow and Subflow classes 2019-01-16 23:33:04 +00:00
Nick O'Leary
6286b34d00 Add Flow.getSetting for resolving env-var properties
This lays the groundwork for subflow-specific settings
2019-01-16 22:38:04 +00:00
Nick O'Leary
81f4e0de56 Refactor Subflow logic into own class 2019-01-16 16:27:19 +00:00
Hiroyasu Nishiyama
596fbfb517 allow $parent access of flow context 2019-01-16 23:10:03 +09:00
Nick O'Leary
da756fa568 WIP: Start refactor of nodes/Flow.js 2019-01-11 14:53:21 +00:00
Nick O'Leary
30aebc4ee3 Bump JSONata in util package 2019-01-11 10:08:09 +00:00
Nick O'Leary
45138ce5ca Bump JSONata to 1.6.4:wq 2019-01-11 09:54:56 +00:00
Nick O'Leary
f26b9feeaf Change default dropdown appearance and sidebar tab menu handling 2019-01-10 23:30:51 +00:00
Nick O'Leary
9e47d933af Handle multiple-select box when nothing selected
Fixes #2021
2019-01-10 15:21:27 +00:00
Nick O'Leary
f309a9d537 Bump version to 0.20.0-beta.3 2019-01-10 13:34:47 +00:00
Nick O'Leary
a786b37cb9 Make ssh key dialog accessible when opened from new proj dialog 2019-01-10 13:28:40 +00:00
Nick O'Leary
6a519a30a2 Update changelog 2019-01-09 17:04:33 +00:00
Nick O'Leary
81ae552e69 Project ui code using incorrect error property 2019-01-09 17:03:17 +00:00
Nick O'Leary
0ec04a3624 Allow notifications to be reused in place rather than stack
For example, clipboard actions now reuse the same notification.
Similarly the Inject node will reuse its notification when
injecting.
2019-01-09 14:02:46 +00:00
Nick O'Leary
81d5b47fce Update ws dependency in sub-modules 2019-01-08 16:31:18 +00:00
Nick O'Leary
ed31a0cf15 Update to WS 6.x and fix all it broke
Significant update to the ws module to get it completely up to date.

The jump from 1.x to 6.x has required a rewrite of our WS handling. Most
specifically the means by which you can have multiple ws servers on a
single http server has completely changed; we now have to handle the
'upgrade' event on the server ourselves.
2019-01-08 16:21:36 +00:00
Nick O'Leary
201d1926bc Bump dependencies 2019-01-08 10:32:23 +00:00
Nick O'Leary
9ee6655bfa Bump jsonata in submodule package.json 2019-01-07 17:03:32 +00:00
Nick O'Leary
c4beab6b0d Bump JSONata to 1.6.4
Fixes #2023
2019-01-07 16:59:38 +00:00
Nick O'Leary
34b6643913 Remove unused variable declaration in Change node 2019-01-07 15:00:32 +00:00
Nick O'Leary
98e391b867 Add audit logging to admin api 2019-01-07 14:59:48 +00:00
Nick O'Leary
19eb8e9a6d Update palette manager properly when module updated 2019-01-07 14:54:35 +00:00
Nick O'Leary
43b7aa40c3 Remove promises from Join node 2019-01-02 22:37:06 +00:00
Nick O'Leary
747af44fc1 Tidy up variable naming in split.js 2019-01-01 23:05:13 +00:00
Joerg Wende
a73381e24b german translation v01 2018-12-22 09:22:25 +01:00
Nick O'Leary
d5ef428edd Remove promises from Change node 2018-12-21 14:37:04 +00:00
Nick O'Leary
5fa4d227b8 Merge pull request #2010 from node-red-hitachi/fix-require
Fix RED.require
2018-12-21 10:38:35 +00:00
Hiroyasu Nishiyama
cc7e3b0c26 fix failure of RED.require 2018-12-21 14:39:51 +09:00
Nick O'Leary
473a2ae275 Remove all Promises from Switch node
Promises are expensive and should not be used in the main
message handling path. The Switch node used them a lot if
the node references context - with a lot of duplicate code
to handle async and sync code paths.

This change modifies the code to use callbacks throughout
that are just as performant in either case.
2018-12-20 22:57:47 +00:00
Nick O'Leary
7f5d47f39d Update Link node UI to use TreeList 2018-12-20 13:15:42 +00:00
Nick O'Leary
6031f146aa Add TreeList common widget 2018-12-20 13:15:31 +00:00
Nick O'Leary
020a469f3b Fix visual jump when opening Comment editor on Safari
Part of #2008
2018-12-19 10:05:09 +00:00
Nick O'Leary
091de3aa66 Fix vertical align of markdown editor in Safari
Fixes #2008
2018-12-19 10:04:36 +00:00
Nick O'Leary
b837f7608c Avoid marking node as changed if label state is default
Fixes #2009
2018-12-19 09:30:20 +00:00
Nick O'Leary
afe9367bac Merge pull request #2005 from kazuhitoyokoi/dev-updatemessagecatalog
Update message catalog
2018-12-18 22:58:19 +00:00
Nick O'Leary
9bd9023cb6 Merge pull request #2007 from node-red-hitachi/dev-uitest-mac
Update UI test for mac OS
2018-12-18 22:57:48 +00:00
Nick O'Leary
8502cf8498 Highlight port on node hover while joining 2018-12-18 21:45:33 +00:00
Nick O'Leary
33dade0584 Support drag-wiring of link nodes 2018-12-18 10:57:53 +00:00
Nick O'Leary
84cc2ad0fa Allow TypeSearch to include a filter option 2018-12-18 10:57:33 +00:00
Nick O'Leary
dc2d3bc7c0 Improve diff colouring 2018-12-18 10:57:18 +00:00
Nick O'Leary
64df557423 Allow sections to toggle in 2-element stack 2018-12-18 10:56:54 +00:00
Yuma Matsuura
715cc77e76 Update UI test for mac os 2018-12-18 09:29:46 +09:00
Kazuhito Yokoi
b80d1af3d7 Fix typo 2018-12-17 19:01:33 +09:00
Kazuhito Yokoi
f05f534fd2 Update message catalogue 2018-12-17 18:48:19 +09:00
Nick O'Leary
c0837ead0e Add support for ${} env var syntax when skipping validation
Closes #1980

See also #825
2018-12-13 16:13:57 +00:00
Nick O'Leary
a1f135bd66 Allow oauth strategy callback method to be customised
Closes #1998

Method can be set via: `adminAuth.strategy.options.callbackMethod`

Can be either GET (default) or POST.
2018-12-13 13:43:57 +00:00
Nick O'Leary
978f4ecc58 Ensure fs context cache is flushed on close
Fixes #2001
2018-12-13 12:46:19 +00:00
Dave Conway-Jones
46a8d96997 fix library Buffer( to Buffer.alloc( for node 10 2018-12-13 11:32:58 +00:00
Nick O'Leary
c283224000 Merge branch 'master' into dev 2018-12-13 11:14:58 +00:00
Nick O'Leary
a6ef755139 Merge pull request #1993 from arunnattarayan/patch-1
Export to library produces empty folder when name has a trailing slash
2018-12-13 11:05:52 +00:00
Nick O'Leary
29a257d17a Merge pull request #1995 from node-red-hitachi/debug-node-with-jsonata
Add support of output editing in DEBUG node using JSONata
2018-12-13 11:05:21 +00:00
Nick O'Leary
368b76a183 Merge pull request #2000 from node-red-hitachi/i18n-markdown-tooltip
i18 support for markdown editor tooltips
2018-12-13 11:00:03 +00:00
Nick O'Leary
8bb861124d Catch file-not-found on startup when non-existant flow file specified 2018-12-13 10:59:03 +00:00
Hiroyasu Nishiyama
2f884ec778 i18 support for markdown editor tooltip 2018-12-11 22:33:11 +09:00
Nick O'Leary
8c561e92c8 Actively expire login sesssions and notify user 2018-12-11 11:32:12 +00:00
Hiroyasu Nishiyama
633b9180d7 update info text 2018-12-11 19:53:10 +09:00
Hiroyasu Nishiyama
0e2d0e1b6f merge dev 2018-12-11 19:02:49 +09:00
Nick O'Leary
ea4d65ceee Add RED.editor.registerTypeEditor for custom type editors 2018-12-10 22:21:21 +00:00
Nick O'Leary
d47ac84d2e Merge pull request #1999 from natcl/dev
JSON node: delete msg.schema before sending msg to avoid conflicts
2018-12-10 20:54:24 +00:00
Nathanaël Lécaudé
a97759aa35 JSON node: add help about schema deletion 2018-12-10 14:47:52 -05:00
Nathanaël Lécaudé
3fcfd4abdd JSON node: add help about schema deletion 2018-12-10 14:46:21 -05:00
Nathanaël Lécaudé
6d771da9a9 JSON node: delete msg.schema before sending msg to avoid conflicts 2018-12-10 13:47:55 -05:00
Nick O'Leary
6201247875 Tidy up markdown toolbar handling across all editors
Any editor for the markdown mode will now automatically get
the markdown toolbar added.

The comment node has been updated to handle this properly and
to not add two copies of its content to the sidebar.
2018-12-10 15:24:27 +00:00
Hiroyasu Nishiyama
8c367bcc53 update messages 2018-12-09 20:33:58 +09:00
Hiroyasu Nishiyama
8198132ca7 use output selector for specifying JSONata expression 2018-12-09 20:30:35 +09:00
Dave Conway-Jones
cf3b4e9e63 change check order for node.users
If node _def hasUsers is false then node.users may not exist... so won't have a length...
2018-12-08 18:09:33 +00:00
Hiroyasu Nishiyama
987dbf8a92 Merge branch 'dev' into debug-node-with-jsonata 2018-12-08 17:43:29 +09:00
Nick O'Leary
acf8c9bc4a Fixup version again because its Friday afternoon 2018-12-07 16:49:37 +00:00
Nick O'Leary
7173895d36 Fixup version 2018-12-07 16:47:19 +00:00
Nick O'Leary
43530d4a5f Remove tail/sentiment node tests 2018-12-07 16:45:06 +00:00
Nick O'Leary
fefabef9ee Bump for 0.20.0.beta.2 2018-12-07 16:44:14 +00:00
Nick O'Leary
0dd40a941b Handle 3rd party node trying to use core node-red icon 2018-12-07 16:43:06 +00:00
Nick O'Leary
24b6670bc4 Remove sentiment and tail nodes 2018-12-07 16:23:55 +00:00
Nick O'Leary
76661abbf5 Ensure changelog is copied into node-red module 2018-12-07 16:10:51 +00:00
Nick O'Leary
e9dc9eff9b fix changelog typo 2018-12-07 15:30:20 +00:00
Nick O'Leary
2f160743bc Add sentiment/tail external modules 2018-12-07 15:29:35 +00:00
Nick O'Leary
98616e772c Bump dependencies 2018-12-07 14:13:59 +00:00
Nick O'Leary
bc014fec9b Update changelog 2018-12-07 13:35:36 +00:00
Nick O'Leary
732598d9d2 Remove deprecated warning from RED.nodes for now... 2018-12-07 11:40:47 +00:00
Nick O'Leary
2979acd5b8 Bump to beta.1 version 2018-12-07 11:40:33 +00:00
Nick O'Leary
fd20cd524e Add senitment and tail nodes to deprecated list
Partof #1904
2018-12-07 11:25:09 +00:00
Nick O'Leary
7b80ae42e1 removed regex in if statement
Co-Authored-By: arunnattarayan <arunkumarit02@gmail.com>
2018-12-06 19:20:43 +05:30
Nick O'Leary
b7012674c6 Fix Template node name field from overflowing row 2018-12-06 10:54:42 +00:00
Nick O'Leary
c76bd39280 Improve alignment of node label edit inputs 2018-12-06 10:54:23 +00:00
Nick O'Leary
09cd710f66 Add RED.notifications.hide flag
For use by the UI tests to suppress notifications from being displayed
in the editor. It is not for use by end-users and not exposed in any
way other than via javascript injection by the UI tests
2018-12-06 10:51:56 +00:00
Nick O'Leary
d9aadf9d98 Merge pull request #1979 from node-red/better-npm-check
refuse to enable palette if npm too old
2018-12-05 14:52:28 +00:00
meeki007
86716b5ffb Tidy up Template node edit form
Closes #1969 #1970 #1968 #1967
2018-12-05 14:47:38 +00:00
meeki007
96e3aab3b4 update 10-switch.html - missing Font Awesome font
switch node was missing font  ( fa-ellipsis-h )
other nodes that have been updated with property option have the icon.
rbe-node and range-node have the icon to the left of property

its time the switch-node joins its siblings in uniformity.
2018-12-05 14:27:38 +00:00
Nick O'Leary
72c78fe3ad Merge pull request #1996 from node-red-hitachi/http-redirectList
Add redirectList property in msg of http-request node
2018-12-05 14:01:36 +00:00
Nick O'Leary
abe746020b Update packages/node_modules/@node-red/runtime/locales/en-US/runtime.json
Co-Authored-By: dceejay <dceejay@users.noreply.github.com>
2018-12-05 13:48:39 +00:00
Nick O'Leary
8e1c15419c API documentation updates 2018-12-05 13:00:25 +00:00
Nick O'Leary
ee47646cf7 Fix up unit tests 2018-12-04 15:59:43 +00:00
nakanishi
32d9acdaa5 minor fix 2018-12-04 15:46:46 +00:00
nakanishi
4eb3bd496b Add redirectList property in msg of http-request node 2018-12-04 15:39:01 +00:00
Hiroyasu Nishiyama
3c4f4d27d6 Support output editting of DEBUG node using JSONata 2018-12-01 18:03:04 +01:00
Nick O'Leary
2060af8a92 Merge pull request #1987 from node-red-hitachi/fa-nonexist
Show arrow-in node when invalid font-awesome icon name was specified …
2018-11-30 23:13:33 +00:00
Nick O'Leary
21bf74a467 Merge pull request #1989 from TothiViseo/diffNodePropertiesOnClick
Speeding up display of diff for large flows
2018-11-30 23:12:33 +00:00
Nick O'Leary
677833a277 Merge pull request #1990 from node-red-hitachi/move-en-info-text
Move English info text under locales directory
2018-11-30 23:07:53 +00:00
Nick O'Leary
0b5e4f2dd7 Documentation updates for node-red and runtime modules 2018-11-30 23:01:09 +00:00
Arun Nattarayan
06a1f30350 Added validation while export into library 2018-11-30 20:04:12 +05:30
Hiroyasu Nishiyama
c1ff241550 add fixes for core nodes 2018-11-30 13:02:15 +00:00
Hiroyasu Nishiyama
5717f75eac move English info text under locales directory 2018-11-30 12:50:49 +00:00
To-Thi Hoang
6b3b68a4e5 Diff panel DOM generation : Move generation of DOM for node properties to the click on the node row (instead of the diff panel opening). 2018-11-30 10:48:21 +01:00
nakanishi
8bda2d0add Show arrow-in node when invalid font-awesome icon name was specified for default icon 2018-11-29 16:57:39 +00:00
Nick O'Leary
bc02c9573c Generate runtime api docs in runtime module 2018-11-16 10:04:53 +00:00
jonferreira
86bb5503ab Update 21-httprequest.html 2018-11-15 17:11:40 +00:00
jonferreira
21ce23d27d Update 21-httprequest.js 2018-11-15 17:11:27 +00:00
jonferreira
6c75baecb2 Update messages.json 2018-11-15 17:11:11 +00:00
Nick O'Leary
8167608f04 Handle lookup of undefined property in Global context
Fixes #1978
2018-11-14 20:57:17 +00:00
Dave Conway-Jones
514e31aef9 refuse to enable palette if npm too old 2018-11-14 18:29:27 +00:00
Nick O'Leary
20a31a6d38 Allow a project to be loaded from cmdline even if its unknown 2018-11-14 12:51:23 +00:00
Nick O'Leary
4f0aa1bc02 Add 'open project' option to Projects Welcome dialog 2018-11-14 12:51:02 +00:00
Nick O'Leary
be0ef6e594 Merge pull request #1973 from node-red-hitachi/update-i18n-editor
add i18n & JP message for node config tab
2018-11-14 09:39:55 +00:00
Hiroyasu Nishiyama
93a8dbd31a add Japanese translation of newly added message 2018-11-14 09:13:21 +09:00
Hiroyasu Nishiyama
cf931e8ddf Merge remote-tracking branch 'upstream/dev' into update-i18n-editor 2018-11-14 09:06:05 +09:00
Hiroyasu Nishiyama
510bfbf268 remove @ from English message 2018-11-14 08:59:36 +09:00
Nick O'Leary
2f93bb969b Merge pull request #1976 from MatthiasU/master
Add quotation marks for basic auth challenge
2018-11-13 23:16:41 +00:00
Nick O'Leary
27365c9f7b Tweak conflict message 2018-11-13 23:12:39 +00:00
Nick O'Leary
b1d2e188f5 Merge pull request #1971 from node-red-hitachi/update-message-catalogue-JP
Update Japanese message catalogue
2018-11-13 23:10:24 +00:00
Nick O'Leary
e4f67df2a1 Add 'type already registered' check in palette editor 2018-11-13 22:36:56 +00:00
Matthias Uttendorfer
e094ea3d2a Add quotation marks for basic auth challenge
This is required by RFC 2617
2018-11-13 23:05:19 +01:00
Nick O'Leary
7515b745b5 Make sure editor footer is i18n enabled 2018-11-13 15:10:19 +00:00
Hiroyasu Nishiyama
0e902a7e71 i18n & JP message for node config tab 2018-11-13 23:29:14 +09:00
Nick O'Leary
2dfb443625 Handle missing tab.disabled property 2018-11-13 13:39:06 +00:00
Nick O'Leary
e6e7747ae1 Handle missing wires prop and string x/y props on import 2018-11-13 13:29:48 +00:00
Hiroyasu Nishiyama
d80ea6c0f5 add additional Japanese translation 2018-11-13 06:55:04 +09:00
Hiroyasu Nishiyama
ac6e3988a8 merge upstream/dev 2018-11-13 06:50:01 +09:00
Nick O'Leary
bfd98f3767 Add ability to delete context values from sidebar 2018-11-12 17:04:22 +00:00
Hiroyasu Nishiyama
cc8bc1339f update Japanese message catalogue - nodes messages.json 2018-11-12 21:09:19 +09:00
Hiroyasu Nishiyama
542d1dc600 update Japanese message catalogue - editor.json 2018-11-12 20:59:09 +09:00
Nick O'Leary
1c66c88f95 Allow copy-to-clipboard copy whole tabs 2018-11-09 09:51:55 +01:00
Nick O'Leary
dc880c672a Make disabled flows more obvious in editor 2018-11-08 18:04:36 +01:00
Nick O'Leary
073f38c68c Only unsub mqtt node that is being removed 2018-11-08 17:03:41 +01:00
Nick O'Leary
6a6d13b075 Allow import/export from file in editor 2018-11-05 22:32:39 +00:00
Nick O'Leary
9bb7e72c69 Merge pull request #1962 from node-red-hitachi/update-JP-catalog-for-nodes
Update Japanese message catalog for nodes
2018-11-03 21:36:29 +00:00
Nick O'Leary
7436e01188 Allow config nodes to be selected in sidebar and deleted 2018-11-03 21:32:38 +00:00
Hiroyasu Nishiyama
3d272d0f10 update Japanese translation of nodes 2018-11-02 23:41:48 +09:00
Hiroyasu Nishiyama
ee66a12dad add missing message reference in websocket config node 2018-11-02 23:40:14 +09:00
Nick O'Leary
47de85b012 Merge pull request #1956 from node-red-hitachi/dev-fix_rpi_html
Fix html file of rpi-gpio out node
2018-11-02 14:01:28 +00:00
Nick O'Leary
f85b63a972 Merge pull request #1959 from natcl/httpTimeoutPerMsg
http request: add msg.requestTimeout parameter
2018-11-02 14:00:57 +00:00
Nick O'Leary
e630919ef8 Handle subflow type in refreshLabelForm
Part of #1955
2018-11-02 13:59:10 +00:00
Nick O'Leary
2e3fd49b40 Merge pull request #1955 from node-red-hitachi/subflow-label
Show port label of subflow with input port
2018-11-02 13:59:04 +00:00
Nick O'Leary
dd54af2c08 Merge pull request #1961 from node-red-hitachi/update-JP-catalog-for-editor
Update message catalog for Node-RED editor
2018-11-02 13:17:27 +00:00
Hiroyasu Nishiyama
737bf411ff make projects menu use i18n 2018-11-02 21:25:14 +09:00
Hiroyasu Nishiyama
5070b1a6b5 update Japanese editor.json message 2018-11-02 21:11:09 +09:00
Hiroyasu Nishiyama
c849da92cf fix word "reloaded" to "reverted" 2018-11-02 21:10:29 +09:00
Nathanaël Lécaudé
30c1d31a99 http request: coding style 2018-11-01 17:32:25 -04:00
Nathanaël Lécaudé
6934a2d5c3 http request: add msg.requestTimeout parameter 2018-11-01 17:27:04 -04:00
Masae Okada
b9906ced9a fix html file of rpi-gpio out node 2018-11-01 18:34:38 +09:00
nakanishi
5500b4fe35 Show port label of subflow with input port 2018-11-01 13:35:13 +09:00
286 changed files with 13799 additions and 5297 deletions

1
.gitignore vendored
View File

@@ -21,3 +21,4 @@ nodes/core/locales/zz-ZZ
packages/node_modules/@node-red/editor-client/public
!test/**/node_modules
docs
!packages/node_modules/**/docs

View File

@@ -2,12 +2,9 @@ sudo: false
language: node_js
matrix:
include:
- node_js: "11"
- node_js: "10"
- node_js: "8"
script:
- ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
before_script:
- npm install -g istanbul coveralls
allow_failures:
- node_js: "11"
- node_js: "8"

View File

@@ -1,3 +1,200 @@
#### 0.20.0-beta.5: Beta Release
Runtime
- Bump dependencies
- Allow `$parent` access of flow context
- Make Node.\_flow a writeable property
- Do not propagate Flow.getNode to parent when called from outside flow
- Add support of subflow env var
Editor
- Properly sanitize node names in deploy warning dialogs
- Fix XSS issues in library ui code
- Add env type to subflow env var types
- Display parent subflow properties in edit dialog
- Fix direction value of subflow output
- Add Status Node to Subflow to allow subflow-specific status Closes #597
- Better handling of multiple flow merges Fixes #2039
Nodes
- Various translation updates
- Catch: Add 'catch uncaught only' mode. Closes #1747
- Link: scroll to current flow in node list
- HTTPRequest: add option to urlencode cookies
- HTTPRequest: option to use msg.payload as query params on GET. #1981
- Debug: Add local time display option to numerics in debug window
- MQTT: Add parsed JSON output option
#### 0.20.0-beta.4: Beta Release
Runtime
- Bump JSONata to 1.6.4
- Add Flow.getSetting for resolving env-var properties
- Refactor Subflow logic into own class
- Restore RED.auth to node-red module api
- Tidy up when usage in Flow and Node
Editor
- German translation
- Change default dropdown appearance and sidebar tab menu handling
- Handle multiple-select box when nothing selected Fixes #2021
- Handle i18n properly when key is a valid sub-identifier Fixes #2028
- Avoid duplicate links when missing node type installed Fixes #2032
- Add View Tools
- Don't collapse version control header when clicking refresh
- Add fast entry via keyboard for string of nodes
- Check for undeployed change before showing open project dialog
- Add placeholder node when in quick-add mode
- Move nodes to top-left corner when converting to subflow
Nodes
- Debug: Allow debug edit expression to be sent to status
- WebSocket: Fix missing translated help
#### 0.20.0-beta.3: Beta Release
Editor
- Update palette manager view properly when module updated
- Add TreeList common widget
- Fix visual jump when opening Comment editor on Safari Part of #2008
- Fix vertical align of markdown editor in Safari Fixes #2008
- Avoid marking node as changed if label state is default Fixes #2009
- Highlight port on node hover while joining
- Support drag-wiring of link nodes
- Allow TypeSearch to include a filter option
- Improve diff colouring
- Allow sections to toggle in 2-element stack
- Add support for ${} env var syntax when skipping validation Closes #1980
- i18 support for markdown editor tooltip
- Add RED.editor.registerTypeEditor for custom type editors
- Tidy up markdown toolbar handling across all editors
- Added validation while export into library
- Reuse notification boxes rather than stack multiple of the same type
- Make ssh key dialog accessible when opened from new proj dialog
Runtime
- Bump JSONata to 1.6.4 Fixes #2023
- Add audit logging to admin api
- Fix failure of RED.require #2010
- Allow oauth strategy callback method to be customised Closes #1998
- Ensure fs context cache is flushed on close Fixes #2001
- Fix library Buffer( to Buffer.alloc( for node 10
- Catch file-not-found on startup when non-existant flow file specified
- Actively expire login sesssions and notify user
- Add quotation marks for basic auth challenge #1976
Nodes
- Change: remove promises to improve performance
- Debug: add ability to apply JSONata expression to message
- Join: remove promises to improve performance
- JSON: delete msg.schema before sending msg to avoid conflicts
- Link: update UI to use common TreeList widget
- Switch: remove promises to improve performance
#### 0.20.0-beta.2: Beta Release
- Split Node-RED internals into multiple sub-modules
Editor
- Allow the editor to use a custom admin api url root
- Improve performance of Flow Diff dialog - @TothiViseo #1989
- Add 'open project' option to Projects Welcome dialog
- Add 'type already registered' check in palette editor
- Handle missing tab.disabled property
- Handle missing wires prop and string x/y props on import
- Add RED.notifications.hide flag - for UI testing
- Improve alignment of node label edit inputs
- Show arrow-in node when invalid font-awesome icon name was specified for default icon
- Add ability to delete context values from sidebar
- Allow copy-to-clipboard copy whole tabs
- Make disabled flows more obvious in editor
- Allow import/export from file in editor
- Allow config nodes to be selected in sidebar and deleted
- Show port label of subflow with input port
- Support ctrl-click selection of flow tabs
- Allow left-hand node button to act as toggle
- Support dbl-click in tab bar to add new flow in position
- Fix duplicate subflow detection on import
- Add import notification with info on what has been imported Closes #1862
- Show error details when trying to import invalid json
- Show default icon when non-existent font-awesome icon was specified
- Add configurable option for showing node label
- Avoid http redirects as Safari doesn't reuse Auth header Fixes #1903
- Tidy up ace tooltip styling
- Add event log to editor
- Add tooltips to multiple editor elements
- Allow palette to be hidden
- Add node module into to sidebar and palette popover
- Mark all newly imported nodes as changed
- Allow a node label to be hidden
- Add markdown formatting toolbar
- Add markdown toolbar to various editors
- Fix i18n handling for ja-JP locale on Safari/MacOS
- Add node body tooltip
- Decrease opacity of flow-navigator
- Update tooltip style
- Update ACE to 1.4.1-src-min-noconflict
- Cache node locales by language
- Show icon element with either icon image or fa-icon
- Added font-awesome icons to user defined icon
- Update info side bar with node description section
- One-click search of config node users
- Redesign node edit dialog to tabbed style
- Add 'restart flows' option to deploy menu
- Add node description property UI
Runtime
- Allow a project to be loaded from cmdline
- Handle lookup of undefined property in Global context Fixes #1978
- Refuse to enable Manage Palette if npm too old
- Remove restriction on upgrading non-local modules
- Remove deprecated Buffer constructor usage Fixes #1709
- Update httpServerOptions doc in settings.js
- Exclude non-testable .js files from the unit tests
- Add --safe mode flag to allow starting without flows running
- Add setting-defined accessToken for automated access to the adminAPI - #1789
Nodes
- Move all core node EN help to their own locale files - #1990
- CSV: better regex for number detection
- Debug: hide button if not configured to send to sidebar
- Delay: report queue activity when in by-topic mode
- Delay: add msg.flush mode
- Exec: Preserve existing properties on msg object
- File: remove CR/LF from incoming filename
- Function: create custom ace javascript mode to handle ES6 Fixes #1911
- Function: add env.get
- HTTP Request: Add http-proxy config #1913
- HTTP Request: add msg.redirectList to output
- HTTP Request: add msg.requestTimeout option for per-message setting - @natcl #1959
- MQTT: add auto-detect and base64 output to mqtt node Fixes #1912 - @DurandA
- MQTT: only unsubscribe node that is being removed
- Sentiment: move to node-red-node-sentiment
- Switch: add missing edit dialog icon
- Tail: move to node-red-node-tail
- TCPGet: clear status if user changes target per message
- Template: tidy up edit dialog
- UDP: more resilient binding to correct port for udp, give input side priority
- Split/Join: add msg.reset to info panel
- Split/Join: reset join without sending part array
- Watch: add msg.filename so can feed direct to file in node
- WebSocket: preserve \_session on msg but don't send as part of wholemsg
#### 0.19.5: Maintenance Release
- Recognize pip installs of RPi.GPIO (#1934)

View File

@@ -15,6 +15,7 @@
**/
var path = require("path");
var fs = require("fs-extra");
module.exports = function(grunt) {
@@ -135,6 +136,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js",
@@ -150,6 +152,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js",
@@ -373,6 +376,10 @@ module.exports = function(grunt) {
src: 'CHANGELOG.md',
dest: 'packages/node_modules/@node-red/editor-client/public/red/about'
},
{
src: 'CHANGELOG.md',
dest: 'packages/node_modules/node-red/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/',
src: '**',
@@ -429,28 +436,43 @@ module.exports = function(grunt) {
}
},
jsdoc : {
runtimeAPI: {
src: 'packages/node_modules/@node-red/runtime/lib/api/*.js',
modules: {
src: [
'packages/node_modules/node-red/lib/red.js',
'packages/node_modules/@node-red/runtime/lib/index.js',
'packages/node_modules/@node-red/runtime/lib/api/*.js',
'packages/node_modules/@node-red/runtime/lib/events.js',
'packages/node_modules/@node-red/util/**/*.js',
'packages/node_modules/@node-red/editor-api/lib/index.js',
'packages/node_modules/@node-red/editor-api/lib/auth/index.js'
],
options: {
destination: 'docs',
configure: './jsdoc.json'
}
},
nodeREDUtil: {
src: 'packages/node_modules/@node-red/util/**/*.js',
editor: {
src: [
'packages/node_modules/@node-red/editor-client/src/js'
],
options: {
destination: 'packages/node_modules/@node-red/util/docs',
destination: 'packages/node_modules/@node-red/editor-client/docs',
configure: './jsdoc.json'
}
}
},
jsdoc2md: {
runtimeAPI: {
options: {
separators: true
},
src: 'packages/node_modules/@node-red/runtime/lib/api/*.js',
dest: 'docs/runtime-api.md'
src: [
'packages/node_modules/@node-red/runtime/lib/index.js',
'packages/node_modules/@node-red/runtime/lib/api/*.js',
'packages/node_modules/@node-red/runtime/lib/events.js'
],
dest: 'packages/node_modules/@node-red/runtime/docs/api.md'
},
nodeREDUtil: {
options: {
@@ -523,11 +545,21 @@ module.exports = function(grunt) {
});
grunt.registerTask('verifyPackageDependencies', function() {
var done = this.async();
var verifyDependencies = require("./scripts/verify-package-dependencies.js");
var failures = verifyDependencies();
if (failures.length > 0) {
failures.forEach(f => grunt.log.error(f));
grunt.fail.fatal("Failed to verify package dependencies");
verifyDependencies().then(function(failures) {
if (failures.length > 0) {
failures.forEach(f => grunt.log.error(f));
grunt.fail.fatal("Failed to verify package dependencies");
}
done();
});
});
grunt.registerTask('verifyUiTestDependencies', function() {
if (!fs.existsSync(path.join("node_modules", "chromedriver"))) {
grunt.fail.fatal('You need to run "npm install chromedriver@2" before running UI test.');
return false;
}
});
@@ -551,7 +583,7 @@ module.exports = function(grunt) {
grunt.registerTask('test-ui',
'Builds editor content then runs unit tests on editor ui',
['build','jshint:editor','webdriver:all']);
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
grunt.registerTask('test-nodes',
'Runs unit tests on core nodes',

View File

@@ -15,7 +15,6 @@
},
"templates": {
"systemName": "Node-RED Runtime API",
"theme":"yeti",
"footer": "",
"copyright": "Released under the Apache License v2.0",
"default": {

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "0.20.0-alpha.0",
"version": "0.20.0-beta.5",
"description": "A visual tool for wiring the Internet of Things",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -24,7 +24,7 @@
}
],
"dependencies": {
"ajv": "6.5.4",
"ajv": "6.8.1",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
@@ -32,29 +32,31 @@
"clone": "2.1.2",
"cookie": "0.3.1",
"cookie-parser": "1.4.3",
"cors": "2.8.4",
"cron": "1.5.0",
"denque": "1.3.0",
"cors": "2.8.5",
"cron": "1.6.0",
"denque": "1.4.0",
"express": "4.16.4",
"express-session": "1.15.6",
"fs-extra": "5.0.0",
"fs-extra": "7.0.1",
"fs.notify": "0.0.4",
"hash-sum": "1.0.2",
"https-proxy-agent": "2.2.1",
"i18next": "11.6.0",
"i18next": "14.1.1",
"is-utf8": "0.2.1",
"js-yaml": "3.12.0",
"js-yaml": "3.12.1",
"json-stringify-safe": "5.0.1",
"jsonata": "1.5.4",
"media-typer": "0.3.0",
"jsonata": "1.6.4",
"media-typer": "1.0.1",
"memorystore": "1.6.0",
"mime": "1.4.1",
"mime": "2.4.0",
"mqtt": "2.18.8",
"multer": "1.4.1",
"mustache": "2.3.2",
"node-red-node-email": "0.1.*",
"node-red-node-feedparser": "^0.1.12",
"mustache": "3.0.1",
"node-red-node-email": "1.0.*",
"node-red-node-feedparser": "^0.1.14",
"node-red-node-rbe": "0.2.*",
"node-red-node-sentiment": "^0.1.0",
"node-red-node-tail": "^0.0.2",
"node-red-node-twitter": "^1.1.0",
"nopt": "4.0.1",
"oauth2orize": "1.11.0",
@@ -65,20 +67,18 @@
"raw-body": "2.3.3",
"request": "2.88.0",
"semver": "5.6.0",
"sentiment": "2.1.0",
"uglify-js": "3.4.9",
"when": "3.7.8",
"ws": "1.1.5",
"ws": "6.1.3",
"xml2js": "0.4.19"
},
"optionalDependencies": {
"bcrypt": "~2.0.0"
},
"devDependencies": {
"chromedriver": "2.43.1",
"grunt": "~1.0.3",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.1",
"grunt-cli": "~1.3.2",
"grunt-concurrent": "~2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-compress": "~1.4.0",
@@ -104,12 +104,12 @@
"mosca": "^2.8.3",
"should": "^8.4.0",
"sinon": "1.17.7",
"stoppable": "^1.0.7",
"supertest": "3.3.0",
"wdio-chromedriver-service": "^0.1.3",
"wdio-mocha-framework": "^0.6.2",
"stoppable": "^1.1.0",
"supertest": "3.4.2",
"wdio-chromedriver-service": "^0.1.5",
"wdio-mocha-framework": "^0.6.4",
"wdio-spec-reporter": "^0.1.5",
"webdriverio": "^4.13.1",
"webdriverio": "^4.14.1",
"node-red-node-test-helper": "node-red/node-red-node-test-helper",
"jsdoc-nr-template": "node-red/jsdoc-nr-template"
},

View File

@@ -37,5 +37,20 @@ module.exports = {
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
},
delete: function(req,res) {
var opts = {
user: req.user,
scope: req.params.scope,
id: req.params.id,
key: req.params[0],
store: req.query['store']
}
runtimeAPI.context.delete(opts).then(function(result) {
res.status(204).end();
}).catch(function(err) {
apiUtils.rejectHandler(req,res,err);
})
}
}

View File

@@ -62,6 +62,11 @@ module.exports = {
adminApp.get("/context/:scope(node|flow)/:id",needsPermission("context.read"),context.get,apiUtil.errorHandler);
adminApp.get("/context/:scope(node|flow)/:id/*",needsPermission("context.read"),context.get,apiUtil.errorHandler);
// adminApp.delete("/context/:scope(global)",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
adminApp.delete("/context/:scope(global)/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
// adminApp.delete("/context/:scope(node|flow)/:id",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
adminApp.delete("/context/:scope(node|flow)/:id/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
return adminApp;
}
}

View File

@@ -14,6 +14,11 @@
* limitations under the License.
**/
/**
* @mixin @node-red/editor-api_auth
*/
var passport = require("passport");
var oauth2orize = require("oauth2orize");
@@ -44,7 +49,14 @@ function init(_settings,storage) {
Tokens.init(mergedAdminAuth,storage);
}
}
/**
* Returns an Express middleware function that ensures the user making a request
* has the necessary permission.
*
* @param {String} permission - the permission required for the request, such as `flows.write`
* @return {Function} - an Express middleware
* @memberof @node-red/editor-api_auth
*/
function needsPermission(permission) {
return function(req,res,next) {
if (settings && settings.adminAuth) {
@@ -182,7 +194,12 @@ function genericStrategy(adminApp,strategy) {
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
);
adminApp.get('/auth/strategy/callback',
var callbackMethodFunc = adminApp.get;
if (/^post$/i.test(options.callbackMethod)) {
callbackMethodFunc = adminApp.post;
}
callbackMethodFunc('/auth/strategy/callback',
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
completeGenerateStrategyAuth
);

View File

@@ -25,27 +25,39 @@ function generateToken(length) {
var storage;
var sessionExpiryTime
var sessions = {};
var loadedSessions = null;
var apiAccessTokens;
var sessionExpiryListeners = [];
var expiryTimeout;
function expireSessions() {
if (expiryTimeout) {
clearTimeout(expiryTimeout);
expiryTimeout = null;
}
var nextExpiry = Number.MAX_SAFE_INTEGER;
var now = Date.now();
var modified = false;
for (var t in sessions) {
if (sessions.hasOwnProperty(t)) {
var session = sessions[t];
if (!session.hasOwnProperty("expires") || session.expires < now) {
sessionExpiryListeners.forEach(listener => { listener(session) })
delete sessions[t];
modified = true;
} else {
if (session.expires < nextExpiry) {
nextExpiry = session.expires;
}
}
}
}
if (nextExpiry < Number.MAX_SAFE_INTEGER) {
// Allow 5 seconds grace
expiryTimeout = setTimeout(expireSessions,(nextExpiry - Date.now()) + 5000)
}
if (modified) {
return storage.saveSessions(sessions);
} else {
@@ -65,6 +77,9 @@ function loadSessions() {
module.exports = {
init: function(adminAuthSettings, _storage) {
storage = _storage;
sessionExpiryListeners = [];
sessionExpiryTime = adminAuthSettings.sessionExpiryTime || 604800; // 1 week in seconds
// At this point, storage will not have been initialised, so defer loading
// the sessions until there's a request for them.
@@ -112,6 +127,11 @@ module.exports = {
expires: accessTokenExpiresAt
};
sessions[accessToken] = session;
if (!expiryTimeout) {
expiryTimeout = setTimeout(expireSessions,(accessTokenExpiresAt - Date.now()) + 5000)
}
return storage.saveSessions(sessions).then(function() {
return {
accessToken: accessToken,
@@ -125,5 +145,8 @@ module.exports = {
delete sessions[token];
return storage.saveSessions(sessions);
});
},
onSessionExpiry: function(callback) {
sessionExpiryListeners.push(callback);
}
}

View File

@@ -15,6 +15,7 @@
**/
var ws = require("ws");
var url = require("url");
var log = require("@node-red/util").log; // TODO: separate module
var Tokens;
@@ -40,11 +41,19 @@ function init(_server,_settings,_runtimeAPI) {
settings = _settings;
runtimeAPI = _runtimeAPI;
Tokens = require("../auth/tokens");
Tokens.onSessionExpiry(handleSessionExpiry);
Users = require("../auth/users");
Permissions = require("../auth/permissions");
}
function handleSessionExpiry(session) {
activeConnections.forEach(connection => {
if (connection.token === session.accessToken) {
connection.ws.send(JSON.stringify({auth:"fail"}));
connection.ws.close();
}
})
}
function generateSession(length) {
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
var token = [];
@@ -88,7 +97,7 @@ function CommsConnection(ws) {
// handleRemoteSubscription(ws,msg.subscribe);
}
} else {
var completeConnection = function(userScope,sendAck) {
var completeConnection = function(userScope,session,sendAck) {
try {
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
ws.send(JSON.stringify({auth:"fail"}));
@@ -96,6 +105,7 @@ function CommsConnection(ws) {
} else {
pendingAuth = false;
addActiveConnection(self);
self.token = msg.auth;
if (sendAck) {
ws.send(JSON.stringify({auth:"ok"}));
}
@@ -113,29 +123,29 @@ function CommsConnection(ws) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(client.scope,true);
completeConnection(client.scope,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
completeConnection(null,null,false);
}
});
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
completeConnection(null,null,false);
}
});
} else {
if (anonymousUser) {
log.audit({event: "comms.auth",user:anonymousUser});
self.user = anonymousUser;
completeConnection(anonymousUser.permissions,false);
completeConnection(anonymousUser.permissions,null,false);
//TODO: duplicated code - pull non-auth message handling out
if (msg.subscribe) {
self.subscribe(msg.subscribe);
}
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,false);
completeConnection(null,null,false);
}
}
}
@@ -178,27 +188,27 @@ function start() {
Users.default().then(function(_anonymousUser) {
anonymousUser = _anonymousUser;
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
var path = settings.httpAdminRoot || "/";
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
wsServer = new ws.Server({
server:server,
path:path,
// Disable the deflate option due to this issue
// https://github.com/websockets/ws/pull/632
// that is fixed in the 1.x release of the ws module
// that we cannot currently pickup as it drops node 0.10 support
//perMessageDeflate: false
});
var commsPath = settings.httpAdminRoot || "/";
commsPath = (commsPath.slice(0,1) != "/" ? "/":"") + commsPath + (commsPath.slice(-1) == "/" ? "":"/") + "comms";
wsServer = new ws.Server({ noServer: true });
wsServer.on('connection',function(ws) {
var commsConnection = new CommsConnection(ws);
});
wsServer.on('error', function(err) {
log.warn(log._("comms.error-server",{message:err.toString()}));
});
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === commsPath) {
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request);
});
}
// Don't destroy the socket as other listeners may want to handle the
// event.
});
lastSentTime = Date.now();
heartbeatTimer = setInterval(function() {

View File

@@ -57,7 +57,7 @@ module.exports = {
}
runtimeAPI.nodes.getIcon(opts).then(function(data) {
if (data) {
var contentType = mime.lookup(icon);
var contentType = mime.getType(icon);
res.set("Content-Type", contentType);
res.send(data);
} else {

View File

@@ -14,6 +14,16 @@
* limitations under the License.
**/
/**
* This module provides an Express application to serve the Node-RED editor.
*
* It implements the Node-RED HTTP Admin API the Editor uses to interact
* with the Node-RED runtime.
*
* @namespace @node-red/editor-api
*/
var express = require("express");
var bodyParser = require("body-parser");
var util = require('util');
@@ -28,7 +38,16 @@ var adminApp;
var server;
var editor;
function init(_server,settings,storage,runtimeAPI) {
/**
* Initialise the module.
* @param {Object} settings The runtime settings
* @param {HTTPServer} server An instance of HTTP Server
* @param {Storage} storage An instance of Node-RED Storage
* @param {Runtime} runtimeAPI An instance of Node-RED Runtime
* @memberof @node-red/editor-api
*/
function init(settings,_server,storage,runtimeAPI) {
server = _server;
if (settings.httpAdminRoot !== false) {
adminApp = express();
@@ -80,6 +99,12 @@ function init(_server,settings,storage,runtimeAPI) {
adminApp = null;
}
}
/**
* Start the module.
* @return {Promise} resolves when the application is ready to handle requests
* @memberof @node-red/editor-api
*/
function start() {
if (editor) {
return editor.start();
@@ -87,6 +112,12 @@ function start() {
return when.resolve();
}
}
/**
* Stop the module.
* @return {Promise} resolves when the application is stopped
* @memberof @node-red/editor-api
*/
function stop() {
if (editor) {
editor.stop();
@@ -97,9 +128,18 @@ module.exports = {
init: init,
start: start,
stop: stop,
/**
* @memberof @node-red/editor-api
* @mixes @node-red/editor-api_auth
*/
auth: {
needsPermission: auth.needsPermission
},
get adminApp() { return adminApp; },
get server() { return server; }
/**
* The Express app used to serve the Node-RED Editor
* @type ExpressApplication
* @memberof @node-red/editor-api
*/
get httpAdmin() { return adminApp; }
};

View File

@@ -21,6 +21,8 @@ var i18n = require("@node-red/util").i18n; // TODO: separate module
module.exports = {
errorHandler: function(err,req,res,next) {
//TODO: why this when rejectHandler also?!
if (err.message === "request entity too large") {
log.error(err);
} else {
@@ -39,7 +41,9 @@ module.exports = {
return lang;
},
rejectHandler: function(req,res,err) {
res.status(err.status||500).json({
//TODO: why this when errorHandler also?!
log.audit({event: "api.error",error:err.code||"unexpected_error",message:err.message||err.toString()},req);
res.status(err.status||400).json({
code: err.code||"unexpected_error",
message: err.message||err.toString()
});

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "0.20.0-alpha.0",
"version": "0.20.0-beta.5",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -8,26 +8,30 @@
"url": "https://github.com/node-red/node-red.git"
},
"contributors": [
{ "name": "Nick O'Leary" },
{ "name": "Dave Conway-Jones"}
{
"name": "Nick O'Leary"
},
{
"name": "Dave Conway-Jones"
}
],
"dependencies": {
"@node-red/util": "*",
"@node-red/editor-client": "*",
"@node-red/util": "0.20.0-beta.5",
"@node-red/editor-client": "0.20.0-beta.5",
"bcryptjs": "2.4.3",
"body-parser": "1.18.3",
"clone": "2.1.2",
"cors": "2.8.4",
"cors": "2.8.5",
"express-session": "1.15.6",
"express": "4.16.4",
"memorystore": "1.6.0",
"mime": "1.4.1",
"mustache": "2.3.2",
"mime": "2.4.0",
"mustache": "3.0.1",
"oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",
"passport": "0.4.0",
"when": "3.7.8",
"ws": "1.1.5"
"ws": "6.1.3"
}
}

View File

@@ -0,0 +1,829 @@
{
"common" : {
"label" : {
"name" : "Name",
"ok" : "OK",
"done" : "Fertig",
"cancel" : "Abbrechen",
"delete" : "Löschen",
"close" : "Schließen",
"load" : "Laden",
"save" : "Speichern",
"import" : "Import",
"export" : "Exportieren",
"back" : "Zurück",
"next" : "Weiter",
"clone" : "Projekt duplizieren",
"cont" : "Weiter"
}
},
"workspace" : {
"defaultName" : "Flow __number__",
"editFlow" : "Flow bearbeiten: __name__",
"confirmDelete" : "Löschen bestätigen",
"delete" : "Sind Sie wirklich sicher, dass Sie '__label__' löschen wollen?",
"dropFlowHere" : "Hier können Sie den Flow fallen lassen.",
"status" : "Status",
"enabled" : "Aktiviert",
"disabled" : "Inaktiviert",
"info" : "Beschreibung",
"tip" : "Beschreibung akzeptiert Markdown und wird auf der Registerkarte Info angezeigt."
},
"menu" : {
"label" : {
"view" : {
"view" : "Ansicht",
"grid" : "Gitter",
"showGrid" : "Raster anzeigen",
"snapGrid" : "Einrasten am Raster",
"gridSize" : "Rastergröße",
"textDir" : "Textrichtung",
"defaultDir" : "Standard",
"ltr" : "Links-nach-rechts",
"rtl" : "Von rechts nach links",
"auto" : "Kontextuell"
},
"sidebar" : {
"show" : "Seitenleiste anzeigen"
},
"settings" : "Einstellungen",
"userSettings" : "Benutzereinstellungen",
"nodes" : "Knoten",
"displayStatus" : "Knotenstatus anzeigen",
"displayConfig" : "Konfigurationsknoten",
"import" : "Import",
"export" : "Exportieren",
"search" : "Flows durchsuchen",
"searchInput" : "durchsuchen Sie Ihre Flows",
"clipboard" : "Zwischenablage",
"library" : "Bibliothek",
"examples" : "Beispiele",
"subflows" : "Subflow",
"createSubflow" : "Subflow erstellen",
"selectionToSubflow" : "Auswahl für Subflow",
"flows" : "Flows",
"add" : "Hinzufügen",
"rename" : "Umbenennen",
"delete" : "Löschen",
"keyboardShortcuts" : "Tastaturkurzbefehle",
"login" : "Anmelden",
"logout" : "Abmelden",
"editPalette" : "Palette verwalten",
"other" : "Sonstige",
"showTips" : "Tipps anzeigen",
"help" : "Node-RED-Website",
"projects" : "Projekte",
"projects-new" : "Neu",
"projects-open" : "Öffnen",
"projects-settings" : "Projekteinstellungen"
}
},
"user" : {
"loggedInAs" : "Angemeldet als __name__",
"username" : "Benutzername",
"password" : "Kennwort",
"login" : "Anmelden",
"loginFailed" : "Anmeldung fehlgeschlagen",
"notAuthorized" : "Keine Berechtigung",
"errors" : {
"settings" : "Sie müssen angemeldet sein, um auf die Einstellungen zuzugreifen.",
"deploy" : "Sie müssen angemeldet sein, um Änderungen implementieren zu können.",
"notAuthorized" : "Sie müssen angemeldet sein, um diese Aktion ausführen zu können."
}
},
"notification" : {
"warning" : "<strong> Warnung </strong>: __message__",
"warnings" : {
"undeployedChanges" : "Knoten hat nicht implementierte Änderungen",
"nodeActionDisabled" : "In Subflow inaktivierte Knotenaktionen",
"missing-types" : "<p> Die Flows wurden aufgrund fehlender Knotentypen gestoppt. </p>",
"restartRequired" : "Knoten-RED muss erneut gestartet werden, damit aufgerüstete Module aktiviert werden können",
"credentials_load_failed" : "<p> Die Flows wurden gestoppt, da die Berechtigungsnachweise nicht entschlüsselt werden konnten. </p> <p> Die Datei mit dem Datenflowberechtigungsnachweis ist verschlüsselt, aber der Verschlüsselungsschlüssel des Projekts fehlt oder ist ungültig. </p>",
"credentials_load_failed_reset" : "<p> Die Berechtigungsnachweise konnten nicht entschlüsselt werden </p> <p> Die Datei mit dem Flow-Berechtigungsnachweis ist verschlüsselt, aber der Chiffrierschlüssel des Projekts fehlt oder ist ungültig. </p> <p> Die Datei des Flow-Berechtigungsnachweises wird bei der nächsten Implementierung zurückgesetzt. Alle vorhandenen Datenflowberechtigungsnachweise werden gelöscht. </p>",
"missing_flow_file" : "<p> Die Projektflowdatei wurde nicht gefunden. </p> <p> Das Projekt ist nicht mit einer Flow-Datei konfiguriert. </p>",
"missing_package_file" : "<p> Die Projektpaketdatei wurde nicht gefunden. </p> <p> In dem Projekt fehlt eine Datei 'package.json'. </p>",
"project_empty" : "<p> Das Projekt ist leer. </p> <p> Möchten Sie eine Standardgruppe von Projektdateien erstellen? <br/> Andernfalls müssen Sie Dateien außerhalb des Editors manuell zum Projekt hinzufügen. </p>",
"project_not_found" : "<p> Das Projekt '__project__' wurde nicht gefunden. </p>",
"git_merge_conflict" : "<p> Das automatische Zusammenführen von Änderungen ist fehlgeschlagen. </p> <p> Beheben Sie die nicht zusammengeführten Konflikte und schreiben Sie die Ergebnisse fest. </p>"
},
"error" : "<strong> Fehler </strong>: __message__",
"errors" : {
"lostConnection" : "Verbindung zum Server verloren, Verbindung wird erneut hergestellt ...",
"lostConnectionReconnect" : "Verbindung zum Server verloren, Verbindung in __time__s wird wieder hergestellt.",
"lostConnectionTry" : "Jetzt testen",
"cannotAddSubflowToItself" : "Subflow kann nicht zu sich selbst hinzugefügt werden",
"cannotAddCircularReference" : "Subflow kann nicht hinzugefügt werden-zirkuläre Referenz wurde erkannt",
"unsupportedVersion" : "<p> Verwenden einer nicht unterstützten Version von Node.js </p> <p> Sie sollten ein Upgrade auf das neueste LTS-Release von Node.js durchführen. </p>",
"failedToAppendNode" : "<p> Fehler beim Laden von '__module__' </p> <p> __error__ </p>"
},
"project" : {
"change-branch" : "Wechseln Sie in die lokale Verzweigung '__project__'.",
"merge-abort" : "Git-Zusammenführung abgebrochen",
"loaded" : "Projekt '__project__' geladen",
"updated" : "Projekt '__project__' aktualisiert",
"pull" : "Projekt '__project__' erneut geladen",
"revert" : "Projekt '__project__' erneut geladen",
"merge-complete" : "Git-Zusammenführung abgeschlossen"
},
"label" : {
"manage-project-dep" : "Projektabhängigkeiten verwalten",
"setup-cred" : "Berechtigungsnachweise einrichten",
"setup-project" : "Projektdateien konfigurieren",
"create-default-package" : "Standardpaketdatei erstellen",
"no-thanks" : "Nein danke",
"create-default-project" : "Standardprojektdateien erstellen",
"show-merge-conflicts" : "Zusammenführungskonflikte anzeigen"
}
},
"clipboard" : {
"nodes" : "Knoten",
"selectNodes" : "Wählen Sie den Text oben aus, und kopieren Sie die Datei in die Zwischenablage.",
"pasteNodes" : "Knoten hier einfügen",
"importNodes" : "Knoten importieren",
"exportNodes" : "Knoten in Zwischenablage exportieren",
"importUnrecognised" : "Importierter Typ nicht erkannt:",
"importUnrecognised_plural" : "Importierte Typen nicht erkannt:",
"nodesExported" : "Knoten, die in die Zwischenablage exportiert wurden",
"nodeCopied" : "__count__ Knoten kopiert",
"nodeCopied_plural" : "__count__ Knoten kopiert",
"invalidFlow" : "Ungültiger Nachrichtenflow: __message__",
"export" : {
"selected" : "Ausgewählte Knoten",
"current" : "Aktueller Flow",
"all" : "alle Flows",
"compact" : "kompakt",
"formatted" : "formatiert",
"copy" : "In Zwischenablage exportieren"
},
"import" : {
"import" : "Importieren in",
"newFlow" : "neuer Flow"
},
"copyMessagePath" : "Pfad kopiert",
"copyMessageValue" : "Wert kopiert",
"copyMessageValue_truncated" : "Abgeschnittene Wert kopiert"
},
"deploy" : {
"deploy" : "Implementieren",
"full" : "Voll",
"fullDesc" : "Implementiert alles im Arbeitsbereich",
"modifiedFlows" : "Geänderte Flows",
"modifiedFlowsDesc" : "Implementiert nur Flows, die geänderte Knoten enthalten.",
"modifiedNodes" : "Geänderte Knoten",
"modifiedNodesDesc" : "Implementiert nur Knoten, die sich geändert haben.",
"successfulDeploy" : "Erfolgreich implementiert",
"deployFailed" : "Implementieren fehlgeschlagen: __message__",
"unusedConfigNodes" : "Sie haben einige nicht verwendete Konfigurationsknoten.",
"unusedConfigNodesLink" : "Klicken Sie hier, um sie zu sehen",
"errors" : {
"noResponse" : "Keine Antwort vom Server"
},
"confirm" : {
"button" : {
"ignore" : "Ignorieren",
"confirm" : "Implementieren bestätigen",
"review" : "Änderungen prüfen",
"cancel" : "Abbrechen",
"merge" : "Zusammenführen",
"overwrite" : "Ignorieren & implementieren"
},
"undeployedChanges" : "Sie haben nicht implementierte Änderungen.\n\nWenn Sie diese Seite verlassen, gehen diese Änderungen verloren.",
"improperlyConfigured" : "Der Arbeitsbereich enthält einige Knoten, die nicht ordnungsgemäß konfiguriert sind:",
"unknown" : "Der Arbeitsbereich enthält einige unbekannte Knotentypen:",
"confirm" : "Sind Sie sicher, dass Sie implementieren möchten?",
"doNotWarn" : "warnen Sie nicht noch einmal.",
"conflict" : "Auf dem Server wird eine aktuellere Gruppe von Datenflüssen ausgeführt.",
"backgroundUpdate" : "Die Datenflüsse auf dem Server wurden aktualisiert.",
"conflictChecking" : "Überprüfen Sie, ob die Änderungen automatisch gemischt werden können.",
"conflictAutoMerge" : "Die Änderungen enthalten keine Konflikte und können automatisch gemischt werden.",
"conflictManualMerge" : "Zu den Änderungen gehören Konflikte, die aufgelöst werden müssen, bevor sie implementiert werden können.",
"plusNMore" : "+ __count__ mehr"
}
},
"diff" : {
"unresolvedCount" : "__count__ unaufgelöster Konflikt",
"unresolvedCount_plural" : "__count__ unaufgelöste Konflikte",
"globalNodes" : "Globale Knoten",
"flowProperties" : "Flow-Eigenschaften",
"type" : {
"added" : "hinzugefügt",
"changed" : "geändert",
"unchanged" : "unverändert",
"deleted" : "gelöscht",
"flowDeleted" : "Flow gelöscht",
"flowAdded" : "Flow hinzugefügt",
"movedTo" : "verschoben zu __id__",
"movedFrom" : "verschoben von __id__"
},
"nodeCount" : "__count__, Knoten",
"nodeCount_plural" : "__count__-Knoten",
"local" : "Lokale Änderungen",
"remote" : "Ferne Änderungen",
"reviewChanges" : "Änderungen prüfen",
"noBinaryFileShowed" : "Der Inhalt der Binärdatei kann nicht angezeigt",
"viewCommitDiff" : "Änderungen festschreiben",
"compareChanges" : "Änderungen vergleichen",
"saveConflict" : "Konfliktlösung speichern",
"conflictHeader" : "<span> __resolved__ </span> von <span> __unresolved__ </span> -Konflikten behoben",
"commonVersionError" : "Allgemeine Version enthält keine gültige JSON-Datei:",
"oldVersionError" : "Alte Version enthält keine gültige JSON-Datei:",
"newVersionError" : "Neue Version enthält keine gültige JSON-Datei:"
},
"subflow" : {
"editSubflow" : "Flowschablone bearbeiten: __name__",
"edit" : "Flowsschablone bearbeiten",
"subflowInstances" : "Es ist __count__ Instanz dieser Subflow-Vorlage vorhanden.",
"subflowInstances_plural" : "Es gibt __count__ Instanzen dieser Subflow-Vorlage.",
"editSubflowProperties" : "Eigenschaften bearbeiten",
"input" : "Eingaben:",
"output" : "Ausgaben:",
"deleteSubflow" : "Subflow löschen",
"info" : "Beschreibung",
"category" : "Kategorie",
"format" : "Markdown-Format",
"errors" : {
"noNodesSelected" : "<strong> Subflow kann nicht erstellt werden </strong>: Es wurden keine Knoten ausgewählt.",
"multipleInputsToSelection" : "<strong> Subflow kann nicht erstellt werden </strong>: Mehrere Eingaben zur Auswahl"
}
},
"editor" : {
"configEdit" : "Bearbeiten",
"configAdd" : "Hinzufügen",
"configUpdate" : "Aktualisieren",
"configDelete" : "Löschen",
"nodesUse" : "__count__node verwendet diese Konfiguration",
"nodesUse_plural" : "__count__ -Knoten verwenden diese Konfiguration",
"addNewConfig" : "Neuen __type__config-Knoten hinzufügen",
"editNode" : "__type__ Knoten bearbeiten",
"editConfig" : "__type__config-Knoten bearbeiten",
"addNewType" : "Neuen __type__ hinzufügen ...",
"nodeProperties" : "Knoteneigenschaften",
"portLabels" : "Knoteneinstellungen",
"labelInputs" : "Eingänge",
"labelOutputs" : "Ausgänge",
"settingIcon" : "Symbol",
"noDefaultLabel" : "keine",
"defaultLabel" : "Standardbeschriftung verwenden",
"searchIcons" : "Suchsymbole",
"useDefault" : "Standardwert verwenden",
"errors" : {
"scopeChange" : "Wenn Sie den Geltungsbereich ändern, wird er für Knoten in anderen Nachrichtenflüssen, die ihn verwenden, nicht verfügbar sein."
}
},
"keyboard" : {
"title" : "Tastaturkurzbefehle",
"keyboard" : "Tastatur",
"filterActions" : "Filteraktionen",
"shortcut" : "Direktaufruf",
"scope" : "Bereich",
"unassigned" : "Nicht zugeordnet",
"global" : "global",
"workspace" : "Arbeitsbereich",
"selectAll" : "Alle Knoten auswählen",
"selectAllConnected" : "Alle verbundenen Knoten auswählen",
"addRemoveNode" : "Knoten aus Auswahl hinzufügen/entfernen",
"editSelected" : "Ausgewählten Knoten bearbeiten",
"deleteSelected" : "Ausgewählte Knoten oder ausgewählten Link löschen",
"importNode" : "Knoten importieren",
"exportNode" : "Knoten exportieren",
"nudgeNode" : "Ausgewählte Knoten verschieben (1px)",
"moveNode" : "Ausgewählte Knoten verschieben (20px)",
"toggleSidebar" : "Seitenleiste ein-/ausschalten",
"copyNode" : "Ausgewählte Knoten kopieren",
"cutNode" : "Ausgewählte Knoten ausschneiden",
"pasteNode" : "Knoten einfügen",
"undoChange" : "Letzte Änderung rückgängig machen",
"searchBox" : "Suchfeld öffnen",
"managePalette" : "Palette verwalten"
},
"library" : {
"openLibrary" : "Bibliothek öffnen ...",
"saveToLibrary" : "In Bibliothek speichern ...",
"typeLibrary" : "__type__, Bibliothek",
"unnamedType" : "Unbenannt __type__",
"exportToLibrary" : "Knoten in Bibliothek exportieren",
"dialogSaveOverwrite" : "Ein __libraryType__ mit dem Namen __libraryName__ ist bereits vorhanden. Überschreiben?",
"invalidFilename" : "Ungültiger Dateiname",
"savedNodes" : "Gespeicherte Knoten",
"savedType" : "Gespeichert __type__",
"saveFailed" : "Speichern fehlgeschlagen: __message__",
"filename" : "Name der Datei",
"folder" : "Ordner",
"filenamePlaceholder" : "Datei",
"fullFilenamePlaceholder" : "a/b/Datei",
"folderPlaceholder" : "a/b",
"breadcrumb" : "Bibliothek"
},
"palette" : {
"noInfo" : "Keine Informationen verfügbar",
"filter" : "Filterknoten",
"search" : "Suchmodule",
"addCategory" : "Neu hinzufügen ...",
"label" : {
"subflows" : "untergeordnete Nachrichtenflüsse",
"input" : "Eingabe",
"output" : "Ausgabe",
"function" : "Funktion",
"social" : "Soziale",
"storage" : "Speicher",
"analysis" : "Analyse",
"advanced" : "fortgeschritten"
},
"event" : {
"nodeAdded" : "Knoten zur Palette hinzugefügt:",
"nodeAdded_plural" : "Die Palette wurde der Palette hinzugefügt.",
"nodeRemoved" : "Knoten aus Palette entfernt:",
"nodeRemoved_plural" : "Knoten aus Palette entfernt:",
"nodeEnabled" : "Knoten aktiviert:",
"nodeEnabled_plural" : "Knoten aktiviert:",
"nodeDisabled" : "Knoten inaktiviert:",
"nodeDisabled_plural" : "Knoten inaktiviert:",
"nodeUpgraded" : "Knotenmodul __module__ aktualisiert auf Version __version__"
},
"editor" : {
"title" : "Palette verwalten",
"palette" : "Palette",
"times" : {
"seconds" : "Vor Sekunden",
"minutes" : "Minuten vor",
"minutesV" : "__count__ Minuten",
"hoursV" : "__count__ Stunde ago",
"hoursV_plural" : "__count__hours ago",
"daysV" : "__count__ Tag ago",
"daysV_plural" : "__count__ Tage",
"weeksV" : "__count__ Woche vor",
"weeksV_plural" : "__count__wochen ago",
"monthsV" : "__count__ Monat vor",
"monthsV_plural" : "__count__ Monaten",
"yearsV" : "__count__ Jahr",
"yearsV_plural" : "__count__ Jahren",
"yearMonthsV" : "____ Jahr, __count__ Monat",
"yearMonthsV_plural" : "____ Jahr, __count__ Monaten",
"yearsMonthsV" : "____ Jahre, __count__ Monat vor",
"yearsMonthsV_plural" : "____ Jahre, __count__ Monaten"
},
"nodeCount" : "__label__, Knoten",
"nodeCount_plural" : "__label__ Knoten",
"moduleCount" : "__count__ Modul verfügbar",
"moduleCount_plural" : "__count__-Module verfügbar",
"inuse" : "im Gebrauch",
"enableall" : "alle aktivieren",
"disableall" : "Alle inaktivieren",
"enable" : "aktivieren",
"disable" : "inaktivieren",
"remove" : "entfernen",
"update" : "Update auf __version__",
"updated" : "aktualisiert",
"install" : "installieren",
"installed" : "installiert",
"loading" : "Kataloge werden geladen ...",
"tab-nodes" : "Knoten",
"tab-install" : "installieren",
"sort" : "Sortierung:",
"sortAZ" : "a-z",
"sortRecent" : "kürzlich",
"more" : "+ __count__ mehr",
"errors" : {
"catalogLoadFailed" : "<p> Fehler beim Laden des Knotenkatalogs. </p> <p> Weitere Informationen finden Sie in der Browserkonsole. </p>",
"installFailed" : "<p> Installation fehlgeschlagen: __module__ </p> <p> __message__ </p> <p> Überprüfen Sie das Protokoll auf weitere Informationen. </p>",
"removeFailed" : "<p> Entfernen fehlgeschlagen: __module__ </p> <p> __message__ </p> <p> Überprüfen Sie das Protokoll auf weitere Informationen. </p>",
"updateFailed" : "<p> Aktualisierung fehlgeschlagen: __module__ </p> <p> __message__ </p> <p> Überprüfen Sie das Protokoll auf weitere Informationen. </p>",
"enableFailed" : "<p> Fehlgeschlagene Aktivierung: __module__ </p> <p> __message__ </p> <p> Überprüfen Sie das Protokoll auf weitere Informationen. </p>",
"disableFailed" : "<p> Inaktivieren fehlgeschlagen: __module__ </p> <p> __message__ </p> <p> Überprüfen Sie das Protokoll auf weitere Informationen. </p>"
},
"confirm" : {
"install" : {
"body" : "<p> Installieren von '__module__' </p> <p> Vor der Installation von lesen Sie bitte die Dokumentation des Knotens. Einige Knoten haben Abhängigkeiten, die nicht automatisch aufgelöst werden können und einen Neustart von 'Node-RED' erfordern. </p>",
"title" : "Knoten installieren"
},
"remove" : {
"body" : "<p> Entfernen von '__module__' </p> <p>-Der Knoten deinstalliert ihn aus Node-RED. Der Knoten kann weiterhin Ressourcen verwenden, bis Node-RED erneut gestartet wird. </p>",
"title" : "Knoten entfernen"
},
"update" : {
"body" : "<p> Aktualisieren von '__module__' </p> <p> Für die Aktualisierung des Knotens ist ein Neustart von 'Node-RED' erforderlich, damit die Aktualisierung abgeschlossen werden kann. Dies muss manuell geschehen. </p>",
"title" : "Knoten aktualisieren"
},
"cannotUpdate" : {
"body" : "Es ist eine Aktualisierung für diesen Knoten verfügbar, aber sie ist nicht an einer Position installiert, die vom Palettenmanager aktualisiert werden kann. <br/> <br/> Weitere Informationen zum Aktualisieren dieses Knotens finden Sie in der Dokumentation."
},
"button" : {
"review" : "Knoteninformationen öffnen",
"install" : "installieren",
"remove" : "Entfernen",
"update" : "Aktualisieren"
}
}
}
},
"sidebar" : {
"info" : {
"name" : "Knoteninformationen",
"tabName" : "Name",
"label" : "info",
"node" : "Knoten",
"type" : "Typ",
"id" : "ID",
"status" : "Status",
"enabled" : "Aktiviert",
"disabled" : "Inaktiviert",
"subflow" : "Subflow",
"instances" : "Exemplare",
"properties" : "Eigenschaften",
"info" : "Informationen",
"blank" : "leer",
"null" : "null",
"showMore" : "Weitere anzeigen",
"showLess" : "Weniger anzeigen",
"flow" : "Flow",
"selection" : "Auswahl",
"nodes" : "__count__ Knoten",
"flowDesc" : "Beschreibung des Flows",
"subflowDesc" : "Beschreibung des Subflows",
"nodeHelp" : "Knotenhilfe",
"none" : "Keine",
"arrayItems" : "__count__ items",
"showTips" : "Sie können die Tipps in der Anzeige \"Einstellungen\" öffnen."
},
"config" : {
"name" : "Konfigurationsknoten",
"label" : "Konfiguration",
"global" : "Bei allen Flows",
"none" : "keine",
"subflows" : "Subflows",
"flows" : "Flows",
"filterUnused" : "Nicht verwendet",
"filterAll" : "alle",
"filtered" : "__count__ verdeckt"
},
"context" : {
"name" : "Kontextdaten",
"label" : "Kontext",
"none" : "keine ausgewählt",
"refresh" : "Aktualisierung zum Laden",
"empty" : "leer",
"node" : "Knoten",
"flow" : "Flow",
"global" : "Global"
},
"palette" : {
"name" : "Palettenverwaltung",
"label" : "Palette"
},
"project" : {
"label" : "Projekt",
"name" : "Projekt",
"description" : "Beschreibung",
"dependencies" : "Abhängigkeiten",
"settings" : "Einstellungen",
"noSummaryAvailable" : "Keine Zusammenfassung verfügbar",
"editDescription" : "Projektbeschreibung bearbeiten",
"editDependencies" : "Projektabhängigkeiten bearbeiten",
"editReadme" : "README.md bearbeiten",
"projectSettings" : {
"edit" : "bearbeiten",
"none" : "Keine",
"install" : "installieren",
"removeFromProject" : "Aus Projekt entfernen",
"addToProject" : "zu Projekt hinzufügen",
"files" : "Dateien",
"flow" : "Flow",
"credentials" : "Berechtigungsnachweis",
"invalidEncryptionKey" : "Ungültiger Chiffrierschlüssel",
"encryptionEnabled" : "Verschlüsselung aktiviert",
"encryptionDisabled" : "Verschlüsselung inaktiviert",
"setTheEncryptionKey" : "Legen Sie den Verschlüsselungsschlüssel fest:",
"resetTheEncryptionKey" : "Setzt den Verschlüsselungsschlüssel zurück:",
"changeTheEncryptionKey" : "Ändern Sie den Verschlüsselungsschlüssel:",
"currentKey" : "Aktueller Schlüssel",
"newKey" : "Neuer Schlüssel",
"credentialsAlert" : "Dadurch werden alle vorhandenen Berechtigungsnachweise gelöscht.",
"versionControl" : "Versionssteuerung",
"branches" : "Verzweigungen",
"noBranches" : "Keine Verzweigungen",
"deleteConfirm" : "Sind Sie sicher, dass Sie die lokale Verzweigung '__name__' löschen wollen? Dies kann nicht rückgängig gemacht werden.",
"unmergedConfirm" : "Die lokale Verzweigung '__name__' enthält nicht zusammengeführte Änderungen, die verloren gehen. Sind Sie sicher, dass Sie ihn löschen möchten?",
"deleteUnmergedBranch" : "Nicht zusammengeführte Verzweigung löschen",
"gitRemotes" : "Git Remotes",
"addRemote" : "ferne hinzufügen",
"addRemote2" : "Ferne hinzufügen",
"remoteName" : "Ferner Name",
"nameRule" : "Darf nur A-Z 0-9 _ enthalten.",
"url" : "URL",
"urlRule" : "https://, ssh:// oder file://",
"urlRule2" : "Geben Sie den Benutzernamen/das Kennwort nicht in die URL ein.",
"noRemotes" : "Keine Remotes",
"deleteRemoteConfrim" : "Sind Sie sicher, dass Sie den fernen '__name__' löschen möchten?",
"deleteRemote" : "Ferne löschen"
},
"userSettings" : {
"committerDetail" : "Committer-Details",
"committerTip" : "Leer Wert für Systemstandardwert belassen",
"userName" : "Benutzername",
"email" : "E-Mail",
"sshKeys" : "SSH-Schlüssel",
"sshKeysTip" : "Ermöglicht es Ihnen, sichere Verbindungen zu fernen Git-Repositorys zu erstellen.",
"add" : "Schlüssel hinzufügen",
"addSshKey" : "SSH-Schlüssel hinzufügen",
"addSshKeyTip" : "Ein neues öffentungs-/privates Schlüsselpaar generieren",
"name" : "Name",
"nameRule" : "Darf nur A-Z 0-9 _ enthalten.",
"passphrase" : "Kennphrase",
"passphraseShort" : "Kennphrase zu kurz",
"optional" : "Optional",
"cancel" : "Abbrechen",
"generate" : "Schlüssel generieren",
"noSshKeys" : "Keine SSH-Schlüssel",
"copyPublicKey" : "Öffentlichen Schlüssel in Zwischenablage kopieren",
"delete" : "Löschtaste",
"gitConfig" : "Git-Konfiguration",
"deleteConfirm" : "Sind Sie sicher, dass der SSH-Schlüssel __name__ gelöscht werden soll? Dies kann nicht rückgängig gemacht werden."
},
"versionControl" : {
"unstagedChanges" : "Nicht zwischengespeicherte Änderungen",
"stagedChanges" : "Gespeichte Änderungen",
"resolveConflicts" : "Konflikte auflösen",
"head" : "HEAD",
"staged" : "Zwischengelagert",
"unstaged" : "Nicht zwischengespeichert",
"local" : "Lokal",
"remote" : "Fern",
"revert" : "Sind Sie sicher, dass die Änderungen auf '__file__' zurückgesetzt werden sollen? Dies kann nicht rückgängig gemacht werden.",
"revertChanges" : "Änderungen zurücksetzen",
"localChanges" : "Lokale Änderungen",
"none" : "Keine",
"conflictResolve" : "Alle Konflikte wurden aufgelöst. Festschreiben der Änderungen, um den Mischvorgang abzuschließen.",
"localFiles" : "Lokale Dateien",
"all" : "alle",
"unmergedChanges" : "Nicht zusammengeführte Änderungen",
"abortMerge" : "Zusammenführen abbrechen",
"commit" : "Festschreiben",
"changeToCommit" : "Änderungen beim Festschreiben",
"commitPlaceholder" : "Geben Sie Ihre Festschreibungsnachricht",
"cancelCapital" : "Abbrechen",
"commitCapital" : "Festschreiben",
"commitHistory" : "Protokoll festschreiben",
"branch" : "Verzweigung:",
"moreCommits" : " weitere Commit (s)",
"changeLocalBranch" : "Lokale Verzweigung ändern",
"createBranchPlaceholder" : "Verzweigung suchen oder erstellen",
"upstream" : "Upstream",
"localOverwrite" : "Sie haben lokale Änderungen, die überschrieben werden, indem Sie die Verzweigung ändern. Sie müssen diese Änderungen zuerst festschreiben oder rückgängig machen.",
"manageRemoteBranch" : "Ferne Verzweigung verwalten",
"unableToAccess" : "Zugriff auf fernes Repository nicht möglich",
"retry" : "Retry",
"setUpstreamBranch" : "Als vorgeschaltete Verzweigung festlegen",
"createRemoteBranchPlaceholder" : "Ferne Verzweigung suchen oder erstellen",
"trackedUpstreamBranch" : "Die erstellte Verzweigung wird als überwachte Upstream-Verzweigung festgelegt.",
"selectUpstreamBranch" : "Die Verzweigung wird erstellt. Wählen Sie diese Option aus, um sie als überwachte Upstream-Verzweigung festzulegen",
"pushFailed" : "Push ist fehlgeschlagen, da die ferne Commit-COMMs-COMMs (COMM Zuerst ziehen und mischen, dann erneut drücken.",
"push" : "Push",
"pull" : "Pull",
"unablePull" : "<p> Ferne Änderungen können nicht gezogen werden. Die nicht zwischengespeicherten lokalen Änderungen werden überschrieben. </p> <p> Die Änderungen festschreiben und die Anforderung wiederholen. </p>",
"showUnstagedChanges" : "Nicht zwischengespeicherte Änderungen anzeigen",
"connectionFailed" : "Verbindung zum fernen Repository konnte nicht hergestellt werden: ",
"pullUnrelatedHistory" : "<p> Das ferne Protokoll der Festschreibungen hat einen nicht zugehörigen Verlauf. </p> <p> Sind Sie sicher, dass Sie die Änderungen in Ihr lokales Repository ziehen möchten? </p>",
"pullChanges" : "Änderungen extrahieren",
"history" : "Verlauf",
"daysAgo" : "__count__ Tag ago",
"daysAgo_plural" : "__count__ Tage",
"hoursAgo" : "__count__ Stunde ago",
"hoursAgo_plural" : "__count__hours ago",
"minsAgo" : "__count__ min ago",
"minsAgo_plural" : "__count__ mins ago",
"secondsAgo" : "Sekunden zurück",
"notTracking" : "Ihre lokale Verzweigung verfolgt derzeit keine ferne Verzweigung.",
"statusUnmergedChanged" : "In Ihrem Repository sind nicht zusammengeführte Änderungen vorhanden. Sie müssen die Konflikte beheben und das Ergebnis festschreiben.",
"repositoryUpToDate" : "Ihr Repository ist auf dem neuesten Stand.",
"commitsAhead" : "Ihr Repository ist __count__commit vor der fernen. Sie können diese Festschreibung jetzt übertragen.",
"commitsAhead_plural" : "Ihr Repository ist __count__ ist vor der fernen Commits festgeschrieben. Sie können diese Commits jetzt verschieben.",
"commitsBehind" : "Ihr Projektarchiv ist __count__ hinter der Fernbedienung. Sie können diese Festschreibung jetzt extrahieren.",
"commitsBehind_plural" : "Ihr Repository ist __count__ ist hinter der Fernbedienung festgeschrieben. Sie können diese Commits jetzt extrahieren.",
"commitsAheadAndBehind1" : "Ihr Projektarchiv ist __count__commit hinter und ",
"commitsAheadAndBehind1_plural" : "Ihr Repository ist __count__ schreibt sich zurück und ",
"commitsAheadAndBehind2" : "__count__ wird vor der fernen festgeschrieben. ",
"commitsAheadAndBehind2_plural" : "__count__ schreibt vor der fernen Funktion fest. ",
"commitsAheadAndBehind3" : "Sie müssen die ferne Festschreibung nach unten ziehen, bevor Sie sie drücken.",
"commitsAheadAndBehind3_plural" : "Sie müssen die fernen Festschreibungen vor dem Pusdrücken zurückziehen."
}
}
},
"typedInput" : {
"type" : {
"str" : "Zeichenfolge",
"num" : "Anzahl",
"re" : "Regulärer Ausdruck",
"bool" : "boolean",
"json" : "JSON",
"bin" : "Puffer",
"date" : "Zeitmarke",
"jsonata" : "Ausdruck",
"env" : "env, Variable"
}
},
"editableList" : {
"add" : "hinzufügen"
},
"search" : {
"empty" : "Keine Übereinstimmungen gefunden",
"addNode" : "Knoten hinzufügen ..."
},
"expressionEditor" : {
"functions" : "Funktionen",
"functionReference" : "Funktionsreferenz",
"insert" : "Einfügen",
"title" : "JSONata-Ausdruckseditor",
"test" : "Test",
"data" : "Beispielnachricht",
"result" : "Ergebnis",
"format" : "Formatiere Ausdruck",
"compatMode" : "Kompatibilitätsmodus aktiviert",
"compatModeDesc" : "<h3> JSONata-Kompatibilitätsmodus </h3> <p> Der aktuelle Ausdruck scheint immer noch auf <code> msg </code> zu verweisen, so dass er im Kompatibilitätsmodus ausgewertet wird. Aktualisieren Sie den Ausdruck so, dass <code> msg </code> nicht verwendet wird, da dieser Modus in der Zukunft entfernt wird. </p> <p> Wenn die JSONata-Unterstützung zuerst zu Node-RED hinzugefügt wurde, ist der Ausdruck erforderlich, um auf das Objekt <code> msg </code> zu verweisen. Beispiel: <code> msg.payload </code> würde für den Zugriff auf die Nutzdaten verwendet. </p> <p> Das ist nicht mehr erforderlich, da der Ausdruck direkt anhand der Nachricht ausgewertet wird. Um auf die Nutzdaten zugreifen zu können, muss der Ausdruck nur <code> Nutzdaten </code> sein. </p>",
"noMatch" : "Kein übereinstimmende Ergebnisse",
"errors" : {
"invalid-expr" : "Ungültiger JSONata-Ausdruck:\n __message__",
"invalid-msg" : "Ungültiges Beispiel für JSON-Nachricht:\n __message__",
"context-unsupported" : "Kontextfunktionen können nicht getestet werden\n $flowContext oder $globalContext",
"eval" : "Fehler beim Auswerten des Ausdrucks\n __message__"
}
},
"jsEditor" : {
"title" : "JavaScript-Editor"
},
"jsonEditor" : {
"title" : "JSON-Editor",
"format" : "Formatiere JSON"
},
"markdownEditor" : {
"title" : "Markdown-Editor"
},
"bufferEditor" : {
"title" : "Puffereditor",
"modeString" : "Als UTF-8-Zeichenfolge bearbeiten",
"modeArray" : "Als JSON-Array bearbeiten",
"modeDesc" : "<h3> Puffereditor </h3> <p> Der Puffertyp wird als JSON-Array mit Bytewerten gespeichert. Der Editor versucht, den eingegebenen Wert als JSON-Array zu parsen. Wenn es sich nicht um ein gültiges JSON handelt, wird es als UTF-8-Zeichenfolge behandelt und in ein Array der einzelnen Zeichencodepunkte konvertiert. </p> <p> Beispiel: Der Wert <code> Hello World </code> wird in das JSON-Array konvertiert: <pre> [ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100] </pre> </p>"
},
"projects" : {
"config-git" : "Git-Client konfigurieren",
"welcome" : {
"hello" : "Hallo! Wir haben 'Projekte' in 'Node-RED' eingeführt.",
"desc0" : "Dies ist eine neue Methode für die Verwaltung Ihrer Datenflowsdateien und die Versionssteuerung Ihrer Abläufe.",
"desc1" : "Um zu beginnen, können Sie Ihr erstes Projekt erstellen oder ein vorhandenes Projekt aus einem Git-Repository klonen.",
"desc2" : "Wenn Sie sich nicht sicher sind, können Sie das jetzt überspringen. Sie können immer noch Ihr erstes Projekt aus dem 'Projects' -Menü erstellen.",
"create" : "Projekt erstellen",
"clone" : "Repository klonen",
"not-right-now" : "Jetzt nicht mehr"
},
"git-config" : {
"setup" : "Konfigurieren Sie Ihren Versionssteuerungsclient.",
"desc0" : "Node-RED verwendet das Open-Source-Tool Git für die Versionssteuerung. Es protokolliert Änderungen in Ihren Projektdateien und ermöglicht es Ihnen, sie in ferne Repositorys zu übertragen.",
"desc1" : "Wenn Sie eine Reihe von Änderungen festschreiben, werden die Änderungen mit einem Benutzernamen und einer E-Mail-Adresse von GIT-Datensätzen vorgenommen. Der Benutzername kann alles sein, was Sie wollen-es muss nicht Ihr richtiger Name sein.",
"desc2" : "Ihr Git-Client ist bereits mit den unten stehenden Details konfiguriert.",
"desc3" : "Sie können diese Einstellungen später unter der Registerkarte \"Git config\" des Einstellungsdialogs ändern.",
"username" : "Benutzername",
"email" : "E-Mail"
},
"project-details" : {
"create" : "Erstellen Sie Ihr Projekt.",
"desc0" : "Ein Projekt wird als Git-Repository verwaltet. Es ist wesentlich einfacher, Ihre Abläufe mit anderen zu teilen und an ihnen zu arbeiten.",
"desc1" : "Sie können mehrere Projekte erstellen und schnell zwischen den Projekten im Editor wechseln.",
"desc2" : "Zu Beginn benötigt Ihr Projekt einen Namen und eine optionale Beschreibung.",
"already-exists" : "Das Projekt ist bereits vorhanden",
"must-contain" : "Darf nur A-Z 0-9 _ enthalten.",
"project-name" : "Projektname",
"desc" : "Beschreibung",
"opt" : "Optional"
},
"clone-project" : {
"clone" : "Projekt klonen",
"desc0" : "Wenn Sie bereits über ein Git-Repository verfügen, das ein Projekt enthält, können Sie es klonen, um es zu starten.",
"already-exists" : "Das Projekt ist bereits vorhanden",
"must-contain" : "Darf nur A-Z 0-9 _ enthalten.",
"project-name" : "Projektname",
"no-info-in-url" : "Geben Sie den Benutzernamen/das Kennwort nicht in die URL ein.",
"git-url" : "Git-Repository-URL",
"protocols" : "https://, ssh:// oder file://",
"auth-failed" : "Authentifizierung fehlgeschlagen",
"username" : "Benutzername",
"passwd" : "Kennwort",
"ssh-key" : "SSH-Schlüssel",
"passphrase" : "Kennphrase",
"ssh-key-desc" : "Bevor Sie ein Repository über ssh klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zu zugreifen.",
"ssh-key-add" : "Einen ssh-Schlüssel hinzufügen",
"credential-key" : "Verschlüsselungsschlüssel für Berechtigungsnachweise",
"cant-get-ssh-key" : "Fehler! Der ausgewählte SSH-Schlüsselpfad kann nicht abgerufen werden.",
"already-exists2" : "bereits vorhanden",
"git-error" : "Git-Fehler",
"connection-failed" : "Verbindung fehlgeschlagen",
"not-git-repo" : "Kein Git-Repository",
"repo-not-found" : "Repository nicht gefunden"
},
"default-files" : {
"create" : "Erstellen Sie Ihre Projektdateien.",
"desc0" : "Ein Projekt enthält Ihre Flow-Dateien, eine README-Datei und eine package.json-Datei.",
"desc1" : "Es kann alle anderen Dateien enthalten, die im Git-Repository verwaltet werden sollen.",
"desc2" : "Ihre vorhandenen Flow- und Berechtigungsnachweisdateien werden in das Projekt kopiert.",
"flow-file" : "Flow-Datei",
"credentials-file" : "Berechtigungsnachweisdatei"
},
"encryption-config" : {
"setup" : "Setup der Verschlüsselung Ihrer Berechtigungsnachweisdatei",
"desc0" : "Die Datei mit den Datenflowsberechtigungsnachweisen kann verschlüsselt werden, um ihren Inhalt sicher zu halten.",
"desc1" : "Wenn Sie diese Berechtigungsnachweise in einem öffentlichen Git-Repository speichern möchten, müssen Sie sie verschlüsseln, indem Sie einen geheimen Schlüsselausdruck bereitstellen.",
"desc2" : "Die Datei mit den Datenflowberechtigungsnachweisen ist derzeit nicht verschlüsselt.",
"desc3" : "Das heißt, ihr Inhalt, wie z. B. Kennwörter und Zugriffstokens, kann von jedem mit Zugriff auf die Datei gelesen werden.",
"desc4" : "Wenn Sie diese Berechtigungsnachweise in einem öffentlichen Git-Repository speichern möchten, müssen Sie sie verschlüsseln, indem Sie einen geheimen Schlüsselausdruck bereitstellen.",
"desc5" : "Ihre Datei mit den Datenflowberechtigungsnachweisen wird derzeit mit der Eigenschaft credentialSecret aus Ihrer Einstellungsdatei als Schlüssel verschlüsselt.",
"desc6" : "Die Datei mit den Datenflowberechtigungsnachweisen wird derzeit mit einem vom System generierten Schlüssel verschlüsselt. Sie sollten einen neuen geheimen Schlüssel für dieses Projekt angeben.",
"desc7" : "Der Schlüssel wird separat von den Projektdateien gespeichert. Sie müssen den Schlüssel angeben, damit dieses Projekt in einer anderen Instanz von Node-RED verwendet werden kann.",
"credentials" : "Berechtigungsnachweis",
"enable" : "Verschlüsselung aktivieren",
"disable" : "Verschlüsselung inaktivieren",
"disabled" : "inaktiviert",
"copy" : "Vorhandenen Schlüssel kopieren",
"use-custom" : "Angepasster Schlüssel verwenden",
"desc8" : "Die Datei mit den Berechtigungsnachweisen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden.",
"create-project-files" : "Projektdateien erstellen",
"create-project" : "Projekt erstellen",
"already-exists" : "bereits vorhanden",
"git-error" : "Git-Fehler",
"git-auth-error" : "git-auth-Fehler"
},
"create-success" : {
"success" : "Sie haben Ihr erstes Projekt erfolgreich erstellt!",
"desc0" : "Sie können jetzt weiterhin Node-RED verwenden, wie Sie es immer haben.",
"desc1" : "Auf der Registerkarte \"info\" in der Seitenleiste wird angezeigt, was Ihr aktuelles aktives Projekt ist. Die Schaltfläche neben dem Namen kann für den Zugriff auf die Sicht 'Projekteinstellungen' verwendet werden.",
"desc2" : "Die Registerkarte 'Verlauf' in der Seitenleiste kann verwendet werden, um Dateien anzuzeigen, die sich in Ihrem Projekt geändert haben, und sie festzuschreiben. Es zeigt Ihnen eine vollständige Historie Ihrer Commits an und ermöglicht es Ihnen, Ihre Änderungen in ein fernes Repository zu übertragen."
},
"create" : {
"projects" : "Projekte",
"already-exists" : "Das Projekt ist bereits vorhanden",
"must-contain" : "Darf nur A-Z 0-9 _ enthalten.",
"no-info-in-url" : "Geben Sie den Benutzernamen/das Kennwort nicht in die URL ein.",
"open" : "Projekt öffnen",
"create" : "Projekt erstellen",
"clone" : "Repository klonen",
"project-name" : "Projektname",
"desc" : "Beschreibung",
"opt" : "Optional",
"flow-file" : "Flow-Datei",
"credentials" : "Berechtigungsnachweis",
"enable-encryption" : "Verschlüsselung aktivieren",
"disable-encryption" : "Verschlüsselung inaktivieren",
"encryption-key" : "Chiffrierschlüssel",
"desc0" : "Eine Phrase, mit der Sie Ihre Berechtigungsnachweise schützen",
"desc1" : "Die Datei mit den Berechtigungsnachweisen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden.",
"git-url" : "Git-Repository-URL",
"protocols" : "https://, ssh:// oder file://",
"auth-failed" : "Authentifizierung fehlgeschlagen",
"username" : "Benutzername",
"password" : "Kennwort",
"ssh-key" : "SSH-Schlüssel",
"passphrase" : "Kennphrase",
"desc2" : "Bevor Sie ein Repository über ssh klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zu zugreifen.",
"add-ssh-key" : "Einen ssh-Schlüssel hinzufügen",
"credentials-encryption-key" : "Verschlüsselungsschlüssel für Berechtigungsnachweise",
"already-exists-2" : "bereits vorhanden",
"git-error" : "Git-Fehler",
"con-failed" : "Verbindung fehlgeschlagen",
"not-git" : "Kein Git-Repository",
"no-resource" : "Repository nicht gefunden",
"cant-get-ssh-key-path" : "Fehler! Der ausgewählte SSH-Schlüsselpfad kann nicht abgerufen werden.",
"unexpected_error" : "unerwarteter_Fehler"
},
"delete" : {
"confirm" : "Sind Sie sicher, dass Sie dieses Projekt löschen möchten?"
},
"create-project-list" : {
"search" : "Projekte durchsuchen",
"current" : "aktuell"
},
"require-clean" : {
"confirm" : "<p> Sie haben nicht implementierte Änderungen verloren, die verloren gehen. </p> <p> Möchten Sie fortfahren? </p>"
},
"send-req" : {
"auth-req" : "Authentifizierung für Repository erforderlich",
"username" : "Benutzername",
"password" : "Kennwort",
"passphrase" : "Kennphrase",
"retry" : "Retry",
"update-failed" : "Fehler beim Aktualisieren der Auth.",
"unhandled" : "Nicht behandelte Fehlerantwort"
},
"create-branch-list" : {
"invalid" : "Ungültige Verzweigung",
"create" : "Verzweigung erstellen",
"current" : "aktuell"
},
"create-default-file-set" : {
"no-active" : "Standarddatei kann ohne aktives Projekt nicht erstellt werden",
"no-empty" : "Für ein nicht leeres Projekt kann keine Standarddatei erstellt werden.",
"git-error" : "Git-Fehler"
},
"errors" : {
"no-username-email" : "Ihr Git-Client ist nicht mit einem Benutzernamen/einer E-Mail konfiguriert.",
"unexpected" : "Es ist ein unerwarteter Fehler aufgetreten",
"code" : "code"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"info" : {
"tip0" : "Sie können die ausgewählten Knoten oder Verbindungen mit {{ core:delete-selection }} entfernen.",
"tip1" : "Suche nach Knoten mit {{ core:search }}",
"tip2" : "{{ core:toggle-sidebar }} schaltet die Ansicht dieser Seitenleiste ein.",
"tip3" : "Sie können Ihre Palette von Knoten mit {{ core:manage-palette }} verwalten.",
"tip4" : "Ihre Flow-Konfigurationsknoten werden in der Seitenleiste angezeigt. Es kann über das Menü oder mit {{ core:show-config-tab }} aufgerufen werden.",
"tip5" : "Aktiviert oder inaktiviert diese Tipps von der Option in den Einstellungen",
"tip6" : "Verschieben Sie die ausgewählten Knoten mit Hilfe der [left] [up] [down] und [right] Tasten. Halten Sie [Shift] gedrückt, um das Fenster weiter zu schieben",
"tip7" : "Wenn Sie einen Knoten auf eine Verbindung ziehen, wird er in die Verbindung eingefügt.",
"tip8" : "Die ausgewählten Knoten exportieren oder die aktuelle Registerkarte mit {{ core:show-export-dialog }}",
"tip9" : "Importieren Sie einen Flow, indem Sie sein JSON in den Editor ziehen oder mit {{ core:show-import-dialog }}.",
"tip10" : "[Umschalt] [Klicken] und ziehen Sie auf einen Knotenanschluss, um alle angeschlossenen Verbindungen oder nur die ausgewählte zu verschieben.",
"tip11" : "Die Registerkarte \"Info\" mit {{ core:show-info-tab }} oder der Registerkarte \"Debug\" mit {{ core:show-debug-tab }} anzeigen",
"tip12" : "[ctrl] [Klicken] in den Arbeitsbereich, um den Schnellhinzufügedialog zu öffnen.",
"tip13" : "Halten Sie [ctrl] gedrückt, wenn Sie auf einem Knotenanschluss klicken, um eine Schnellverbindung zu aktivieren.",
"tip14" : "Halten Sie [Umschalt] gedrückt, wenn Sie auf einen Knoten klicken, um auch alle verbundenen Knoten auszuwählen.",
"tip15" : "Halten Sie [ctrl] gedrückt, wenn Sie auf einen Knoten klicken, um ihn aus der aktuellen Auswahl hinzuzufügen oder zu entfernen.",
"tip16" : "Indexzungen wechseln mit {{ core:show-previous-tab }} und {{ core:show-next-tab }}",
"tip17" : "Sie können die Änderungen im Editierrahmen des Knotens mit {{ core:confirm-edit-tray }} bestätigen oder sie mit {{ core:cancel-edit-tray }} abbrechen.",
"tip18" : "Durch Drücken von {{ core:edit-selected-node }} wird der erste Knoten in der aktuellen Auswahl bearbeitet."
}
}

View File

@@ -0,0 +1,222 @@
{
"$string" : {
"args" : "arg",
"desc" : "Transformiert den Parameter *arg* in eine Zeichenfolge mit den folgenden Transformationsregeln:\n\n -Zeichenfolgen bleiben unverändert\n -Funktionen werden in eine leere Zeichenfolge konvertiert\n -Numerische Unendlichkeit und NaN lösen einen Fehler aus, da sie nicht als JSON-Nummer dargestellt werden können.\n -Alle anderen Werte werden mit Hilfe der Funktion 'JSON.stringify' in eine JSON-Zeichenfolge konvertiert."
},
"$length" : {
"args" : "str",
"desc" : "Gibt die Anzahl der Zeichen in der Zeichenfolge `str` zurück. Es wird ein Fehler ausgelöst, wenn `str` keine Zeichenfolge ist."
},
"$substring" : {
"args" : "str, start [, länge]",
"desc" : "Gibt eine Zeichenfolge zurück, die die Zeichen im ersten Parameter `str` beginnend bei Position `start` (Null-Offset) enthält. Wenn \"length\" angegeben ist, enthält die Unterzeichenfolge maximal \"Länge\" Zeichen. Wenn `start` negativ ist, gibt es die Anzahl der Zeichen am Ende von `str` an."
},
"$substringBefore" : {
"args" : "str, chars",
"desc" : "Gibt die Unterzeichenfolge vor dem ersten Auftreten der Zeichenfolge `chars` in `str` zurück. Falls `str` nicht `chars` enthält, gibt es `str` zurück."
},
"$substringAfter" : {
"args" : "str, chars",
"desc" : "Gibt die Unterzeichenfolge nach dem ersten Auftreten der Zeichenfolge `chars` in `str` zurück. Falls `str` nicht `chars` enthält, gibt es `str` zurück."
},
"$uppercase" : {
"args" : "str",
"desc" : "Gibt eine Zeichenfolge mit allen Zeichen von `str` zurück, die in Großbuchstaben konvertiert werden."
},
"$lowercase" : {
"args" : "str",
"desc" : "Gibt eine Zeichenfolge mit allen Zeichen von `str` in Kleinbuchstaben zurück."
},
"$trim" : {
"args" : "str",
"desc" : "Normalisiert und trimmt alle Leerzeichen in `str` durch Anwenden der folgenden Schritte:\n\n -Alle Tabulatorstopps, Wagenrückläufe und Zeilenvorschübe werden durch Leerzeichen ersetzt.\n-Zusammenhängende Folgen von Räumen werden auf einen einzigen Raum reduziert.\n-Trailing und führende Plätze werden entfernt.\n\n Wenn 'str' nicht angegeben ist (d. h. Diese Funktion wird ohne Argumente aufgerufen), dann wird der Kontextwert als Wert von `str` verwendet. Es wird ein Fehler ausgelöst, wenn `str` keine Zeichenfolge ist."
},
"$contains" : {
"args" : "str, Muster",
"desc" : "Gibt `true` zurück, wenn `str` durch `Muster` abgeglichen wird, sonst gibt es `false` zurück. Wenn 'str' nicht angegeben ist (d. h. Diese Funktion wird mit einem Argument aufgerufen), dann wird der Kontextwert als Wert von `str` verwendet. Der Parameter 'Muster' kann entweder eine Zeichenfolge oder ein regulärer Ausdruck sein."
},
"$split" : {
"args" : "str [, Trennzeichen] [, Grenzwert]",
"desc" : "Teilt den Parameter 'str' in einem Array mit Unterzeichenfolgen. Es ist ein Fehler, wenn `str` keine Zeichenfolge ist. Der optionale Parameter 'Trennzeichen' gibt die Zeichen in der `str` an, um die es entweder als Zeichenfolge oder als regulärer Ausdruck geteilt werden soll. Wenn 'Trennzeichen' nicht angegeben wird, wird die leere Zeichenfolge angenommen, und `str` wird in ein Array aus einzelnen Zeichen aufgeteilt. Es handelt sich um einen Fehler, wenn `Trennzeichen' keine Zeichenfolge ist. Der optionale Parameter 'Grenzwert' ist eine Zahl, die die maximale Anzahl von Unterzeichenfolgen angibt, die in das resultierende Array eingeschlossen werden sollen. Alle zusätzlichen Unterzeichenfolgen werden gelöscht. Wenn 'Grenzwert' nicht angegeben wird, wird ' str ` vollständig geteilt, wobei die Größe des resultierenden Arrays nicht begrenzt ist. Es handelt sich um einen Fehler, wenn `Grenzwert' keine nicht negative Zahl ist."
},
"$join" : {
"args" : "array [, Trennzeichen]",
"desc" : "Verkettet ein Array von Komponentenzeichenfolgen in eine einzelne verkettete Zeichenfolge mit jeder Komponentenzeichenfolge, die durch den optionalen Parameter 'separator' getrennt ist. Es ist ein Fehler, wenn die Eingabe `Array` ein Element enthält, das keine Zeichenfolge ist. Wenn 'Trennzeichen' nicht angegeben wird, wird davon ausgegangen, dass es sich um eine leere Zeichenfolge handelt, d. h. Zwischen den Komponentenzeichenfolgen ist kein Trennzeichen vorhanden. Es handelt sich um einen Fehler, wenn `Trennzeichen' keine Zeichenfolge ist."
},
"$match" : {
"args" : "str, Muster [, Grenzwert]",
"desc" : "Wendet die Zeichenfolge `str` an den regulären Ausdruck `Muster` an und gibt ein Array von Objekten zurück, wobei jedes Objekt Informationen zu jedem Vorkommen einer Übereinstimmung in `str` enthält."
},
"$replace" : {
"args" : "str, Muster, Ersatz [, Grenzwert]",
"desc" : "Findet Vorkommen von `Muster` in `str` und ersetzt sie durch `Ersatz`.\n\nDer optionale Parameter 'Grenzwert' ist die maximale Anzahl an Ersetzungen."
},
"$now" : {
"args" : "",
"desc" : "Generiert eine Zeitmarke im ISO-8601-kompatiblen Format und gibt sie als Zeichenfolge zurück."
},
"$base64encode" : {
"args" : "Zeichenfolge",
"desc" : "Konvertiert eine ASCII-Zeichenfolge in eine Basis-64-Darstellung. Jedes Zeichen in der Zeichenfolge wird als Byte mit binären Daten behandelt. Dies setzt voraus, dass alle Zeichen in der Zeichenfolge im Bereich von 0x00 bis 0xFF liegen, der alle Zeichen in URI-codierten Zeichenfolgen enthält. Unicode-Zeichen außerhalb dieses Bereichs werden nicht unterstützt."
},
"$base64decode" : {
"args" : "Zeichenfolge",
"desc" : "Konvertiert die Basis-64-codierten Byte in eine Zeichenfolge unter Verwendung einer UTF-8-Unicode-Codepage."
},
"$number" : {
"args" : "arg",
"desc" : "Der Parameter 'arg' wird unter Verwendung der folgenden Regeln für das Casting in eine Zahl verwendet:\n\n -Zahlen bleiben unverändert\n -Zeichenfolgen, die eine Folge von Zeichen enthalten, die eine rechtliche JSON-Nummer darstellen, werden in diese Zahl konvertiert.\n -Alle anderen Werte bewirken, dass ein Fehler ausgelöst wird."
},
"$abs" : {
"args" : "Anzahl",
"desc" : "Gibt den absoluten Wert des Parameters 'Zahl' zurück."
},
"$floor" : {
"args" : "Anzahl",
"desc" : "Gibt den Wert von 'Zahl' auf die nächste ganze Zahl zurück, die kleiner oder gleich 'Zahl' ist."
},
"$ceil" : {
"args" : "Anzahl",
"desc" : "Gibt den Wert von 'Zahl' auf die nächste ganze Zahl zurück, die größer oder gleich 'Zahl' ist."
},
"$round" : {
"args" : "Zahl [, Genauigkeit]",
"desc" : "Gibt den Wert des Parameters `Zahl` zurück, der auf die Anzahl der Dezimalstellen gerundet wird, die durch den optionalen Parameter 'Genauigkeit' angegeben wird."
},
"$power" : {
"args" : "Basis, Exponent",
"desc" : "Gibt den Wert von `Basis` potenziert mit `Exponent` zurück."
},
"$sqrt" : {
"args" : "Zahl",
"desc" : "Gibt die Quadratwurzel des Werts des Parameters 'Zahl' zurück."
},
"$random" : {
"args" : "",
"desc" : "Gibt eine Pseudozufallszahl größer-gleich null und kleiner als eins zurück."
},
"$millis" : {
"args" : "",
"desc" : "Gibt die Anzahl der Millisekunden seit der Unix-Epoche (1. Januar 1970 (UTC)) als Zahl zurück. Alle Invocationen von `$millis ()` innerhalb einer Auswertung eines Ausdrucks geben alle denselben Wert zurück."
},
"$sum" : {
"args" : "Array",
"desc" : "Gibt die arithmetische Summe eines `Array` von Zahlen zurück. Es ist ein Fehler, wenn die Eingabe `Array` ein Element enthält, das keine Zahl ist."
},
"$max" : {
"args" : "Array",
"desc" : "Gibt die maximale Anzahl in einem `Array` von Zahlen zurück. Es ist ein Fehler, wenn die Eingabe `Array` ein Element enthält, das keine Zahl ist."
},
"$min" : {
"args" : "Array",
"desc" : "Gibt die minimale Zahl in einem `Array` von Zahlen zurück. Es ist ein Fehler, wenn die Eingabe `Array` ein Element enthält, das keine Zahl ist."
},
"$average" : {
"args" : "Array",
"desc" : "Gibt den Mittelwert eines `Array` von Zahlen zurück. Es ist ein Fehler, wenn die Eingabe `Array` ein Element enthält, das keine Zahl ist."
},
"$boolean" : {
"args" : "arg",
"desc" : "Castet das Argument mit den folgenden Regeln in einen Booleschen Wert:\n\n -` Boolean ': nicht geändert\n -` string `: leer: `false`\n -` string `: nicht leer: `true`\n -` Zahl `: ` 0 `: ` falsch `\n -` Zahl `: Nicht-Null: `true`\n -` null `: `false`\n -` array `: leer: `false`\n -` array `: enthält ein Mitglied, das auf `true` setzt: `true`\n -` array `: alle Member werden in `false` umgesetzt: `false`\n -` object `: empty: `false`\n -` object `: non-empty: `true`\n -` Funktion `: ` falsch `"
},
"$not" : {
"args" : "arg",
"desc" : "Gibt den Booleschen Wert NOT für das Argument zurück. `arg` wird zuerst in einen Booleschen Wert umgesetzt."
},
"$exists" : {
"args" : "arg",
"desc" : "Gibt den Booleschen Wert 'true' zurück, wenn der Ausdruck `arg` als Wert ausgewertet wird, oder 'false', wenn der Ausdruck nicht mit einem anderen Ausdruck übereinstimmt (z. B. ein Pfad zu einer nicht vorhandenen Feldreferenz)."
},
"$count" : {
"args" : "Array",
"desc" : "Gibt die Anzahl der Elemente in dem Array zurück."
},
"$append" : {
"args" : "Array, Array",
"desc" : "Hängen Sie zwei Arrays an."
},
"$sort" : {
"args" : "array [, Funktion]",
"desc" : "Gibt ein Array zurück, das alle Werte im Parameter 'array' enthält, aber in der Reihenfolge sortiert wird.\n\nWenn ein Vergleichsoperator 'function' angegeben wird, muss es sich um eine Funktion handeln, die zwei Parameter benötigt:\n\n` Funktion (links, rechts) `\n\nDiese Funktion wird durch den Sortieralgorithmus aufgerufen, um zwei Werte links und rechts zu vergleichen. Wenn der Wert von links nach dem Wert von rechts in der gewünschten Sortierreihenfolge platziert werden soll, muss die Funktion den Booleschen Wert 'true' zurückgeben, um einen Auslagerungsspeicher anzuzeigen. Andernfalls muss 'false' zurückgegeben werden."
},
"$reverse" : {
"args" : "Array",
"desc" : "Gibt ein Array zurück, das alle Werte aus dem Parameter 'array' enthält, aber in umgekehrter Reihenfolge."
},
"$shuffle" : {
"args" : "Array",
"desc" : "Gibt ein Array zurück, das alle Werte aus dem Parameter ` array ` enthält, aber in zufälliger Reihenfolge geschattiert ist."
},
"$zip" : {
"args" : "Array, ...",
"desc" : "Gibt ein konvolviertes (gezipptes) Array zurück, das gruppierte Arrays von Werten aus den Argumenten ` array1 ` ... ` arrayN ' aus Index 0, 1, 2 ... enthält."
},
"$keys" : {
"args" : "Objekt",
"desc" : "Gibt ein Array zurück, das die Schlüssel in dem Objekt enthält. Wenn es sich bei dem Argument um ein Array von Objekten handelt, enthält das zurückgegebene Array eine deduplizierte Liste aller Schlüssel in allen Objekten."
},
"$lookup" : {
"args" : "Objekt, Schlüssel",
"desc" : "Gibt den Wert zurück, der dem Schlüssel im Objekt zugeordnet ist. Wenn es sich bei dem ersten Argument um ein Array von Objekten handelt, werden alle Objekte im Array durchsucht, und die Werte, die mit allen Vorkommen des Schlüssels verknüpft sind, werden zurückgegeben."
},
"$spread" : {
"args" : "Objekt",
"desc" : "Teilt ein Objekt, das Schlüssel/Wert-Paare enthält, in ein Array von Objekten, von denen jedes ein einzelnes Schlüssel/Wert-Paar aus dem Eingabeobjekt hat. Wenn es sich bei dem Parameter um ein Array von Objekten handelt, enthält die resultierende Feldgruppe ein Objekt für jedes Schlüssel/Wert-Paar in jedem Objekt in der angegebenen Feldgruppe."
},
"$merge" : {
"args" : "array &lt;object&gt;",
"desc" : "Mischt ein Array von ` Objekten ` in ein einzelnes ` Objekt `, das alle Schlüssel/Wert-Paare aus jedem der Objekte in dem Eingabe-Array enthält. Wenn eines der Eingabeobjekte denselben Schlüssel enthält, enthält das zurückgegebene Objekt den Wert des letzten Objekts in der Feldgruppe. Es handelt sich um einen Fehler, wenn das Eingabe-Array ein Element enthält, das kein Objekt ist."
},
"$sift" : {
"args" : "Objekt, Funktion",
"desc" : "Gibt ein Objekt zurück, das nur die Schlüssel/Wert-Paare aus dem Parameter 'object' enthält, die die Prädikat ` funktion ' erfüllen, die als zweiter Parameter übergeben wird.\n\nDie Funktion ` function `, die als zweiter Parameter angegeben wird, muss die folgende Signatur aufweisen:\n\n` function (value [, key [, object]]) `"
},
"$each" : {
"args" : "Objekt, Funktion",
"desc" : "Gibt ein Array zurück, das die Werte enthält, die von der Funktion ` function ` zurückgegeben werden, wenn sie auf jedes Schlüssel/Wert-Paar im ` object ` angewendet werden."
},
"$map" : {
"args" : "Array, Funktion",
"desc" : "Gibt ein Array zurück, das die Ergebnisse der Anwendung des Parameters ` function ` auf jeden Wert im Parameter 'array' enthält.\n\nDie Funktion ` function `, die als zweiter Parameter angegeben wird, muss die folgende Signatur aufweisen:\n\n` function (value [, index [, array]]) `"
},
"$filter" : {
"args" : "Array, Funktion",
"desc" : "Gibt ein Array zurück, das nur die Werte im Parameter 'array' enthält, die das Prädikat ` funktion ` erfüllen.\n\nDie Funktion ` function `, die als zweiter Parameter angegeben wird, muss die folgende Signatur aufweisen:\n\n` function (value [, index [, array]]) `"
},
"$reduce" : {
"args" : "array, function [, init]",
"desc" : "Gibt einen aggregierten Wert zurück, der aus der Anwendung des Parameters ` function 'nacheinander auf jeden Wert in' array ` in Kombination mit dem Ergebnis der vorherigen Anwendung der Funktion angewendet wurde.\n\nDie Funktion muss zwei Argumente akzeptieren und verhält sich wie ein Infix-Operator zwischen jedem Wert innerhalb des ` Array `.\n\nDer optionale Parameter 'init' wird als Anfangswert in der Aggregation verwendet."
},
"$flowContext" : {
"args" : "Zeichenfolge [, Zeichenfolge]",
"desc" : "Ruft eine Flusskontexteigenschaft ab.\n\nDies ist eine definierte Funktion vom Typ \"Node-RED\"."
},
"$globalContext" : {
"args" : "Zeichenfolge [, Zeichenfolge]",
"desc" : "Ruft eine globale Kontexteigenschaft ab.\n\nDies ist eine definierte Funktion vom Typ \"Node-RED\"."
},
"$pad" : {
"args" : "string, width [, char]",
"desc" : "Gibt eine Kopie der ` Zeichenfolge ` mit zusätzlichen Aufenthalten zurück, falls erforderlich, so dass die Gesamtzahl der Zeichen mindestens der absolute Wert des Parameters 'width' ist.\n\nWenn ` width ` eine positive Zahl ist, wird die Zeichenfolge nach rechts aufgefüllt. Wenn sie negativ ist, wird sie nach links geplisften.\n\nDas optionale Argument 'char' gibt die Padding-Zeichen an, die verwendet werden sollen. Wenn keine Angabe gemacht wird, wird standardmäßig der Wert für das Leerzeichen angenommen."
},
"$fromMillis" : {
"args" : "Anzahl",
"desc" : "Konvertieren Sie eine Zahl, die Millisekunden seit der Unix-Epoche (1. Januar 1970 (UTC)) in eine Zeitmarkenzeichenfolge im ISO 8601-Format darstellt."
},
"$formatNumber" : {
"args" : "Zahl, Bild [, Optionen]",
"desc" : "Transformiere die `Zahl` an eine Zeichenfolge und formatiert sie in eine dezimale Darstellung, wie in der 'Bild' -Zeichenfolge angegeben.\n\n Das Verhalten dieser Funktion ist mit der XPath/XQuery-Funktion fn:formatnummer konsistent, wie sie in der XPath F&O 3.1-Spezifikation definiert ist. Der Parameter für die Bildzeichenfolge definiert, wie die Zahl formatiert ist und hat die gleiche Syntax wie fn:format-number.\n\nDas optionale dritte Argument ` Optionen ` wird verwendet, um die standardmäßigen länderspezifischen Formatierungszeichen, wie z. B. das Dezimaltrennzeichen, zu überschreiben. Wenn dieses Argument angegeben wird, muss es sich um ein Objekt handeln, das Name/Wert-Paare enthält, die im Abschnitt mit dem Dezimalformat der XPath F&O 3.1-Spezifikation angegeben sind."
},
"$formatBase" : {
"args" : "Zahl [, Radix]",
"desc" : "Transformiere die `Zahl` in eine Zeichenfolge und formatiert sie in eine ganze Zahl, die in der durch das `radix` -Argument angegebenen Zahlenbasis dargestellt wird. Wenn 'radix' nicht angegeben wird, wird standardmäßig die Basis 10 verwendet. 'radix` kann zwischen 2 und 36 liegen, andernfalls wird ein Fehler ausgelöst."
},
"$toMillis" : {
"args" : "Zeitmarke",
"desc" : "Konvertieren Sie eine Zeichenfolge `Zeitmarke' im ISO 8601-Format in die Anzahl der Millisekunden seit der Unix-Epoche (1. Januar 1970 (UTC)) als Zahl. Es wird ein Fehler ausgelöst, wenn die Zeichenfolge nicht das richtige Format hat."
},
"$env" : {
"args" : "arg",
"desc" : "Gibt den Wert einer Umgebungsvariablen zurück.\n\nDies ist eine definierte Funktion vom Typ \"Node-RED\"."
}
}

View File

@@ -27,8 +27,7 @@
"status": "Status",
"enabled": "Enabled",
"disabled":"Disabled",
"info": "Description",
"tip": "Description accepts Markdown and will appear in the Info tab."
"info": "Description"
},
"menu": {
"label": {
@@ -106,7 +105,8 @@
"warning": "<strong>Warning</strong>: __message__",
"warnings": {
"undeployedChanges": "node has undeployed changes",
"nodeActionDisabled": "node actions disabled within subflow",
"nodeActionDisabled": "node actions disabled",
"nodeActionDisabledSubflow": "node actions disabled within subflow",
"missing-types": "<p>Flows stopped due to missing node types.</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",
@@ -118,7 +118,6 @@
"project_not_found": "<p>Project '__project__' not found.</p>",
"git_merge_conflict": "<p>Automatic merging of changes failed.</p><p>Fix the unmerged conflicts then commit the results.</p>"
},
"error": "<strong>Error</strong>: __message__",
"errors": {
"lostConnection": "Lost connection to server, reconnecting...",
@@ -135,7 +134,7 @@
"loaded": "Project '__project__' loaded",
"updated": "Project '__project__' updated",
"pull": "Project '__project__' reloaded",
"revert": "Project '__project__' reloaded",
"revert": "Project '__project__' reverted",
"merge-complete": "Git merge completed"
},
"label": {
@@ -154,15 +153,16 @@
"node_plural": "__count__ nodes",
"configNode": "__count__ configuration node",
"configNode_plural": "__count__ configuration nodes",
"node_plural": "__count__ nodes",
"flow": "__count__ flow",
"flow_plural": "__count__ flows",
"subflow": "__count__ subflow",
"subflow_plural": "__count__ subflows",
"selectNodes": "Select the text above and copy to the clipboard.",
"pasteNodes": "Paste nodes here",
"pasteNodes": "Paste flow json or",
"selectFile": "select a file to import",
"importNodes": "Import nodes",
"exportNodes": "Export nodes to clipboard",
"exportNodes": "Export nodes",
"download": "Download",
"importUnrecognised": "Imported unrecognised type:",
"importUnrecognised_plural": "Imported unrecognised types:",
"nodesExported": "Nodes exported to clipboard",
@@ -273,10 +273,14 @@
"editSubflowProperties": "edit properties",
"input": "inputs:",
"output": "outputs:",
"status": "status node",
"deleteSubflow": "delete subflow",
"info": "Description",
"category": "Category",
"format":"markdown format",
"env": {
"restore": "Restore to subflow default",
"remove": "Remove environment variable"
},
"errors": {
"noNodesSelected": "<strong>Cannot create subflow</strong>: no nodes selected",
"multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection"
@@ -376,7 +380,7 @@
},
"event": {
"nodeAdded": "Node added to palette:",
"nodeAdded_plural": "Nodes added to palette",
"nodeAdded_plural": "Nodes added to palette:",
"nodeRemoved": "Node removed from palette:",
"nodeRemoved_plural": "Nodes removed from palette:",
"nodeEnabled": "Node enabled:",
@@ -421,6 +425,8 @@
"updated": "updated",
"install": "install",
"installed": "installed",
"conflict": "conflict",
"conflictTip": "<p>This module cannot be installed as it includes a<br/>node type that has already been installed</p><p>Conflicts with <code>__module__</code></p>",
"loading": "Loading catalogues...",
"tab-nodes": "Nodes",
"tab-install": "Install",
@@ -459,7 +465,6 @@
"update": "Update"
}
}
}
},
"sidebar": {
@@ -512,7 +517,8 @@
"empty": "empty",
"node": "Node",
"flow": "Flow",
"global": "Global"
"global": "Global",
"deleteConfirm": "Are you sure you want to delete this item?"
},
"palette": {
"name": "Palette management",
@@ -713,7 +719,20 @@
"format": "format JSON"
},
"markdownEditor": {
"title": "Markdown editor"
"title": "Markdown editor",
"format": "Formatted with markdown",
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"bold": "Bold",
"italic": "Italic",
"code": "Code",
"ordered-list": "Ordered list",
"unordered-list": "Unordered list",
"quote": "Quote",
"link": "Link",
"horizontal-rule": "Horizontal rule",
"toggle-preview": "Toggle preview"
},
"bufferEditor": {
"title": "Buffer editor",
@@ -883,5 +902,11 @@
"unexpected": "An unexpected error occurred",
"code": "code"
}
},
"editor-tab": {
"properties": "Properties",
"description": "Description",
"appearance": "Appearance",
"env": "Environment Variables"
}
}

View File

@@ -115,7 +115,6 @@
"args": "array",
"desc": "Returns the mean value of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
},
"$boolean": {
"args": "arg",
"desc": "Casts the argument to a Boolean using the following rules:\n\n - `Boolean` : unchanged\n - `string`: empty : `false`\n - `string`: non-empty : `true`\n - `number`: `0` : `false`\n - `number`: non-zero : `true`\n - `null` : `false`\n - `array`: empty : `false`\n - `array`: contains a member that casts to `true` : `true`\n - `array`: all members cast to `false` : `false`\n - `object`: empty : `false`\n - `object`: non-empty : `true`\n - `function` : `false`"
@@ -219,5 +218,18 @@
"$env": {
"args": "arg",
"desc": "Returns the value of an environment variable.\n\nThis is a Node-RED defined function."
},
"$eval": {
"args": "expr [, context]",
"desc": "Parses and evaluates the string `expr` which contains literal JSON or a JSONata expression using the current context as the context for evaluation."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Casts the `number` to a string and formats it to an integer representation as specified by the `picture` string. The picture string parameter defines how the number is formatted and has the same syntax as `fn:format-integer` from the XPath F&O 3.1 specification."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Parses the contents of the `string` parameter to an integer (as a JSON number) using the format specified by the `picture` string. The `picture` string parameter has the same format as `$formatInteger`."
}
}

116
packages/node_modules/@node-red/editor-client/locales/ja/editor.json vendored Normal file → Executable file
View File

@@ -23,11 +23,11 @@
"confirmDelete": "削除の確認",
"delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください",
"addFlow": "フローの追加",
"status": "状態",
"enabled": "有効",
"disabled": "無効",
"info": "詳細",
"tip": "マークダウン形式で記述した「詳細」は「情報タブ」に表示されます。"
"info": "詳細"
},
"menu": {
"label": {
@@ -46,6 +46,9 @@
"sidebar": {
"show": "サイドバーを表示"
},
"palette": {
"show": "パレットを表示"
},
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
@@ -75,7 +78,8 @@
"projects": "プロジェクト",
"projects-new": "新規",
"projects-open": "開く",
"projects-settings": "設定"
"projects-settings": "設定",
"showNodeLabelDefault": "追加したノードのラベルを表示する"
}
},
"actions": {
@@ -101,7 +105,8 @@
"warning": "<strong>警告</strong>: __message__",
"warnings": {
"undeployedChanges": "ノードの変更をデプロイしていません",
"nodeActionDisabled": "ノードのアクションは、サブフロー内で無効になっています",
"nodeActionDisabled": "ノードのアクションは無効になっています",
"nodeActionDisabledSubflow": "ノードのアクションは、サブフロー内で無効になっています",
"missing-types": "不明なノードが存在するため、フローを停止しました。詳細はログを確認してください。",
"safe-mode": "<p>セーフモードでフローを停止しました</p><p>フローを変更し、再起動するために変更をデプロイできます</p>",
"restartRequired": "更新されたモジュールを有効化するため、Node-REDを再起動する必要があります",
@@ -120,7 +125,7 @@
"lostConnectionTry": "すぐに接続",
"cannotAddSubflowToItself": "サブフロー自身を追加できません",
"cannotAddCircularReference": "循環参照を検出したため、サブフローを追加できません",
"unsupportedVersion": "サポートされていないバージョンのNode.jsを使用しています。<br/>最新のNode.js LTSに更新してください。",
"unsupportedVersion": "<p>サポートされていないバージョンのNode.jsを使用しています。</p><p><br/>最新のNode.js LTSに更新してください。</p>",
"failedToAppendNode": "<p>'__module__'がロードできませんでした。</p><p>__error__</p>"
},
"project": {
@@ -129,7 +134,7 @@
"loaded": "プロジェクト'__project__'をロードしました",
"updated": "プロジェクト'__project__'を更新しました",
"pull": "プロジェクト'__project__'を再ロードしました",
"revert": "プロジェクト'__project__'を再ロードしました",
"revert": "プロジェクト'__project__'を取り消しました",
"merge-complete": "Gitマージが完了しました"
},
"label": {
@@ -144,13 +149,24 @@
},
"clipboard": {
"nodes": "ノード",
"node": "__count__ 個のノード",
"node_plural": "__count__ 個のノード",
"configNode": "__count__ 個の設定ノード",
"configNode_plural": "__count__ 個の設定ノード",
"flow": "__count__ 個のフロー",
"flow_plural": "__count__ 個のフロー",
"subflow": "__count__ 個のサブフロー",
"subflow_plural": "__count__ 個のサブフロー",
"selectNodes": "上のテキストを選択し、クリップボードへコピーしてください",
"pasteNodes": "JSON形式のフローデータを貼り付けてください",
"selectFile": "読み込むファイルを選択してください",
"importNodes": "フローをクリップボートから読み込み",
"exportNodes": "フローをクリップボードへ書き出し",
"download": "ダウンロード",
"importUnrecognised": "認識できない型が読み込まれました:",
"importUnrecognised_plural": "認識できない型が読み込まれました:",
"nodesExported": "クリップボードへフローを書き出しました",
"nodesImported": "読み込みました:",
"nodeCopied": "__count__ 個のノードをコピーしました",
"nodeCopied_plural": "__count__ 個のノードをコピーしました",
"invalidFlow": "不正なフロー: __message__",
@@ -164,7 +180,13 @@
},
"import": {
"import": "読み込み先",
"newFlow": "新規のタブ"
"newFlow": "新規のタブ",
"errors": {
"notArray": "JSON形式の配列ではありません",
"itemNotObject": "不正なフロー - __index__ 番目の要素はノードオブジェクトではありません",
"missingId": "不正なフロー - __index__ 番目の要素に'id'プロパティがありません",
"missingType": "不正なフロー - __index__ 番目の要素に'type'プロパティがありません"
}
},
"copyMessagePath": "パスをコピーしました",
"copyMessageValue": "値をコピーしました",
@@ -202,7 +224,7 @@
"unknown": "ワークスペースに未知の型のノードがあります。",
"confirm": "このままデプロイしても良いですか?",
"doNotWarn": "この警告を再度表示しない",
"conflict": "フローを編集している間に、他のブラウザがフローをデプロイしました。デプロイを継続すると、他のブラウザがデプロイしたフローが削除されます。",
"conflict": "フローを編集している間に、他のブラウザがフローをデプロイしました。",
"backgroundUpdate": "サーバ上のフローが更新されました",
"conflictChecking": "変更を自動的にマージしてよいか確認してください。",
"conflictAutoMerge": "変更の衝突がないため、自動的にマージできます。",
@@ -210,6 +232,10 @@
"plusNMore": "さらに __count__ 個"
}
},
"eventLog": {
"title": "イベントログ",
"view": "ログを確認"
},
"diff": {
"unresolvedCount": "未解決の衝突 __count__",
"unresolvedCount_plural": "未解決の衝突 __count__",
@@ -247,10 +273,14 @@
"editSubflowProperties": "プロパティを編集",
"input": "入力:",
"output": "出力:",
"status": "ステータスノード",
"deleteSubflow": "サブフローを削除",
"info": "詳細",
"category": "カテゴリ",
"format": "マークダウン形式",
"env": {
"restore": "デフォルト値に戻す",
"remove": "環境変数を削除"
},
"errors": {
"noNodesSelected": "<strong>サブフローを作成できません</strong>: ノードが選択されていません",
"multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています"
@@ -268,15 +298,18 @@
"editConfig": "__type__ ノードの設定を編集",
"addNewType": "新規に __type__ を追加...",
"nodeProperties": "プロパティ",
"portLabels": "設定",
"label": "ラベル",
"portLabels": "ポートラベル",
"labelInputs": "入力",
"labelOutputs": "出力",
"settingIcon": "アイコン",
"noDefaultLabel": "なし",
"defaultLabel": "既定の名前を使用",
"defaultLabel": "既定のラベルを使用",
"searchIcons": "アイコンを検索",
"useDefault": "デフォルトを使用",
"description": "詳細",
"show": "表示",
"hide": "非表示",
"errors": {
"scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします"
}
@@ -299,7 +332,8 @@
"exportNode": "フローの書き出し",
"nudgeNode": "選択したノードを移動(移動量小)",
"moveNode": "選択したノードを移動(移動量大)",
"toggleSidebar": "サイドバーの表示非表示",
"toggleSidebar": "サイドバーの表示/非表示",
"togglePalette": "パレットの表示/非表示",
"copyNode": "選択したノードをコピー",
"cutNode": "選択したノードを切り取り",
"pasteNode": "ノードを貼り付け",
@@ -346,7 +380,7 @@
},
"event": {
"nodeAdded": "ノードをパレットへ追加しました:",
"nodeAdded_plural": "ノードをパレットへ追加しました",
"nodeAdded_plural": "ノードをパレットへ追加しました:",
"nodeRemoved": "ノードをパレットから削除しました:",
"nodeRemoved_plural": "ノードをパレットから削除しました:",
"nodeEnabled": "ノードを有効化しました:",
@@ -391,6 +425,8 @@
"updated": "更新済",
"install": "ノードを追加",
"installed": "追加しました",
"conflict": "競合",
"conflictTip": "<p>インストール済みのノードの種別と競合しているため<br/>ノードをインストールできません</p><p>競合: <code>__module__</code></p>",
"loading": "カタログを読み込み中",
"tab-nodes": "現在のノード",
"tab-install": "ノードを追加",
@@ -399,28 +435,28 @@
"sortRecent": "日付順",
"more": "+ さらに __count__ 個",
"errors": {
"catalogLoadFailed": "ノードのカタログの読み込みに失敗しました。<br>詳細はブラウザのコンソールを確認してください。",
"installFailed": "追加処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください。",
"removeFailed": "削除処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください。",
"updateFailed": "更新処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください。",
"enableFailed": "有効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください。",
"disableFailed": "無効化処理が失敗しました: __module__<br>__message__<br>詳細はログを確認してください。"
"catalogLoadFailed": "<p>ノードのカタログの読み込みに失敗しました。</p><p>詳細はブラウザのコンソールを確認してください。</p>",
"installFailed": "<p.追加処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"removeFailed": "<p>削除処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"updateFailed": "<p>更新処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"enableFailed": "<p>有効化処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
"disableFailed": "<p>無効化処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>"
},
"confirm": {
"install": {
"body": "ードを追加する前に、ドキュメントを確認してください。ードによっては、モジュールの依存関係を自動的に解決できない場合や、Node-REDの再起動が必要となる場合があります。",
"body": "<p>__module__ をインストールします。</p><p>ードを追加する前に、ドキュメントを確認してください。ードによっては、モジュールの依存関係を自動的に解決できない場合や、Node-REDの再起動が必要となる場合があります。</p>",
"title": "ノードを追加"
},
"remove": {
"body": "Node-REDからードを削除します。ードはNode-REDが再起動されるまで、リソースを使い続けます。",
"body": "<p>__module__ を削除します。</p><p>Node-REDからードを削除します。ードはNode-REDが再起動されるまで、リソースを使い続ける可能性があります。</p>",
"title": "ノードを削除"
},
"update": {
"body": "更新を完了するには手動でNode-REDを再起動する必要があります。",
"body": "<p>__module__ を更新します。</p><p>更新を完了するには手動でNode-REDを再起動する必要があります。</p>",
"title": "ノードの更新"
},
"cannotUpdate": {
"body": "ノードの更新があります。「パレットの管理」の画面では更新されません。ドキュメントを参照し、ノードの更新手順を確認してください。"
"body": "ノードの更新があります。「パレットの管理」の画面では更新されません。<br/><br/>ドキュメントを参照し、ノードの更新手順を確認してください。"
},
"button": {
"review": "ノードの情報を参照",
@@ -438,6 +474,7 @@
"label": "情報",
"node": "ノード",
"type": "型",
"module": "モジュール",
"id": "ID",
"status": "状態",
"enabled": "有効",
@@ -480,7 +517,8 @@
"empty": "データが存在しません",
"node": "Node",
"flow": "Flow",
"global": "Global"
"global": "Global",
"deleteConfirm": "データを削除しても良いですか?"
},
"palette": {
"name": "パレットの管理",
@@ -560,6 +598,11 @@
"versionControl": {
"unstagedChanges": "ステージングされていない変更",
"stagedChanges": "ステージングされた変更",
"unstageChange": "ステージングした変更の取り消し",
"stageChange": "変更をステージング",
"unstageAllChange": "ステージングした全ての変更の取り消し",
"stageAllChange": "全ての変更をステージング",
"commitChanges": "変更をコミット",
"resolveConflicts": "コンフリクトの解決",
"head": "最新",
"staged": "ステージング",
@@ -623,7 +666,9 @@
"commitsAheadAndBehind2": "__count__コミット進んでいます。 ",
"commitsAheadAndBehind2_plural": "__count__コミット進んでいます。 ",
"commitsAheadAndBehind3": "プッシュする前にリモートのコミットをプルしてください。",
"commitsAheadAndBehind3_plural": "プッシュする前にリモートのコミットをプルしてください。"
"commitsAheadAndBehind3_plural": "プッシュする前にリモートのコミットをプルしてください。",
"refreshCommitHistory": "コミット履歴を更新",
"refreshChanges": "変更を更新"
}
}
},
@@ -674,7 +719,20 @@
"format": "JSONフォーマット"
},
"markdownEditor": {
"title": "マークダウンエディタ"
"title": "マークダウンエディタ",
"format": "マークダウン形式で記述",
"heading1": "見出しレベル1",
"heading2": "見出しレベル2",
"heading3": "見出しレベル3",
"bold": "太字",
"italic": "斜体",
"code": "コード",
"ordered-list": "箇条書き(番号付き)",
"unordered-list": "箇条書き",
"quote": "引用",
"link": "リンク",
"horizontal-rule": "区切り線",
"toggle-preview": "プレビュー表示切替え"
},
"bufferEditor": {
"title": "バッファエディタ",
@@ -844,5 +902,11 @@
"unexpected": "予期しないエラーが発生しました",
"code": "コード"
}
},
"editor-tab": {
"properties": "プロパティ",
"description": "説明",
"appearance": "外観",
"env": "環境変数"
}
}

View File

@@ -218,5 +218,17 @@
"$env": {
"args": "arg",
"desc": "環境変数の値を返します。\n\n本関数はNode-REDの定義関数です。"
},
"$eval": {
"args": "expr [, context]",
"desc": "JSONリテラルもしくはJSONata式を表す`expr`を評価します。評価の際には現在のコンテクストをコンテクストして用います。"
},
"$formatInteger": {
"args": "number, picture",
"desc": "`number`を`picture`指定に従って文字列に変換します。`picture`文字列は数値の変換方法をXPath F&O 3.1仕様の`fn:format-integer`に従って定義します。"
},
"$parseInteger": {
"args": "string, picture",
"desc": "`picture`文字列の指定に従って、`string`パラメータを整数(JSON数値)に変換します。`picture`文字列は`$formatInteger`と同じ形式です。"
}
}

View File

@@ -22,8 +22,7 @@
"status": "状态",
"enabled": "有效",
"disabled": "无效",
"info": "详细描述",
"tip": "详细描述支持Markdown轻量级标记语言并将出现在信息标签中。"
"info": "详细描述"
},
"menu": {
"label": {
@@ -86,7 +85,7 @@
"warning": "<strong>警告</strong>: __message__",
"warnings": {
"undeployedChanges": "节点中存在未部署的更改",
"nodeActionDisabled": "节点动作在子流程中被禁用",
"nodeActionDisabledSubflow": "节点动作在子流程中被禁用",
"missing-types": "流程由于缺少节点类型而停止。请检查日志的详细信息",
"restartRequired": "Node-RED必须重新启动以启用升级的模块"
},
@@ -191,7 +190,6 @@
"output": "输出:",
"deleteSubflow": "删除子流程",
"info": "详细描述",
"format": "标记格式",
"errors": {
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"

View File

@@ -1,14 +1,18 @@
{
"name": "@node-red/editor-client",
"version": "0.20.0-alpha.0",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
"contributors": [
{ "name": "Nick O'Leary" },
{ "name": "Dave Conway-Jones"}
],
"main": "./lib/index.js"
"name": "@node-red/editor-client",
"version": "0.20.0-beta.5",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/node-red/node-red.git"
},
"contributors": [
{
"name": "Nick O'Leary"
},
{
"name": "Dave Conway-Jones"
}
],
"main": "./lib/index.js"
}

View File

@@ -75,29 +75,39 @@ RED.comms = (function() {
}
ws.onmessage = function(event) {
var message = JSON.parse(event.data);
for (var m = 0; m < message.length; m++) {
var msg = message[m];
if (pendingAuth && msg.auth) {
if (msg.auth === "ok") {
if (message.auth) {
if (pendingAuth) {
if (message.auth === "ok") {
pendingAuth = false;
completeConnection();
} else if (msg.auth === "fail") {
} else if (message.auth === "fail") {
// anything else is an error...
active = false;
RED.user.login({updateMenu:true},function() {
connectWS();
})
}
} else if (message.auth === "fail") {
// Our current session has expired
active = false;
RED.user.login({updateMenu:true},function() {
connectWS();
})
}
else if (msg.topic) {
for (var t in subscriptions) {
if (subscriptions.hasOwnProperty(t)) {
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
if (re.test(msg.topic)) {
var subscribers = subscriptions[t];
if (subscribers) {
for (var i=0;i<subscribers.length;i++) {
subscribers[i](msg.topic,msg.data);
} else {
// Otherwise, 'message' is an array of actual comms messages
for (var m = 0; m < message.length; m++) {
var msg = message[m];
if (msg.topic) {
for (var t in subscriptions) {
if (subscriptions.hasOwnProperty(t)) {
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
if (re.test(msg.topic)) {
var subscribers = subscriptions[t];
if (subscribers) {
for (var i=0;i<subscribers.length;i++) {
subscribers[i](msg.topic,msg.data);
}
}
}
}
@@ -132,10 +142,10 @@ RED.comms = (function() {
connectWS();
} else {
var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>';
errornotification.update(msg);
errornotification.update(msg,{silent:true});
$(errornotification).find("a").click(function(e) {
e.preventDefault();
errornotification.update(RED._("notification.errors.lostConnection"));
errornotification.update(RED._("notification.errors.lostConnection"),{silent:true});
clearInterval(connectCountdownTimer);
connectWS();
})

View File

@@ -125,14 +125,20 @@ RED.history = (function() {
});
}
}
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
if (ev.subflow) {
if (ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
}
if (ev.subflow.hasOwnProperty('status')) {
subflow = RED.nodes.subflow(ev.subflow.id);
subflow.status = ev.subflow.status;
}
}
if (subflow) {
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
@@ -232,6 +238,11 @@ RED.history = (function() {
}
});
}
if (ev.subflow.hasOwnProperty('status')) {
if (ev.subflow.status) {
delete ev.node.status;
}
}
RED.editor.validateNode(ev.node);
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
n.inputs = ev.node.in.length;
@@ -262,6 +273,8 @@ RED.history = (function() {
} else if (ev.t == "createSubflow") {
if (ev.nodes) {
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
n.x += ev.subflow.offsetX;
n.y += ev.subflow.offsetY;
n.z = ev.activeWorkspace;
n.dirty = true;
});
@@ -288,6 +301,7 @@ RED.history = (function() {
RED.workspaces.order(ev.order);
}
}
Object.keys(modifiedTabs).forEach(function(id) {
var subflow = RED.nodes.subflow(id);
if (subflow) {
@@ -296,10 +310,12 @@ RED.history = (function() {
});
RED.nodes.dirty(ev.dirty);
RED.view.select(null);
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
RED.subflow.refresh();
}
}

View File

@@ -30,12 +30,18 @@ RED.i18n = (function() {
defaultNs: "editor"
},
fallbackLng: ['en-US'],
useCookie: false
useCookie: false,
returnObjectTrees: true
},function() {
done();
});
RED["_"] = function() {
return i18n.t.apply(null,arguments);
var v = i18n.t.apply(null,arguments);
if (typeof v === 'string') {
return v;
} else {
return arguments[0];
}
}
},

View File

@@ -22,6 +22,12 @@
"ctrl-g v": "core:show-version-control-tab",
"ctrl-shift-l": "core:show-event-log"
},
"sidebar-node-config": {
"backspace": "core:delete-config-selection",
"delete": "core:delete-config-selection",
"ctrl-a": "core:select-all-config-nodes",
"ctrl-z": "core:undo"
},
"workspace": {
"backspace": "core:delete-selection",
"delete": "core:delete-selection",

View File

@@ -358,7 +358,10 @@ RED.nodes = (function() {
}
subflows[sf.id] = sf;
RED.nodes.registerType("subflow:"+sf.id, {
defaults:{name:{value:""}},
defaults:{
name:{value:""},
env:{value:[]}
},
icon: function() { return sf.icon||"subflow.png" },
category: sf.category || "subflows",
inputs: sf.in.length,
@@ -369,6 +372,16 @@ RED.nodes = (function() {
paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
oneditresize: function(size) {
var rows = $("#dialog-form>div:not(.node-input-env-container-row)");
var height = size.height;
for (var i=0; i<rows.size(); i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-env-container-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$("#node-input-env-container").editableList('height',height-80);
},
set:{
module: "node-red"
}
@@ -535,6 +548,7 @@ RED.nodes = (function() {
node.category = n.category;
node.in = [];
node.out = [];
node.env = n.env;
n.in.forEach(function(p) {
var nIn = {x:p.x,y:p.y,wires:[]};
@@ -571,6 +585,18 @@ RED.nodes = (function() {
node.icon = n.icon;
}
}
if (n.status) {
node.status = {x: n.status.x, y: n.status.y, wires:[]};
links.forEach(function(d) {
if (d.target === n.status) {
if (d.source.type != "subflow") {
node.status.wires.push({id:d.source.id, port:d.sourcePort})
} else {
node.status.wires.push({id:n.id, port:0})
}
}
});
}
return node;
}
@@ -851,6 +877,12 @@ RED.nodes = (function() {
output.i = i;
output.id = getID();
});
if (n.status) {
n.status.type = "subflow";
n.status.direction = "status";
n.status.z = n.id;
n.status.id = getID();
}
new_subflows.push(n);
addSubflow(n,createNewIds);
}
@@ -955,11 +987,11 @@ RED.nodes = (function() {
def = registry.getNodeType(n.type);
if (!def || def.category != "config") {
var node = {
x:n.x,
y:n.y,
x:parseFloat(n.x || 0),
y:parseFloat(n.y || 0),
z:n.z,
type:0,
wires:n.wires,
wires:n.wires||[],
inputLabels: n.inputLabels,
outputLabels: n.outputLabels,
icon: n.icon,
@@ -1018,6 +1050,7 @@ RED.nodes = (function() {
node.name = n.name;
node.outputs = subflow.out.length;
node.inputs = subflow.in.length;
node.env = n.env;
} else {
if (!node._def) {
if (node.x && node.y) {
@@ -1189,6 +1222,19 @@ RED.nodes = (function() {
});
delete output.wires;
});
if (n.status) {
n.status.wires.forEach(function(wire) {
var link;
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
} else {
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
}
addLink(link);
new_links.push(link);
});
delete n.status.wires;
}
}
RED.workspaces.refresh();
@@ -1306,21 +1352,23 @@ RED.nodes = (function() {
RED.events.on("registry:node-type-added",function(type) {
var def = registry.getNodeType(type);
var replaced = false;
var replaceNodes = [];
var replaceNodes = {};
RED.nodes.eachNode(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes.push(n);
replaceNodes[n.id] = n;
}
});
RED.nodes.eachConfig(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes.push(n);
replaceNodes[n.id] = n;
}
});
if (replaceNodes.length > 0) {
var replaceNodeIds = Object.keys(replaceNodes);
if (replaceNodeIds.length > 0) {
var reimportList = [];
replaceNodes.forEach(function(n) {
replaceNodeIds.forEach(function(id) {
var n = replaceNodes[id];
if (configNodes.hasOwnProperty(n.id)) {
delete configNodes[n.id];
} else {
@@ -1328,6 +1376,18 @@ RED.nodes = (function() {
}
reimportList.push(convertNode(n));
});
// Remove any links between nodes that are going to be reimported.
// This prevents a duplicate link from being added.
var removeLinks = [];
RED.nodes.eachLink(function(l) {
if (replaceNodes.hasOwnProperty(l.source.id) && replaceNodes.hasOwnProperty(l.target.id)) {
removeLinks.push(l);
}
});
removeLinks.forEach(removeLink);
RED.view.redraw(true);
var result = importNodes(reimportList,false);
var newNodeMap = {};

View File

@@ -391,7 +391,7 @@ var RED = (function() {
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
}
} else if (topic == "node/upgraded") {
} else if (topic == "notification/node/upgraded") {
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
}
@@ -418,10 +418,10 @@ var RED = (function() {
function loadEditor() {
var menuOptions = [];
if (RED.settings.theme("projects.enabled",false)) {
menuOptions.push({id:"menu-item-projects-menu",label:"Projects",options:[
{id:"menu-item-projects-new",label:"New",disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:"Open",disabled:false,onselect:"core:open-project"},
{id:"menu-item-projects-settings",label:"Project Settings",disabled:false,onselect:"core:show-project-settings"}
menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[
{id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"},
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
]});
}

View File

@@ -1242,7 +1242,7 @@ RED.text.format = (function() {
element.dispatchEvent(event);
return;
}
var range = selection.getRangeAt(0);
var tempRange = range.cloneRange(), startNode, startOffset;
startNode = range.startContainer;
@@ -1304,7 +1304,7 @@ RED.text.format = (function() {
}
return {
/**
/*!
* Returns the HTML representation of a given structured text
* @param text - the structured text
* @param type - could be one of filepath, url, email
@@ -1315,7 +1315,7 @@ RED.text.format = (function() {
getHtml: function (text, type, args, isRtl, locale) {
return getHandler(type).format(text, args, isRtl, true, locale);
},
/**
/*!
* Handle Structured text correct display for a given HTML element.
* @param element - the element : should be of type div contenteditable=true
* @param type - could be one of filepath, url, email

View File

@@ -49,6 +49,21 @@ RED.clipboard = (function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-download",
class: "primary",
text: RED._("clipboard.download"),
click: function() {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent($("#clipboard-export").val()));
element.setAttribute('download', "flows.json");
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-copy",
class: "primary",
@@ -57,7 +72,7 @@ RED.clipboard = (function() {
$("#clipboard-export").select();
document.execCommand("copy");
document.getSelection().removeAllRanges();
RED.notify(RED._("clipboard.nodesExported"));
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
$( this ).dialog( "close" );
}
},
@@ -92,7 +107,7 @@ RED.clipboard = (function() {
'<a id="export-range-flow" class="editor-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="export-range-full" class="editor-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'</div>'+
'</div>'+
'<div class="form-row">'+
'<textarea readonly style="resize: none; width: 100%; border-radius: 4px;font-family: monospace; font-size: 12px; background:#f3f3f3; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
'</div>'+
@@ -103,10 +118,13 @@ RED.clipboard = (function() {
'</span>'+
'</div>';
importNodesDialog = '<div class="form-row">'+
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+
RED._("clipboard.pasteNodes")+
'"></textarea>'+
importNodesDialog =
'<div class="form-row"><span data-i18n="clipboard.pasteNodes"></span>'+
' <a class="editor-button" id="import-file-upload-btn"><i class="fa fa-upload"></i> <span data-i18n="clipboard.selectFile"></span></a>'+
'<input type="file" id="import-file-upload" accept=".json" style="display:none">'+
'</div>'+
'<div class="form-row">'+
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5"></textarea>'+
'</div>'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.import.import"></label>'+
@@ -223,6 +241,7 @@ RED.clipboard = (function() {
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-close").hide();
$("#clipboard-dialog-copy").hide();
$("#clipboard-dialog-download").hide();
$("#clipboard-dialog-ok").button("disable");
$("#clipboard-import").keyup(validateImport);
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
@@ -236,6 +255,19 @@ RED.clipboard = (function() {
$(this).addClass('selected');
});
$("#import-file-upload").change(function() {
var fileReader = new FileReader();
fileReader.onload = function () {
$("#clipboard-import").val(fileReader.result);
validateImport();
};
fileReader.readAsText($(this).prop('files')[0]);
})
$("#import-file-upload-btn").click(function(evt) {
evt.preventDefault();
$("#import-file-upload").click();
})
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
popover = RED.popover.create({
target: $("#clipboard-import"),
@@ -368,6 +400,8 @@ RED.clipboard = (function() {
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-copy").show();
}
$("#clipboard-dialog-download").show();
}
function hideDropTarget() {

View File

@@ -327,6 +327,14 @@
},
length: function() {
return this.element.children().length;
},
show: function(item) {
var items = this.element.children().filter(function(f) {
return item === $(this).find(".red-ui-editableList-item-content").data('data');
});
if (items.length > 0) {
this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top)
}
}
});
})(jQuery);

View File

@@ -150,13 +150,14 @@ RED.menu = (function() {
}
function createMenu(options) {
var topMenu = $("<ul/>",{class:"dropdown-menu pull-right"});
var menuParent = $("#"+options.id);
var topMenu = $("<ul/>",{id:options.id+"-submenu", class:"dropdown-menu pull-right"});
if (menuParent.length === 1) {
topMenu.insertAfter(menuParent);
if (options.id) {
topMenu.attr({id:options.id+"-submenu"});
var menuParent = $("#"+options.id);
if (menuParent.length === 1) {
topMenu.insertAfter(menuParent);
}
}
var lastAddedSeparator = false;

View File

@@ -38,7 +38,7 @@ RED.popover = (function() {
var direction = options.direction || "right";
var trigger = options.trigger;
var content = options.content;
var delay = options.delay;
var delay = options.delay || { show: 750, hide: 50 };
var autoClose = options.autoClose;
var width = options.width||"auto";
var size = options.size||"default";
@@ -131,6 +131,7 @@ RED.popover = (function() {
}
}
var closePopup = function(instant) {
$(document).off('mousedown.modal-popover-close');
if (!active) {
if (div) {
if (instant) {
@@ -171,6 +172,29 @@ RED.popover = (function() {
openPopup();
}
});
if (autoClose) {
target.on('mouseleave disabled', function(e) {
if (timer) {
clearTimeout(timer);
}
if (active) {
active = false;
setTimeout(closePopup,autoClose);
}
});
}
} else if (trigger === 'modal') {
$(document).on('mousedown.modal-popover-close', function (event) {
var target = event.target;
while (target.nodeName !== 'BODY' && target !== div[0]) {
target = target.parentElement;
}
if (target.nodeName === 'BODY') {
active = false;
closePopup();
}
});
} else if (autoClose) {
setTimeout(function() {
active = false;

View File

@@ -66,6 +66,14 @@ RED.stack = (function() {
}
}
entry.expand();
} else if (entries.length === 2) {
if (entries[0] === entry) {
entries[0].collapse();
entries[1].expand();
} else {
entries[1].collapse();
entries[0].expand();
}
}
} else {
entry.toggle();

View File

@@ -96,6 +96,7 @@ RED.tabs = (function() {
var selectButton = $('<a href="#"><i class="fa fa-caret-down"></i></a>').appendTo(collapsedButtonsRow);
selectButton.addClass("red-ui-tab-link-button-menu")
selectButton.click(function(evt) {
evt.stopPropagation();
evt.preventDefault();
if (!collapsibleMenu) {
var pinnedOptions = [];
@@ -121,15 +122,21 @@ RED.tabs = (function() {
collapsibleMenu.css({
position: "absolute"
})
collapsibleMenu.on('mouseleave', function(){ $(this).hide() });
collapsibleMenu.on('mouseup', function() { $(this).hide() });
collapsibleMenu.appendTo("body");
}
var elementPos = selectButton.offset();
collapsibleMenu.css({
top: (elementPos.top+selectButton.height()-20)+"px",
top: (elementPos.top+selectButton.height()-2)+"px",
left: (elementPos.left - collapsibleMenu.width() + selectButton.width())+"px"
})
if (collapsibleMenu.is(":visible")) {
$(document).off("click.tabmenu");
} else {
$(document).on("click.tabmenu", function(evt) {
$(document).off("click.tabmenu");
collapsibleMenu.hide();
});
}
collapsibleMenu.toggle();
})
}

View File

@@ -0,0 +1,184 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
(function($) {
/**
* options:
* - data : array - initial items to display in tree
*
* methods:
* - data(items) - clears existing items and replaces with new data
*
* events:
* - treelistselect : function(event, item) {}
*
*
* data:
* [
* {
* label: 'Local', // label for the item
* icon: 'fa fa-rocket', // (optional) icon for the item
* selected: true/false, // (optional) if present, display checkbox accordingly
* children: [] | function(done) // (optional) an array of child items, or a function
* // that will call the `done` callback with an array
* // of child items
* }
* ]
*
*
*
* var treeList = $("<div>").css({width: "100%", height: "100%"}).treeList({data:[...]})
* treeList.on('treelistselect', function(e,item) { console.log(item)})
* treeList.treeList('data',[ ... ] )
*
*/
$.widget( "nodered.treeList", {
_create: function() {
var that = this;
this.element.addClass('red-ui-treeList');
var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element);
this._data = [];
this._topList = $('<ol>').css({
position:'absolute',
top: 0,
left:0,
right:0,
bottom:0
}).appendTo(wrapper).editableList({
addButton: false,
scrollOnAdd: false,
height: '100%',
addItem: function(container,i,item) {
that._addSubtree(container,item,0);
}
});
if (this.options.data) {
this.data(this.options.data);
}
},
_addChildren: function(container,children,depth) {
var that = this;
var subtree = $('<ol>').appendTo(container).editableList({
addButton: false,
scrollOnAdd: false,
height: 'auto',
addItem: function(container,i,item) {
that._addSubtree(container,item,depth+1);
}
});
for (var i=0;i<children.length;i++) {
subtree.editableList('addItem',children[i])
}
},
_addSubtree: function(container, item, depth) {
var that = this;
var labelNodeType = "<label>";
if (item.children && item.hasOwnProperty('selected')) {
labelNodeType = "<div>";
}
var label = $(labelNodeType,{tabindex:"0",class:"red-ui-treeList-label"}).appendTo(container);
if (item.class) {
label.addClass(item.class);
}
label.css({
paddingLeft: (depth*15)+'px'
})
label.on('mouseover',function(e) { that._trigger('itemmouseover',e,item); })
label.on('mouseout',function(e) { that._trigger('itemmouseout',e,item); })
if (item.children) {
$('<span class="red-ui-treeList-icon"><i class="fa fa-angle-right" /></span>').appendTo(label);
// $('<span class="red-ui-treeList-icon"><i class="fa fa-folder-o" /></span>').appendTo(label);
label.click(function(e) {
if (!container.hasClass("built") && typeof item.children === 'function') {
container.addClass('built');
var childrenAdded = false;
var spinner;
item.children(function(children) {
childrenAdded = true;
that._addChildren(container,children,depth);
if (spinner) {
spinner.remove();
}
});
if (!childrenAdded) {
spinner = $('<div class="red-ui-treeList-spinner">').css({
"background-position": (35+depth*15)+'px 50%'
}).appendTo(container);
}
}
container.toggleClass("expanded");
})
} else {
$('<span class="red-ui-treeList-icon"></span>').appendTo(label);
}
if (item.hasOwnProperty('selected')) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
var cb = $('<input type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
cb.on('click', function(e) {
e.stopPropagation();
});
cb.on('change', function(e) {
item.selected = this.checked;
that._trigger("select",e,item);
})
} else if (!item.children) {
label.click(function(e) {
that._trigger("select",e,item)
})
}
if (item.icon) {
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
}
$('<span class="red-ui-treeList-label-text"></span>').html(item.label).appendTo(label);
if (item.children) {
if (Array.isArray(item.children)) {
that._addChildren(container,item.children,depth);
}
if (item.expanded) {
label.click();
}
}
},
empty: function() {
this._topList.editableList('empty');
},
data: function(items) {
if (items !== undefined) {
this._data = items;
this._topList.editableList('empty');
for (var i=0; i<items.length;i++) {
this._topList.editableList('addItem',items[i]);
}
} else {
return this._data;
}
},
show: function(id) {
for (var i=0;i<this._data.length;i++) {
if (this._data[i].id === id) {
this._topList.editableList('show',this._data[i]);
}
}
}
});
})(jQuery);

View File

@@ -261,7 +261,9 @@ RED.deploy = (function() {
}
return list;
}
function sanitize(html) {
return html.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")
}
function restart() {
var startTime = Date.now();
$(".deploy-button-content").css('opacity',0);
@@ -340,7 +342,7 @@ RED.deploy = (function() {
var unusedConfigNodes = [];
RED.nodes.eachConfig(function(node) {
if (node.users.length === 0 && (node._def.hasUsers !== false)) {
if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
unusedConfigNodes.push(getNodeInfo(node));
hasUnusedConfig = true;
}
@@ -353,7 +355,7 @@ RED.deploy = (function() {
if (hasUnknown && !ignoreDeployWarnings.unknown) {
showWarning = true;
notificationMessage = "<p>"+RED._('deploy.confirm.unknown')+"</p>"+
'<ul class="node-dialog-configm-deploy-list"><li>'+cropList(unknownNodes).join("</li><li>")+"</li></ul><p>"+
'<ul class="node-dialog-configm-deploy-list"><li>'+cropList(unknownNodes).map(function(n) { return sanitize(n) }).join("</li><li>")+"</li></ul><p>"+
RED._('deploy.confirm.confirm')+
"</p>";
@@ -373,7 +375,7 @@ RED.deploy = (function() {
invalidNodes.sort(sortNodeInfo);
notificationMessage = "<p>"+RED._('deploy.confirm.improperlyConfigured')+"</p>"+
'<ul class="node-dialog-configm-deploy-list"><li>'+cropList(invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"})).join("</li><li>")+"</li></ul><p>"+
'<ul class="node-dialog-configm-deploy-list"><li>'+cropList(invalidNodes.map(function(A) { return sanitize( (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")")})).join("</li><li>")+"</li></ul><p>"+
RED._('deploy.confirm.confirm')+
"</p>";
notificationButtons= [

View File

@@ -498,7 +498,7 @@ RED.diff = (function() {
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, false, def, node);
RED.utils.createIconElement(icon_url, iconContainer, false);
return nodeDiv;
}
@@ -687,7 +687,6 @@ RED.diff = (function() {
diff: remoteDiff
}
}
createNodePropertiesTable(def,node,localNode,remoteNode).appendTo(div);
var selectState = "";
@@ -707,6 +706,10 @@ RED.diff = (function() {
createNodeConflictRadioBoxes(node,div,localNodeDiv,remoteNodeDiv,false,!conflicted,selectState,CurrentDiff);
row.click(function(evt) {
$(this).parent().toggleClass('collapsed');
if($(this).siblings('.node-diff-node-entry-properties').length === 0) {
createNodePropertiesTable(def,node,localNode,remoteNode).appendTo(div);
}
});
return div;
@@ -1155,19 +1158,19 @@ RED.diff = (function() {
}
});
return {
var diff = {
currentConfig: currentConfig,
newConfig: newConfig,
added: added,
deleted: deleted,
changed: changed,
moved: moved
}
};
return diff;
}
function resolveDiffs(localDiff,remoteDiff) {
var conflicted = {};
var resolutions = {};
var diff = {
localDiff: localDiff,
remoteDiff: remoteDiff,
@@ -1345,7 +1348,7 @@ RED.diff = (function() {
if (node) {
nodeChangedStates[id] = node.changed;
}
localChangedStates[id] = true;
localChangedStates[id] = 1;
newConfig.push(remoteDiff.newConfig.all[id]);
}
} else {
@@ -1360,7 +1363,7 @@ RED.diff = (function() {
nodeChangedStates[id] = node.changed;
}
if (!localDiff.added.hasOwnProperty(id)) {
localChangedStates[id] = true;
localChangedStates[id] = 2;
newConfig.push(remoteDiff.newConfig.all[id]);
}
}
@@ -1373,24 +1376,42 @@ RED.diff = (function() {
}
function mergeDiff(diff) {
//console.log(diff);
var appliedDiff = applyDiff(diff);
var newConfig = appliedDiff.config;
var nodeChangedStates = appliedDiff.nodeChangedStates;
var localChangedStates = appliedDiff.localChangedStates;
var isDirty = RED.nodes.dirty();
var historyEvent = {
t:"replace",
config: RED.nodes.createCompleteNodeSet(),
changed: nodeChangedStates,
dirty: RED.nodes.dirty(),
dirty: isDirty,
rev: RED.nodes.version()
}
RED.history.push(historyEvent);
var originalFlow = RED.nodes.originalFlow();
// originalFlow is what the editor things it loaded
// - add any newly added nodes from remote diff as they are now part of the record
for (var id in diff.remoteDiff.added) {
if (diff.remoteDiff.added.hasOwnProperty(id)) {
if (diff.remoteDiff.newConfig.all.hasOwnProperty(id)) {
originalFlow.push(JSON.parse(JSON.stringify(diff.remoteDiff.newConfig.all[id])));
}
}
}
RED.nodes.clear();
var imported = RED.nodes.import(newConfig);
// Restore the original flow so subsequent merge resolutions can properly
// identify new-vs-old
RED.nodes.originalFlow(originalFlow);
imported[0].forEach(function(n) {
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
n.changed = true;
@@ -1399,11 +1420,16 @@ RED.diff = (function() {
RED.nodes.version(diff.remoteDiff.rev);
if (isDirty) {
RED.nodes.dirty(true);
}
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
}
function showTestFlowDiff(index) {
if (index === 1) {
var localFlow = RED.nodes.createCompleteNodeSet();

View File

@@ -13,6 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* @namespace RED.editor
*/
RED.editor = (function() {
@@ -21,6 +25,8 @@ RED.editor = (function() {
var editing_config_node = null;
var subflowEditor;
var customEditTypes = {};
var editTrayWidthCache = {};
function getCredentialsURL(nodeType, nodeID) {
@@ -124,6 +130,9 @@ RED.editor = (function() {
if (/^\$\([a-zA-Z_][a-zA-Z0-9_]*\)$/.test(value)) {
return true;
}
if (/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/.test(value)) {
return true;
}
if ("required" in definition[property] && definition[property].required) {
valid = value !== "";
}
@@ -531,7 +540,150 @@ RED.editor = (function() {
return label;
}
function buildEditForm(container,formId,type,ns) {
function buildEnvForm(container, node) {
var env_container = $('#node-input-env-container');
env_container
.css({
'min-height':'150px',
'min-width':'450px'
})
.editableList({
addItem: function(container, i, opt) {
var row = $('<div/>').appendTo(container);
if (opt.parent) {
$('<div/>', {
class:"uneditable-input",
style: "margin-left: 5px; width: calc(40% - 8px)",
}).appendTo(row).text(opt.name);
} else {
$('<input/>', {
class: "node-input-env-name",
type: "text",
style: "margin-left: 5px; width: calc(40% - 8px)",
placeholder: RED._("common.label.name")
}).appendTo(row).val(opt.name);
}
var valueField = $('<input/>',{
class: "node-input-env-value",
type: "text",
style: "margin-left: 5px; width: calc(60% - 8px)"
}).appendTo(row)
valueField.typedInput({default:'str',
types:['str','num','bool','json','bin','env']
});
valueField.typedInput('type', opt.parent?(opt.type||opt.parent.type):opt.type);
valueField.typedInput('value', opt.parent?(opt.value||opt.parent.value):opt.value);
var actionButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(container);
$('<i/>',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton);
container.parent().addClass("red-ui-editableList-item-removable");
if (opt.parent) {
if (opt.value && (opt.value !== opt.parent.value || opt.type !== opt.parent.type)) {
actionButton.show();
} else {
actionButton.hide();
}
var restoreTip = RED.popover.tooltip(actionButton,RED._("subflow.env.restore"));
valueField.change(function(evt) {
var newType = valueField.typedInput('type');
var newValue = valueField.typedInput('value');
if (newType === opt.parent.type && newValue === opt.parent.value) {
actionButton.hide();
} else {
actionButton.show();
}
})
actionButton.click(function(evt) {
evt.preventDefault();
restoreTip.close();
valueField.typedInput('type', opt.parent.type);
valueField.typedInput('value', opt.parent.value);
})
} else {
var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove"));
actionButton.click(function(evt) {
evt.preventDefault();
removeTip.close();
container.parent().addClass("red-ui-editableList-item-deleting")
container.fadeOut(300, function() {
env_container.editableList('removeItem',opt);
});
});
}
},
sortable: false,
removable: false
});
var parentEnv = {};
var envList = [];
if (/^subflow:/.test(node.type)) {
var subflowDef = RED.nodes.subflow(node.type.substring(8));
if (subflowDef.env) {
subflowDef.env.forEach(function(env) {
var item = {
name:env.name,
parent: {
type: env.type,
value: env.value
}
}
envList.push(item);
parentEnv[env.name] = item;
})
}
}
if (node.env) {
for (var i = 0; i < node.env.length; i++) {
var env = node.env[i];
if (parentEnv.hasOwnProperty(env.name)) {
parentEnv[env.name].type = env.type;
parentEnv[env.name].value = env.value;
} else {
envList.push({
name: env.name,
type: env.type,
value: env.value
});
}
}
}
envList.forEach(function(env) {
env_container.editableList('addItem', env);
})
}
function exportEnvList(list) {
if (list) {
var env = [];
list.each(function(i) {
var entry = $(this);
var item = entry.data('data');
var name = item.parent?item.name:entry.find(".node-input-env-name").val();
var valueInput = entry.find(".node-input-env-value");
var value = valueInput.typedInput("value");
var type = valueInput.typedInput("type");
if (!item.parent || (item.parent.value !== value || item.parent.type !== type)) {
var item = {
name: name,
type: type,
value: value
};
env.push(item);
}
});
return env;
}
return null;
}
function isSameEnv(env0, env1) {
return (JSON.stringify(env0) === JSON.stringify(env1));
}
function buildEditForm(container,formId,type,ns,node) {
var dialogForm = $('<form id="'+formId+'" class="form-horizontal" autocomplete="off"></form>').appendTo(container);
dialogForm.html($("script[data-template-name='"+type+"']").html());
ns = ns||"node-red";
@@ -552,6 +704,11 @@ RED.editor = (function() {
}
$(this).attr("data-i18n",keys.join(";"));
});
if ((type === "subflow") || (type === "subflow-template")) {
buildEnvForm(dialogForm, node);
}
// Add dummy fields to prevent 'Enter' submitting the form in some
// cases, and also prevent browser auto-fill of password
// Add in reverse order as they are prepended...
@@ -569,7 +726,13 @@ RED.editor = (function() {
var inputsDiv = $("#node-label-form-inputs");
var outputsDiv = $("#node-label-form-outputs");
var inputCount = node.inputs || node._def.inputs || 0;
var inputCount;
if (node.type === 'subflow') {
inputCount = node.in.length;
} else {
inputCount = node.inputs || node._def.inputs || 0;
}
var children = inputsDiv.children();
var childCount = children.length;
if (childCount === 1 && $(children[0]).hasClass('node-label-form-none')) {
@@ -588,7 +751,7 @@ RED.editor = (function() {
for (i=inputCount;i<childCount;i++) {
$(children[i]).remove();
}
if (outputCount === 0) {
if (inputCount === 0) {
buildLabelRow().appendTo(inputsDiv);
}
}
@@ -598,7 +761,11 @@ RED.editor = (function() {
var formOutputs = $("#node-input-outputs").val();
if (formOutputs === undefined) {
outputCount = node.outputs || node._def.outputs || 0;
if (node.type === 'subflow') {
outputCount = node.out.length;
} else {
inputCount = node.outputs || node._def.outputs || 0;
}
} else if (isNaN(formOutputs)) {
var outputMap = JSON.parse(formOutputs);
var keys = Object.keys(outputMap);
@@ -820,7 +987,7 @@ RED.editor = (function() {
var icon_url = RED.utils.getNodeIcon(node._def,node);
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true, node._def, node);
RED.utils.createIconElement(icon_url, iconContainer, true);
iconButton.click(function(e) {
e.preventDefault();
@@ -834,7 +1001,7 @@ RED.editor = (function() {
showIconPicker(iconRow,node,iconPath,function(newIcon) {
$("#node-settings-icon").text(newIcon||"");
var icon_url = RED.utils.getNodeIcon(node._def,{type:node.type,icon:newIcon});
RED.utils.createIconElement(icon_url, iconContainer, true, node._def, node);
RED.utils.createIconElement(icon_url, iconContainer, true);
});
});
$('<div id="node-settings-icon">').text(node.icon).appendTo(iconButton);
@@ -855,7 +1022,7 @@ RED.editor = (function() {
var inputPlaceholder = node._def.inputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
var outputPlaceholder = node._def.outputLabels?RED._("editor.defaultLabel"):RED._("editor.noDefaultLabel");
$('<div class="form-row"><span style="margin-left: 10px;" data-i18n="editor.labelInputs"></span><div id="node-label-form-inputs"></div></div>').appendTo(dialogForm);
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelInputs"></span><div id="node-label-form-inputs"></div></div>').appendTo(dialogForm);
var inputsDiv = $("#node-label-form-inputs");
if (inputCount > 0) {
for (i=0;i<inputCount;i++) {
@@ -864,7 +1031,7 @@ RED.editor = (function() {
} else {
buildLabelRow().appendTo(inputsDiv);
}
$('<div class="form-row"><span style="margin-left: 10px;" data-i18n="editor.labelOutputs"></span><div id="node-label-form-outputs"></div></div>').appendTo(dialogForm);
$('<div class="form-row"><span style="margin-left: 50px;" data-i18n="editor.labelOutputs"></span><div id="node-label-form-outputs"></div></div>').appendTo(dialogForm);
var outputsDiv = $("#node-label-form-outputs");
if (outputCount > 0) {
for (i=0;i<outputCount;i++) {
@@ -919,7 +1086,7 @@ RED.editor = (function() {
function buildDescriptionForm(container,node) {
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
var toolbarRow = $('<div></div>').appendTo(dialogForm);
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: calc(100% - 36px);"></div>').appendTo(dialogForm);
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: 100%"></div>').appendTo(dialogForm);
$('<div style="height: 100%" class="node-text-editor" id="node-info-input-info-editor" ></div>').appendTo(row);
var nodeInfoEditor = RED.editor.createEditor({
id: "node-info-input-info-editor",
@@ -929,26 +1096,6 @@ RED.editor = (function() {
if (node.info) {
nodeInfoEditor.getSession().setValue(node.info, -1);
}
var toolbar = RED.editor.types._markdown.buildToolbar(toolbarRow,nodeInfoEditor);
$('<button id="node-info-input-info-expand" class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(toolbar);
$('#node-info-input-info-expand').click(function(e) {
e.preventDefault();
var value = nodeInfoEditor.getValue();
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: nodeInfoEditor.getCursorPosition(),
complete: function(v,cursor) {
nodeInfoEditor.setValue(v, -1);
nodeInfoEditor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
nodeInfoEditor.focus();
},300);
}
})
});
return nodeInfoEditor;
}
@@ -1086,6 +1233,13 @@ RED.editor = (function() {
var input = $("#node-input-"+d);
if (input.attr('type') === "checkbox") {
newValue = input.prop('checked');
} else if (input.prop("nodeName") === "select" && input.attr("multiple") === "multiple") {
// An empty select-multiple box returns null.
// Need to treat that as an empty array.
newValue = input.val();
if (newValue == null) {
newValue = [];
}
} else if ("format" in editing_node._def.defaults[d] && editing_node._def.defaults[d].format !== "" && input[0].nodeName === "DIV") {
newValue = input.text();
} else {
@@ -1210,7 +1364,7 @@ RED.editor = (function() {
node.l = false;
} else {
// A link node - default state is false
if (node.hasOwnProperty('l')) {
if (node.hasOwnProperty('l') && node.l) {
changes.l = node.l
changed = true;
}
@@ -1220,7 +1374,7 @@ RED.editor = (function() {
// Checked - show label
if (!/^link (in|out)$/.test(node.type)) {
// Not a link node - default state is true
if (node.hasOwnProperty('l')) {
if (node.hasOwnProperty('l') && !node.l) {
changes.l = node.l
changed = true;
}
@@ -1262,6 +1416,16 @@ RED.editor = (function() {
}
}
if (type === "subflow") {
var old_env = editing_node.env;
var new_env = exportEnvList($("#node-input-env-container").editableList("items"));
if (!isSameEnv(old_env, new_env)) {
editing_node.env = new_env;
changes.env = editing_node.env;
changed = true;
}
}
if (changed) {
var wasChanged = editing_node.changed;
editing_node.changed = true;
@@ -1362,19 +1526,19 @@ RED.editor = (function() {
var nodePropertiesTab = {
id: "editor-tab-properties",
label: "Properties",
name: "Properties",
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog"
};
buildEditForm(nodePropertiesTab.content,"dialog-form",type,ns);
buildEditForm(nodePropertiesTab.content,"dialog-form",type,ns,node);
editorTabs.addTab(nodePropertiesTab);
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
var descriptionTab = {
id: "editor-tab-description",
label: "Description",
name: "Description",
label: RED._("editor-tab.description"),
name: RED._("editor-tab.description"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-file-text-o",
onchange: function() {
@@ -1387,8 +1551,8 @@ RED.editor = (function() {
var appearanceTab = {
id: "editor-tab-appearance",
label: "Appearance",
name: "Appearance",
label: RED._("editor-tab.appearance"),
name: RED._("editor-tab.appearance"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-object-group",
onchange: function() {
@@ -1528,8 +1692,8 @@ RED.editor = (function() {
var nodePropertiesTab = {
id: "editor-tab-cproperties",
label: "Properties",
name: "Properties",
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog"
};
@@ -1539,8 +1703,8 @@ RED.editor = (function() {
if (!node_def.defaults || !node_def.defaults.hasOwnProperty('info')) {
var descriptionTab = {
id: "editor-tab-description",
label: "Description",
name: "Description",
label: RED._("editor-tab.description"),
name: RED._("editor-tab.description"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-file-text-o",
onchange: function() {
@@ -1602,6 +1766,7 @@ RED.editor = (function() {
$("#node-config-dialog-user-count").find("span").text(RED._("editor.nodesUse", {count:editing_config_node.users.length})).parent().show();
}
trayBody.i18n();
trayFooter.i18n();
finishedBuilding = true;
done();
});
@@ -1979,6 +2144,14 @@ RED.editor = (function() {
changed = true;
}
var old_env = editing_node.env;
var new_env = exportEnvList($("#node-input-env-container").editableList("items"));
if (!isSameEnv(old_env, new_env)) {
editing_node.env = new_env;
changes.env = editing_node.env;
changed = true;
}
RED.palette.refresh();
if (changed) {
@@ -2017,9 +2190,17 @@ RED.editor = (function() {
}
}
],
resize: function(dimensions) {
$(".editor-tray-content").height(dimensions.height - 50);
var form = $(".editor-tray-content form").height(dimensions.height - 50 - 40);
resize: function(size) {
// $(".editor-tray-content").height(size.height - 50);
// var form = $(".editor-tray-content form").height(size.height - 50 - 40);
var rows = $("#dialog-form>div:not(.node-input-env-container-row)");
var height = size.height;
for (var i=0; i<rows.size(); i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-env-container-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$("#node-input-env-container").editableList('height',height-80);
},
open: function(tray) {
var trayFooter = tray.find(".editor-tray-footer");
@@ -2051,18 +2232,18 @@ RED.editor = (function() {
var nodePropertiesTab = {
id: "editor-tab-properties",
label: "Properties",
name: "Properties",
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog"
};
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template");
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
editorTabs.addTab(nodePropertiesTab);
var descriptionTab = {
id: "editor-tab-description",
label: "Description",
name: "Description",
label: RED._("editor-tab.description"),
name: RED._("editor-tab.description"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-file-text-o",
onchange: function() {
@@ -2074,8 +2255,8 @@ RED.editor = (function() {
var appearanceTab = {
id: "editor-tab-appearance",
label: "Appearance",
name: "Appearance",
label: RED._("editor-tab.appearance"),
name: RED._("editor-tab.appearance"),
content: $('<div>', {class:"editor-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-object-group",
onchange: function() {
@@ -2146,7 +2327,7 @@ RED.editor = (function() {
}
function showTypeEditor(type, options) {
if (RED.editor.types.hasOwnProperty(type)) {
if (customEditTypes.hasOwnProperty(type)) {
if (editStack.length > 0) {
options.parent = editStack[editStack.length-1].id;
}
@@ -2155,12 +2336,99 @@ RED.editor = (function() {
options.onclose = function() {
editStack.pop();
}
RED.editor.types[type].show(options);
customEditTypes[type].show(options);
} else {
console.log("Unknown type editor:",type);
}
}
function createEditor(options) {
var el = options.element || $("#"+options.id)[0];
var toolbarRow = $("<div>").appendTo(el);
el = $("<div>").appendTo(el).addClass("node-text-editor-container")[0];
var editor = ace.edit(el);
editor.setTheme("ace/theme/tomorrow");
var session = editor.getSession();
session.on("changeAnnotation", function () {
var annotations = session.getAnnotations() || [];
var i = annotations.length;
var len = annotations.length;
while (i--) {
if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
}
if (len > annotations.length) { session.setAnnotations(annotations); }
});
if (options.mode) {
session.setMode(options.mode);
}
if (options.foldStyle) {
session.setFoldStyle(options.foldStyle);
} else {
session.setFoldStyle('markbeginend');
}
if (options.options) {
editor.setOptions(options.options);
} else {
editor.setOptions({
enableBasicAutocompletion:true,
enableSnippets:true,
tooltipFollowsMouse: false
});
}
if (options.readOnly) {
editor.setOption('readOnly',options.readOnly);
editor.container.classList.add("ace_read-only");
}
if (options.hasOwnProperty('lineNumbers')) {
editor.renderer.setOption('showGutter',options.lineNumbers);
}
editor.$blockScrolling = Infinity;
if (options.value) {
session.setValue(options.value,-1);
}
if (options.globals) {
setTimeout(function() {
if (!!session.$worker) {
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
}
},100);
}
if (options.mode === 'ace/mode/markdown') {
$(el).addClass("node-text-editor-container-toolbar");
editor.toolbar = customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
if (options.expandable !== false) {
var expandButton = $('<button class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar);
expandButton.click(function(e) {
e.preventDefault();
var value = editor.getValue();
RED.editor.editMarkdown({
value: value,
width: "Infinity",
cursor: editor.getCursorPosition(),
complete: function(v,cursor) {
editor.setValue(v, -1);
editor.gotoLine(cursor.row+1,cursor.column,false);
setTimeout(function() {
editor.focus();
},300);
}
})
});
}
var helpButton = $('<button class="node-text-editor-help editor-button editor-button-small"><i class="fa fa-question"></i></button>').appendTo($(el).parent());
RED.popover.create({
target: helpButton,
trigger: 'click',
size: "small",
direction: "left",
content: RED._("markdownEditor.format"),
autoClose: 50
});
}
return editor;
}
return {
init: function() {
@@ -2173,14 +2441,7 @@ RED.editor = (function() {
$("#node-dialog-cancel").click();
$("#node-config-dialog-cancel").click();
});
for (var type in RED.editor.types) {
if (RED.editor.types.hasOwnProperty(type)) {
RED.editor.types[type].init();
}
}
},
types: {},
edit: showEditDialog,
editConfig: showEditConfigNodeDialog,
editSubflow: showEditSubflowDialog,
@@ -2193,57 +2454,32 @@ RED.editor = (function() {
validateNode: validateNode,
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
/**
* Show a type editor.
* @param {string} type - the type to display
* @param {object} options - options for the editor
* @function
* @memberof RED.editor
*/
showTypeEditor: showTypeEditor,
createEditor: function(options) {
var editor = ace.edit(options.id||options.element);
editor.setTheme("ace/theme/tomorrow");
var session = editor.getSession();
session.on("changeAnnotation", function () {
var annotations = session.getAnnotations() || [];
var i = annotations.length;
var len = annotations.length;
while (i--) {
if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
}
if (len > annotations.length) { session.setAnnotations(annotations); }
});
if (options.mode) {
session.setMode(options.mode);
}
if (options.foldStyle) {
session.setFoldStyle(options.foldStyle);
} else {
session.setFoldStyle('markbeginend');
}
if (options.options) {
editor.setOptions(options.options);
} else {
editor.setOptions({
enableBasicAutocompletion:true,
enableSnippets:true,
tooltipFollowsMouse: false
});
}
if (options.readOnly) {
editor.setOption('readOnly',options.readOnly);
editor.container.classList.add("ace_read-only");
}
if (options.hasOwnProperty('lineNumbers')) {
editor.renderer.setOption('showGutter',options.lineNumbers);
}
editor.$blockScrolling = Infinity;
if (options.value) {
session.setValue(options.value,-1);
}
if (options.globals) {
setTimeout(function() {
if (!!session.$worker) {
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
}
},100);
}
return editor;
}
/**
* Register a type editor.
* @param {string} type - the type name
* @param {object} options - the editor definition
* @function
* @memberof RED.editor
*/
registerTypeEditor: function(type, definition) {
customEditTypes[type] = definition;
},
/**
* Create a editor ui component
* @param {object} options - the editor options
* @function
* @memberof RED.editor
*/
createEditor: createEditor
}
})();

View File

@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.editor.types._buffer = (function() {
(function() {
var template = '<script type="text/x-red" data-template-name="_buffer"><div id="node-input-buffer-panels"><div id="node-input-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><span class="node-input-buffer-type"><i class="fa fa-exclamation-circle"></i> <span id="node-input-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="node-input-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></span></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="node-input-buffer-str"></div></div></div><div id="node-input-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px"><div class="node-text-editor" id="node-input-buffer-bin"></div></div></div></div></script>';
@@ -45,10 +44,7 @@ RED.editor.types._buffer = (function() {
}
return {
init: function() {
$(template).appendTo(document.body);
},
var definition = {
show: function(options) {
var value = options.value;
var onComplete = options.complete;
@@ -206,4 +202,7 @@ RED.editor.types._buffer = (function() {
RED.tray.show(trayOptions);
}
}
$(template).appendTo(document.body);
RED.editor.registerTypeEditor("_buffer", definition);
})();

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.editor.types._expression = (function() {
(function() {
var template = '<script type="text/x-red" data-template-name="_expression">'+
@@ -46,10 +46,7 @@ RED.editor.types._expression = (function() {
'</script>';
var expressionTestCache = {};
return {
init: function() {
$(template).appendTo(document.body);
},
var definition = {
show: function(options) {
var expressionTestCacheId = options.parent||"_";
var value = options.value;
@@ -349,4 +346,6 @@ RED.editor.types._expression = (function() {
RED.tray.show(trayOptions);
}
}
$(template).appendTo(document.body);
RED.editor.registerTypeEditor("_expression", definition);
})();

View File

@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.editor.types._js = (function() {
(function() {
var template = '<script type="text/x-red" data-template-name="_js"><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-js"></div></div></script>';
return {
init: function() {
$(template).appendTo(document.body);
},
var definition = {
show: function(options) {
var value = options.value;
var onComplete = options.complete;
@@ -99,4 +96,7 @@ RED.editor.types._js = (function() {
RED.tray.show(trayOptions);
}
}
$(template).appendTo(document.body);
RED.editor.registerTypeEditor("_js", definition);
})();

View File

@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.editor.types._json = (function() {
(function() {
var template = '<script type="text/x-red" data-template-name="_json"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button id="node-input-json-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button></div><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div></div></script>';
return {
init: function() {
$(template).appendTo(document.body);
},
var definition = {
show: function(options) {
var value = options.value;
var onComplete = options.complete;
@@ -115,4 +112,6 @@ RED.editor.types._json = (function() {
RED.tray.show(trayOptions);
}
}
$(template).appendTo(document.body);
RED.editor.registerTypeEditor("_json", definition);
})();

View File

@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.editor.types._markdown = (function() {
(function() {
var toolbarTemplate = '<div style="margin-bottom: 5px">'+
'<span class="button-group">'+
@@ -41,7 +40,7 @@ RED.editor.types._markdown = (function() {
'<div id="node-input-markdown-panel-editor" class="red-ui-panel">'+
'<div style="height: 100%; margin: auto; max-width: 1000px;">'+
'<div id="node-input-markdown-toolbar"></div>'+
'<div class="node-text-editor" style="height: calc(100% - 50px)" id="node-input-markdown"></div>'+
'<div class="node-text-editor" style="height: 100%" id="node-input-markdown"></div>'+
'</div>'+
'</div>'+
'<div class="red-ui-panel">'+
@@ -52,24 +51,7 @@ RED.editor.types._markdown = (function() {
var panels;
var styleActions = {
'h1': { newline: true, before:"# ", tooltip:"Heading 1"},
'h2': { newline: true, before:"## ", tooltip:"Heading 2"},
'h3': { newline: true, before:"### ", tooltip:"Heading 3"},
'b': { before:"**", after: "**", tooltip: "Bold" },
'i': { before:"_", after: "_", tooltip: "Italic" },
'code': { before:"`", after: "`", tooltip: "Code" },
'ol': { before:" * ", newline: true, tooltip: "Ordered list" },
'ul': { before:" - ", newline: true, tooltip: "Unordered list" },
'bq': { before:"> ", newline: true, tooltip: "Quote" },
'link': { before:"[", after: "]()", tooltip: "Link"},
'hr': { before:"\n---\n\n", tooltip: "Horizontal rule" }
}
return {
init: function() {
$(template).appendTo(document.body);
},
var definition = {
show: function(options) {
var value = options.value;
var onComplete = options.complete;
@@ -112,7 +94,8 @@ RED.editor.types._markdown = (function() {
expressionEditor = RED.editor.createEditor({
id: 'node-input-markdown',
value: value,
mode:"ace/mode/markdown"
mode:"ace/mode/markdown",
expandable: false
});
var changeTimer;
expressionEditor.getSession().on("change", function() {
@@ -138,11 +121,10 @@ RED.editor.types._markdown = (function() {
}
});
panels.ratio(1);
var toolbar = RED.editor.types._markdown.buildToolbar($("#node-input-markdown-toolbar"), expressionEditor);
$('<span class="button-group" style="float:right">'+
'<button id="node-btn-markdown-preview" class="editor-button toggle single"><i class="fa fa-eye"></i></button>'+
'</span>').appendTo(toolbar);
'</span>').appendTo(expressionEditor.toolbar);
$("#node-btn-markdown-preview").click(function(e) {
e.preventDefault();
@@ -154,7 +136,7 @@ RED.editor.types._markdown = (function() {
panels.ratio(0.5);
}
});
RED.popover.tooltip($("#node-btn-markdown-preview"),"Toggle preview");
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
if (options.cursor) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
@@ -174,6 +156,19 @@ RED.editor.types._markdown = (function() {
},
buildToolbar: function(container, editor) {
var styleActions = {
'h1': { newline: true, before:"# ", tooltip:RED._("markdownEditor.heading1")},
'h2': { newline: true, before:"## ", tooltip:RED._("markdownEditor.heading2")},
'h3': { newline: true, before:"### ", tooltip:RED._("markdownEditor.heading3")},
'b': { before:"**", after: "**", tooltip: RED._("markdownEditor.bold")},
'i': { before:"_", after: "_", tooltip: RED._("markdownEditor.italic")},
'code': { before:"`", after: "`", tooltip: RED._("markdownEditor.code")},
'ol': { before:" * ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
'ul': { before:" - ", newline: true, tooltip: RED._("markdownEditor.unordered-list")},
'bq': { before:"> ", newline: true, tooltip: RED._("markdownEditor.quote")},
'link': { before:"[", after: "]()", tooltip: RED._("markdownEditor.link")},
'hr': { before:"\n---\n\n", tooltip: RED._("markdownEditor.horizontal-rule")}
}
var toolbar = $(toolbarTemplate).appendTo(container);
toolbar.find('button[data-style]').each(function(el) {
var style = styleActions[$(this).data('style')];
@@ -212,4 +207,6 @@ RED.editor.types._markdown = (function() {
return toolbar;
}
}
$(template).appendTo(document.body);
RED.editor.registerTypeEditor("_markdown", definition);
})();

View File

@@ -19,7 +19,7 @@ RED.keyboard = (function() {
var handlers = {};
var partialState;
RED.h = handlers;
var keyMap = {
"left":37,
"up":38,
@@ -161,6 +161,19 @@ RED.keyboard = (function() {
return [keycode,modifiers];
}
function matchHandlerToEvent(evt,handler) {
var target = evt.target;
var depth = 0;
while (target.nodeName !== 'BODY' && target.id !== handler.scope) {
target = target.parentElement;
depth++;
}
if (target.nodeName === 'BODY' && handler.scope !== "*") {
depth = -1;
}
return depth;
}
function resolveKeyEvent(evt) {
var slot = partialState||handlers;
if (evt.ctrlKey || evt.metaKey) {
@@ -175,7 +188,7 @@ RED.keyboard = (function() {
var keyCode = firefoxKeyCodeMap[evt.keyCode] || evt.keyCode;
if (slot && slot[keyCode]) {
var handler = slot[keyCode];
if (!handler.scope) {
if (!handler.handlers) {
if (partialState) {
partialState = null;
return resolveKeyEvent(evt);
@@ -186,14 +199,19 @@ RED.keyboard = (function() {
} else {
return null;
}
} else if (handler.scope && handler.scope !== "*") {
var target = evt.target;
while (target.nodeName !== 'BODY' && target.id !== handler.scope) {
target = target.parentElement;
}
if (target.nodeName === 'BODY') {
handler = null;
} else {
var depth = Infinity;
var matchedHandler;
var i = 0;
var l = handler.handlers.length;
for (i=0;i<l;i++) {
var d = matchHandlerToEvent(evt,handler.handlers[i]);
if (d > -1 && d < depth) {
depth = d;
matchedHandler = handler.handlers[i];
}
}
handler = matchedHandler;
}
partialState = null;
return handler;
@@ -265,6 +283,8 @@ RED.keyboard = (function() {
slot = slot[key];
//slot[key] = {scope: scope, ondown:cbdown};
}
slot.handlers = slot.handlers || [];
slot.handlers.push({scope:scope,ondown:cbdown})
slot.scope = scope;
slot.ondown = cbdown;
}
@@ -315,6 +335,8 @@ RED.keyboard = (function() {
}
delete slot.scope;
delete slot.ondown;
// TODO: this wipes everything! Need to have something to identify handler
delete slot.handlers;
}
var cmdCtrlKey = '<span class="help-key">'+(isMac?'&#8984;':'Ctrl')+'</span>';

View File

@@ -45,7 +45,7 @@ RED.library = (function() {
a = document.createElement("a");
a.href="#";
var label = i.replace(/^@.*\//,"").replace(/^node-red-contrib-/,"").replace(/^node-red-node-/,"").replace(/-/," ").replace(/_/," ");
a.innerHTML = label;
a.innerText = label;
li.appendChild(a);
li.appendChild(buildMenu(data.d[i],root+(root!==""?"/":"")+i));
ul.appendChild(li);
@@ -58,7 +58,7 @@ RED.library = (function() {
li = document.createElement("li");
a = document.createElement("a");
a.href="#";
a.innerHTML = data.f[i];
a.innerText = data.f[i];
a.flowName = root+(root!==""?"/":"")+data.f[i];
a.onclick = function() {
$.get('library/flows/'+this.flowName, function(data) {
@@ -125,8 +125,8 @@ RED.library = (function() {
li.onclick = (function () {
var dirName = v;
return function(e) {
var bcli = $('<li class="active"><span class="divider">/</span> <a href="#">'+dirName+'</a></li>');
$("a",bcli).click(function(e) {
var bcli = $('<li class="active"><span class="divider">/</span> </li>');
$('<a href="#"></a>').text(dirName).appendTo(bcli).click(function(e) {
$(this).parent().nextAll().remove();
$.getJSON("library/"+options.url+root+dirName,function(data) {
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
@@ -141,12 +141,13 @@ RED.library = (function() {
});
}
})();
li.innerHTML = '<i class="fa fa-folder"></i> '+v+"</i>";
$('<i class="fa fa-folder"></i>').appendTo(li);
$('<span>').text(" "+v).appendTo(li);
ul.appendChild(li);
} else {
// file
li = buildFileListItem(v);
li.innerHTML = v.name;
li.innerText = v.name;
li.onclick = (function() {
var item = v;
return function(e) {
@@ -458,7 +459,10 @@ RED.library = (function() {
click: function() {
//TODO: move this to RED.library
var flowName = $("#node-input-library-filename").val();
if (!/^\s*$/.test(flowName)) {
flowName = flowName.trim();
if(flowName === "" || flowName.endsWith("/")) {
RED.notify(RED._("library.invalidFilename"),"warning");
} else {
$.ajax({
url:'library/flows/'+flowName,
type: "POST",

View File

@@ -16,6 +16,10 @@
RED.notifications = (function() {
/*
If RED.notifications.hide is set to true, all notifications will be hidden.
This is to help with UI testing in certain cases and not intended for the
end-user.
// Example usage for a modal dialog with buttons
var myNotification = RED.notify("This is the message to display",{
modal: true,
@@ -52,6 +56,11 @@ RED.notifications = (function() {
type = options.type;
}
if (options.id && persistentNotifications.hasOwnProperty(options.id)) {
persistentNotifications[options.id].update(msg,options);
return persistentNotifications[options.id];
}
if (options.modal) {
$("#full-shade").show();
}
@@ -108,7 +117,9 @@ RED.notifications = (function() {
$("#notifications").append(n);
$(n).slideDown(300);
if (!RED.notifications.hide) {
$(n).slideDown(300);
}
n.close = (function() {
var nn = n;
return function() {
@@ -123,9 +134,13 @@ RED.notifications = (function() {
notificationButtonWrapper.hide();
}
}
$(nn).slideUp(300, function() {
if (!RED.notifications.hide) {
$(nn).slideUp(300, function() {
nn.parentNode.removeChild(nn);
});
} else {
nn.parentNode.removeChild(nn);
});
}
if (options.modal) {
$("#full-shade").hide();
}
@@ -138,7 +153,9 @@ RED.notifications = (function() {
return
}
nn.hidden = true;
$(nn).slideUp(300);
if (!RED.notifications.hide) {
$(nn).slideUp(300);
}
}
})();
n.showNotification = (function() {
@@ -148,7 +165,9 @@ RED.notifications = (function() {
return
}
nn.hidden = false;
$(nn).slideDown(300);
if (!RED.notifications.hide) {
$(nn).slideDown(300);
}
}
})();
@@ -167,7 +186,9 @@ RED.notifications = (function() {
if (typeof options === 'number') {
timeout = options;
} else if (options !== undefined) {
timeout = options.timeout;
if (!options.fixed) {
timeout = options.timeout || 5000;
}
if (options.buttons) {
var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn)
options.buttons.forEach(function(buttonDef) {
@@ -189,6 +210,11 @@ RED.notifications = (function() {
}
if (nn.hidden) {
nn.showNotification();
} else if (!options || !options.silent){
$(nn).addClass("notification-shake-horizontal");
setTimeout(function() {
$(nn).removeClass("notification-shake-horizontal");
},300);
}
}
@@ -207,7 +233,9 @@ RED.notifications = (function() {
currentNotifications.push(n);
if (options.id) {
persistentNotifications[options.id] = n;
notificationButtonWrapper.show();
if (options.fixed) {
notificationButtonWrapper.show();
}
}
c+=1;
return n;

View File

@@ -289,7 +289,7 @@ RED.palette.editor = (function() {
}
if (moduleInfo.pending_version) {
nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').show();
nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').css('display', 'inline-block');
} else if (loadedIndex.hasOwnProperty(module)) {
if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
nodeEntry.updateButton.show();
@@ -321,7 +321,7 @@ RED.palette.editor = (function() {
var catalogueLoadStart;
var catalogueLoadErrors = false;
var activeSort = sortModulesAZ;
var activeSort = sortModulesRelevance;
function handleCatalogResponse(err,catalog,index,v) {
catalogueLoadStatus.push(err||v);
@@ -333,6 +333,9 @@ RED.palette.editor = (function() {
if (m.keywords) {
m.index = m.index.concat(m.keywords);
}
if (m.types) {
m.index = m.index.concat(m.types);
}
if (m.updated_at) {
m.timestamp = new Date(m.updated_at).getTime();
} else {
@@ -413,6 +416,17 @@ RED.palette.editor = (function() {
packageList.editableList('addItem',{start:10,more:filteredList.length-10})
}
}
function sortModulesRelevance(A,B) {
var currentFilter = searchInput.searchBox('value').trim();
if (currentFilter === "") {
return sortModulesAZ(A,B);
}
var i = A.info.index.indexOf(currentFilter) - B.info.index.indexOf(currentFilter);
if (i === 0) {
return sortModulesAZ(A,B);
}
return i;
}
function sortModulesAZ(A,B) {
return A.info.id.localeCompare(B.info.id);
}
@@ -747,32 +761,29 @@ RED.palette.editor = (function() {
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
var sortAZ = $('<a href="#" class="sidebar-header-button-toggle selected" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
var sortRecent = $('<a href="#" class="sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
var sortRelevance = $('<a href="#" class="palette-editor-install-sort-option sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
var sortAZ = $('<a href="#" class="palette-editor-install-sort-option sidebar-header-button-toggle" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup);
var sortRecent = $('<a href="#" class="palette-editor-install-sort-option sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup);
sortAZ.click(function(e) {
e.preventDefault();
if ($(this).hasClass("selected")) {
return;
}
$(this).addClass("selected");
sortRecent.removeClass("selected");
activeSort = sortModulesAZ;
refreshFilteredItems();
var sortOpts = [
{button: sortRelevance, func: sortModulesRelevance},
{button: sortAZ, func: sortModulesAZ},
{button: sortRecent, func: sortModulesRecent}
]
sortOpts.forEach(function(opt) {
opt.button.click(function(e) {
e.preventDefault();
if ($(this).hasClass("selected")) {
return;
}
$(".palette-editor-install-sort-option").removeClass("selected");
$(this).addClass("selected");
activeSort = opt.func;
refreshFilteredItems();
});
});
sortRecent.click(function(e) {
e.preventDefault();
if ($(this).hasClass("selected")) {
return;
}
$(this).addClass("selected");
sortAZ.removeClass("selected");
activeSort = sortModulesRecent;
refreshFilteredItems();
});
var refreshSpan = $('<span>').appendTo(toolBar);
var refreshButton = $('<a href="#" class="sidebar-header-button"><i class="fa fa-refresh"></i></a>').appendTo(refreshSpan);
refreshButton.click(function(e) {
@@ -814,10 +825,23 @@ RED.palette.editor = (function() {
$('<a target="_blank" class="palette-module-link"><i class="fa fa-external-link"></i></a>').attr('href',entry.url).appendTo(titleRow);
var descRow = $('<div class="palette-module-meta"></div>').appendTo(headerRow);
$('<div>',{class:"palette-module-description"}).text(entry.description).appendTo(descRow);
var metaRow = $('<div class="palette-module-meta"></div>').appendTo(headerRow);
$('<span class="palette-module-version"><i class="fa fa-tag"></i> '+entry.version+'</span>').appendTo(metaRow);
$('<span class="palette-module-updated"><i class="fa fa-calendar"></i> '+formatUpdatedAt(entry.updated_at)+'</span>').appendTo(metaRow);
var duplicateType = false;
if (entry.types && entry.types.length > 0) {
for (var i=0;i<entry.types.length;i++) {
var nodeset = RED.nodes.registry.getNodeSetForType(entry.types[i]);
if (nodeset) {
duplicateType = nodeset.module;
break;
}
}
// $('<div>',{class:"palette-module-meta"}).text(entry.types.join(",")).appendTo(headerRow);
}
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
var buttonGroup = $('<div>',{class:"palette-module-button-group"}).appendTo(buttonRow);
var installButton = $('<a href="#" class="editor-button editor-button-small"></a>').text(RED._('palette.editor.install')).appendTo(buttonGroup);
@@ -830,6 +854,16 @@ RED.palette.editor = (function() {
if (nodeEntries.hasOwnProperty(entry.id)) {
installButton.addClass('disabled');
installButton.text(RED._('palette.editor.installed'));
} else if (duplicateType) {
installButton.addClass('disabled');
installButton.text(RED._('palette.editor.conflict'));
RED.popover.create({
target:installButton,
content: RED._('palette.editor.conflictTip',{module:duplicateType}),
trigger:"hover",
direction:"bottom",
delay:{show:750,hide:50}
})
}
object.elements = {

View File

@@ -144,7 +144,7 @@ RED.palette = (function() {
function setIcon(element,sf) {
var icon_url = RED.utils.getNodeIcon(sf._def);
var iconContainer = element.find(".palette_icon_container");
RED.utils.createIconElement(icon_url, iconContainer, true, sf._def);
RED.utils.createIconElement(icon_url, iconContainer, true);
}
function escapeNodeType(nt) {
@@ -182,7 +182,7 @@ RED.palette = (function() {
if (def.icon) {
var icon_url = RED.utils.getNodeIcon(def);
var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d);
RED.utils.createIconElement(icon_url, iconContainer, true, def);
RED.utils.createIconElement(icon_url, iconContainer, true);
}
d.style.backgroundColor = RED.utils.getNodeColor(nt,def);

View File

@@ -21,7 +21,7 @@ RED.projects = (function() {
var activeProject;
function reportUnexpectedError(error) {
var notification;
if (error.error === 'git_missing_user') {
if (error.code === 'git_missing_user') {
notification = RED.notify("<p>"+RED._("projects.errors.no-username-email")+"</p>",{
fixed: true,
type:'error',
@@ -43,7 +43,7 @@ RED.projects = (function() {
})
} else {
console.log(error);
notification = RED.notify("<p>"+RED._("projects.errors.unexpected")+":</p><p>"+error.message+"</p><small>"+RED._("projects.errors.code")+": "+error.error+"</small>",{
notification = RED.notify("<p>"+RED._("projects.errors.unexpected")+":</p><p>"+error.message+"</p><small>"+RED._("projects.errors.code")+": "+error.code+"</small>",{
fixed: true,
modal: true,
type: 'error',
@@ -103,6 +103,18 @@ RED.projects = (function() {
return container;
},
buttons: [
{
// id: "clipboard-dialog-cancel",
text: "Open existing project", //RED._("projects.welcome.not-right-now"),
class: "secondary",
click: function() {
createProjectOptions = {
action: "open"
}
show('git-config');
}
},
{
// id: "clipboard-dialog-cancel",
text: RED._("projects.welcome.not-right-now"),
@@ -187,6 +199,8 @@ RED.projects = (function() {
show('project-details');
} else if (createProjectOptions.action === "clone") {
show('clone-project');
} else if (createProjectOptions.action === "open") {
show('create',{screen:'open'})
}
}
}
@@ -532,7 +546,7 @@ RED.projects = (function() {
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="editor-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).click(function(e) {
e.preventDefault();
$('#projects-dialog-cancel').click();
dialog.dialog( "close" );
RED.userSettings.show('gitconfig');
setTimeout(function() {
$("#user-settings-gitconfig-add-key").click();
@@ -1493,7 +1507,7 @@ RED.projects = (function() {
return switchProject(selectedProject.name,function(err,data) {
dialog.dialog( "close" );
if (err) {
if (err.error !== 'credentials_load_failed') {
if (err.code !== 'credentials_load_failed') {
console.log(RED._("projects.create.unexpected_error"),err)
}
}
@@ -1587,7 +1601,6 @@ RED.projects = (function() {
sendRequest({
url: "projects/"+name,
type: "PUT",
requireCleanWorkspace: true,
responses: {
200: function(data) {
done(null,data);
@@ -1879,7 +1892,6 @@ RED.projects = (function() {
function sendRequest(options,body) {
// dialogBody.hide();
// console.log(options.url,body);
if (options.requireCleanWorkspace && RED.nodes.dirty()) {
var thenCallback;
var alwaysCallback;
@@ -1938,7 +1950,7 @@ RED.projects = (function() {
resultCallback = responses;
resultCallbackArgs = {error:responses.statusText};
return;
} else if (options.handleAuthFail !== false && xhr.responseJSON.error === 'git_auth_failed') {
} else if (options.handleAuthFail !== false && xhr.responseJSON.code === 'git_auth_failed') {
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
var message = $('<div>'+
@@ -2026,8 +2038,8 @@ RED.projects = (function() {
]
});
return;
} else if (responses[xhr.responseJSON.error]) {
resultCallback = responses[xhr.responseJSON.error];
} else if (responses[xhr.responseJSON.code]) {
resultCallback = responses[xhr.responseJSON.code];
resultCallbackArgs = xhr.responseJSON;
return;
} else if (responses['*']) {
@@ -2036,10 +2048,12 @@ RED.projects = (function() {
return;
}
}
console.log(responses)
console.log(RED._("projects.send-req.unhandled")+":");
console.log(xhr);
console.log(textStatus);
console.log(err);
console.log(stack);
}).always(function() {
var delta = Date.now() - start;
delta = Math.max(0,500-delta);
@@ -2344,7 +2358,15 @@ RED.projects = (function() {
RED.notify(RED._("user.errors.notAuthorized"),"error");
return;
}
show('create',{screen:'open'})
if (RED.nodes.dirty()) {
return requireCleanWorkspace(function(cancelled) {
if (!cancelled) {
show('create',{screen:'open'})
}
})
} else {
show('create',{screen:'open'})
}
},
showCredentialsPrompt: function() { //TODO: rename this function
if (!RED.user.hasPermission("projects.write")) {

View File

@@ -326,6 +326,7 @@ RED.sidebar.versionControl = (function() {
.appendTo(bg)
.click(function(evt) {
evt.preventDefault();
evt.stopPropagation();
refresh(true);
});
RED.popover.tooltip(refreshButton,RED._("sidebar.project.versionControl.refreshChanges"));
@@ -559,6 +560,7 @@ RED.sidebar.versionControl = (function() {
.appendTo(bg)
.click(function(evt) {
evt.preventDefault();
evt.stopPropagation();
refresh(true,true);
})
RED.popover.tooltip(refreshButton,RED._("sidebar.project.versionControl.refreshCommitHistory"))

View File

@@ -203,7 +203,7 @@ RED.search = (function() {
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true, node._def, node);
RED.utils.createIconElement(icon_url, iconContainer, true);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
if (node.z) {

View File

@@ -16,34 +16,36 @@
RED.subflow = (function() {
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow">'+
'<div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div>'+
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
'</script>';
var _subflowEditTemplate = '<script type="text/x-red" data-template-name="subflow"><div class="form-row"><label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label><input type="text" id="node-input-name"></div></script>';
var _subflowTemplateEditTemplate = '<script type="text/x-red" data-template-name="subflow-template">'+
'<div class="form-row"><i class="fa fa-tag"></i> <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"></div>'+
'<div class="form-row"><i class="fa fa-folder-o"></i> <label for="subflow-input-category" data-i18n="editor:subflow.category"></label><select style="width: 250px;" id="subflow-input-category"></select><input style="display:none; margin-left: 10px; width:calc(100% - 250px)" type="text" id="subflow-input-custom-category"></div>'+
'<div class="form-row" style="margin-bottom: 0px;"><label style="width: auto;" data-i18n="[append]editor:editor-tab.env"><i class="fa fa-th-list"></i> </label></div>'+
'<div class="form-row node-input-env-container-row"><ol id="node-input-env-container"></ol></div>'+
'<div class="form-row form-tips" id="subflow-dialog-user-count"></div>'+
'</script>';
function getSubflow() {
return RED.nodes.subflow(RED.workspaces.active());
}
function findAvailableSubflowIOPosition(subflow,isInput) {
var pos = {x:50,y:30};
if (!isInput) {
pos.x += 110;
}
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
var port;
if (i < subflow.out.length) {
port = subflow.out[i];
} else {
port = subflow.in[i-subflow.out.length];
}
var ports = [].concat(subflow.out).concat(subflow.in);
if (subflow.status) {
ports.push(subflow.status);
}
ports.sort(function(A,B) {
return A.x-B.x;
});
for (var i=0; i<ports.length; i++) {
var port = ports[i];
if (port.x == pos.x && port.y == pos.y) {
pos.x += 55;
i=0;
}
}
return pos;
@@ -191,6 +193,61 @@ RED.subflow = (function() {
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
}
function addSubflowStatus() {
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (subflow.status) {
return;
}
var position = findAvailableSubflowIOPosition(subflow,false);
var statusNode = {
type:"subflow",
direction:"status",
z:subflow.id,
x:position.x,
y:position.y,
id:RED.nodes.id()
};
subflow.status = statusNode;
subflow.dirty = true;
var wasDirty = RED.nodes.dirty();
var wasChanged = subflow.changed;
subflow.changed = true;
var result = refresh(true);
var historyEvent = {
t:'edit',
node:subflow,
dirty:wasDirty,
changed:wasChanged,
subflow: { status: true }
};
RED.history.push(historyEvent);
RED.view.select();
RED.nodes.dirty(true);
RED.view.redraw();
$("#workspace-subflow-status").prop("checked",!!subflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
}
function removeSubflowStatus() {
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (!subflow.status) {
return;
}
var subflowRemovedLinks = [];
RED.nodes.eachLink(function(l) {
if (l.target.type == "subflow" && l.target.z == subflow.id && l.target.direction == "status") {
subflowRemovedLinks.push(l);
}
});
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
delete subflow.status;
$("#workspace-subflow-status").prop("checked",!!subflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!subflow.status);
return { links: subflowRemovedLinks }
}
function refresh(markChange) {
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
refreshToolbar(activeSubflow);
@@ -219,12 +276,17 @@ RED.subflow = (function() {
}
}
}
function refreshToolbar(activeSubflow) {
if (activeSubflow) {
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
$("#workspace-subflow-output .spinner-value").text(activeSubflow.out.length);
$("#workspace-subflow-status").prop("checked",!!activeSubflow.status);
$("#workspace-subflow-status").parent().parent().toggleClass("active",!!activeSubflow.status);
}
}
@@ -232,22 +294,32 @@ RED.subflow = (function() {
var toolbar = $("#workspace-toolbar");
toolbar.empty();
// Edit properties
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
// Inputs
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
'<div style="display: inline-block;" class="button-group">'+
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
'</div>').appendTo(toolbar);
// Outputs
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
'<div class="spinner-value">3</div>'+
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
'</div>').appendTo(toolbar);
// Status
$('<span class="button-group"><span class="button" style="padding:0"><label for="workspace-subflow-status"><input id="workspace-subflow-status" type="checkbox"> <span data-i18n="subflow.status"></span></label></span></span>').appendTo(toolbar);
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
// Delete
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
toolbar.i18n();
@@ -274,6 +346,7 @@ RED.subflow = (function() {
RED.view.redraw(true);
}
});
$("#workspace-subflow-output-add").click(function(event) {
event.preventDefault();
addSubflowOutput();
@@ -283,6 +356,7 @@ RED.subflow = (function() {
event.preventDefault();
addSubflowInput();
});
$("#workspace-subflow-input-remove").click(function(event) {
event.preventDefault();
var wasDirty = RED.nodes.dirty();
@@ -307,6 +381,33 @@ RED.subflow = (function() {
}
});
$("#workspace-subflow-status").change(function(evt) {
if (this.checked) {
addSubflowStatus();
} else {
var currentStatus = activeSubflow.status;
var wasChanged = activeSubflow.changed;
var result = removeSubflowStatus();
if (result) {
activeSubflow.changed = true;
var wasDirty = RED.nodes.dirty();
RED.history.push({
t:'delete',
links:result.links,
changed: wasChanged,
dirty:wasDirty,
subflow: {
id: activeSubflow.id,
status: currentStatus
}
});
RED.view.select();
RED.nodes.dirty(true);
RED.view.redraw();
}
}
})
$("#workspace-subflow-edit").click(function(event) {
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
event.preventDefault();
@@ -328,6 +429,7 @@ RED.subflow = (function() {
$("#chart").css({"margin-top": "40px"});
$("#workspace-toolbar").show();
}
function hideWorkspaceToolbar() {
$("#workspace-toolbar").hide().empty();
$("#chart").css({"margin-top": "0"});
@@ -373,6 +475,7 @@ RED.subflow = (function() {
subflows: [activeSubflow]
}
}
function init() {
RED.events.on("workspace:change",function(event) {
var activeSubflow = RED.nodes.subflow(event.workspace);
@@ -430,6 +533,13 @@ RED.subflow = (function() {
RED.nodes.dirty(true);
}
function snapToGrid(x) {
if (RED.settings.get("editor").view['view-snap-grid']) {
x = Math.round(x / RED.view.gridSize()) * RED.view.gridSize();
}
return x;
}
function convertToSubflow() {
var selection = RED.view.selection();
if (!selection.nodes) {
@@ -445,7 +555,6 @@ RED.subflow = (function() {
var candidateOutputs = [];
var candidateInputNodes = {};
var boundingBox = [selection.nodes[0].x,
selection.nodes[0].y,
selection.nodes[0].x,
@@ -461,8 +570,14 @@ RED.subflow = (function() {
Math.max(boundingBox[3],n.y)
]
}
var offsetX = snapToGrid(boundingBox[0] - 200);
var offsetY = snapToGrid(boundingBox[1] - 80);
var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2];
var center = [
snapToGrid((boundingBox[2]+boundingBox[0]) / 2),
snapToGrid((boundingBox[3]+boundingBox[1]) / 2)
];
RED.nodes.eachLink(function(link) {
if (nodes[link.source.id] && nodes[link.target.id]) {
@@ -519,8 +634,8 @@ RED.subflow = (function() {
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
type:"subflow",
direction:"in",
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
y:candidateInputNodes[v].y,
x:snapToGrid(candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80 - offsetX),
y:snapToGrid(candidateInputNodes[v].y - offsetY),
z:subflowId,
i:index,
id:RED.nodes.id(),
@@ -528,9 +643,9 @@ RED.subflow = (function() {
}}),
out: candidateOutputs.map(function(v,i) { var index = i; return {
type:"subflow",
direction:"in",
x:v.source.x+(v.source.w/2)+80,
y:v.source.y,
direction:"out",
x:snapToGrid(v.source.x+(v.source.w/2)+80 - offsetX),
y:snapToGrid(v.source.y - offsetY),
z:subflowId,
i:index,
id:RED.nodes.id(),
@@ -605,6 +720,8 @@ RED.subflow = (function() {
return isLocalLink;
});
}
n.x -= offsetX;
n.y -= offsetY;
n.z = subflow.id;
}
@@ -613,7 +730,9 @@ RED.subflow = (function() {
nodes:[subflowInstance.id],
links:new_links,
subflow: {
subflow: subflow
subflow: subflow,
offsetX: offsetX,
offsetY: offsetY
},
activeWorkspace: RED.workspaces.active(),
@@ -627,8 +746,6 @@ RED.subflow = (function() {
RED.view.redraw(true);
}
return {
init: init,
createSubflow: createSubflow,
@@ -636,6 +753,7 @@ RED.subflow = (function() {
removeSubflow: removeSubflow,
refresh: refresh,
removeInput: removeSubflowInput,
removeOutput: removeSubflowOutput
removeOutput: removeSubflowOutput,
removeStatus: removeSubflowStatus
}
})();

View File

@@ -18,11 +18,13 @@ RED.sidebar.config = (function() {
var content = document.createElement("div");
content.className = "sidebar-node-config";
content.id = "sidebar-node-config";
content.tabIndex = 0;
$('<div class="button-group sidebar-header">'+
$('<div class="sidebar-header"><span class="button-group">'+
'<a class="sidebar-header-button-toggle selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
'<a class="sidebar-header-button-toggle" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
'</div>'
'</span></div>'
).appendTo(content);
@@ -138,15 +140,32 @@ RED.sidebar.config = (function() {
}
var entry = $('<li class="palette_node config_node palette_node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
entry.data('node',node.id);
$('<div class="palette_label"></div>').text(label).appendTo(entry);
if (node._def.hasUsers !== false) {
var iconContainer = $('<div/>',{class:"palette_icon_container palette_icon_container_right"}).appendTo(entry);
var butt = $('<a href="#"/>').click(function(e) { e.preventDefault(); RED.search.show(node.id); }).text(node.users.length).appendTo(iconContainer);
if (node.users.length === 0) {
iconContainer.text(0);
} else {
$('<a href="#"/>').click(function(e) {
e.stopPropagation();
e.preventDefault();
RED.search.show(node.id);
}).text(node.users.length).appendTo(iconContainer);
}
RED.popover.tooltip(iconContainer,RED._('editor.nodesUse',{count:node.users.length}));
if (node.users.length === 0) {
entry.addClass("config_node_unused");
}
}
entry.on('click',function(e) {
RED.view.select(false);
if (e.metaKey) {
$(this).toggleClass("selected");
} else {
$(content).find(".palette_node").removeClass("selected");
$(this).addClass("selected");
}
RED.sidebar.info.refresh(node);
});
entry.on('dblclick',function(e) {
@@ -225,7 +244,60 @@ RED.sidebar.config = (function() {
action: "core:show-config-tab",
onchange: function() { refreshConfigNodeList(); }
});
RED.actions.add("core:show-config-tab",function() {RED.sidebar.show('config')});
RED.actions.add("core:show-config-tab", function() {RED.sidebar.show('config')});
RED.actions.add("core:select-all-config-nodes", function() {
$(content).find(".palette_node").addClass("selected");
})
RED.actions.add("core:delete-config-selection", function() {
var selectedNodes = [];
$(content).find(".palette_node.selected").each(function() {
selectedNodes.push($(this).data('node'));
});
if (selectedNodes.length > 0) {
var historyEvent = {
t:'delete',
nodes:[],
changes: {},
dirty: RED.nodes.dirty()
}
selectedNodes.forEach(function(id) {
var node = RED.nodes.node(id);
try {
if (node._def.oneditdelete) {
node._def.oneditdelete.call(node);
}
} catch(err) {
console.log("oneditdelete",node.id,node.type,err.toString());
}
historyEvent.nodes.push(node);
for (var i=0;i<node.users.length;i++) {
var user = node.users[i];
historyEvent.changes[user.id] = {
changed: user.changed,
valid: user.valid
};
for (var d in user._def.defaults) {
if (user._def.defaults.hasOwnProperty(d) && user[d] == id) {
historyEvent.changes[user.id][d] = id
user[d] = "";
user.changed = true;
user.dirty = true;
}
}
RED.editor.validateNode(user);
}
RED.nodes.remove(id);
})
RED.nodes.dirty(true);
RED.view.redraw(true);
RED.history.push(historyEvent);
}
});
RED.events.on("view:selection-changed",function() {
$(content).find(".palette_node").removeClass("selected");
});
$("#workspace-config-node-collapse-all").on("click", function(e) {
e.preventDefault();
@@ -263,7 +335,8 @@ RED.sidebar.config = (function() {
refreshConfigNodeList();
}
});
RED.popover.tooltip($('#workspace-config-node-filter-all'),"Show all config nodes");
RED.popover.tooltip($('#workspace-config-node-filter-unused'),"Show all unused config nodes");
}
function show(id) {

View File

@@ -237,29 +237,83 @@ RED.sidebar.context = (function() {
var propRow = $('<tr class="node-info-node-row"><td class="sidebar-context-property"></td><td></td></tr>').appendTo(container);
var obj = $(propRow.children()[0]);
obj.text(k);
var tools = $('<span class="debug-message-tools button-group"></span>').appendTo(obj);
var tools = $('<span class="button-group"></span>');
var refreshItem = $('<button class="editor-button editor-button-small"><i class="fa fa-refresh"></i></button>').appendTo(tools).click(function(e) {
e.preventDefault();
e.stopPropagation();
$.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
$(propRow.children()[1]).empty();
var payload = data.msg;
var format = data.format;
payload = RED.utils.decodeObject(payload,format);
RED.utils.createObjectElement(payload, {
typeHint: data.format,
sourceId: id+"."+k
}).appendTo(propRow.children()[1]);
if (data.msg !== payload || data.format !== format) {
payload = data.msg;
format = data.format;
tools.detach();
$(propRow.children()[1]).empty();
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: data.format,
sourceId: id+"."+k,
tools: tools
}).appendTo(propRow.children()[1]);
}
})
});
var deleteItem = $('<button class="editor-button editor-button-small"><i class="fa fa-trash"></i></button>').appendTo(tools).click(function(e) {
e.preventDefault();
e.stopPropagation();
var popover = RED.popover.create({
trigger: 'modal',
target: propRow,
direction: "left",
content: function() {
var content = $('<div>');
$('<p data-i18n="sidebar.context.deleteConfirm"></p>').appendTo(content);
var row = $('<p>').appendTo(content);
var bg = $('<span class="button-group"></span>').appendTo(row);
$('<button class="editor-button" data-i18n="common.label.cancel"></button>').appendTo(bg).click(function(e) {
e.preventDefault();
popover.close();
});
bg = $('<span class="button-group"></span>').appendTo(row);
$('<button class="editor-button primary" data-i18n="common.label.delete"></button>').appendTo(bg).click(function(e) {
e.preventDefault();
popover.close();
$.ajax({
url: baseUrl+"/"+k+"?store="+v.store,
type: "DELETE"
}).done(function(data,textStatus,xhr) {
$.getJSON(baseUrl+"/"+k+"?store="+v.store, function(data) {
if (data.format === 'undefined') {
propRow.remove();
if (container.children().length === 0) {
$('<tr class="node-info-node-row red-ui-search-empty blank" colspan="2"><td data-i18n="sidebar.context.empty"></td></tr>').appendTo(container).i18n();
}
} else {
payload = data.msg;
format = data.format;
tools.detach();
$(propRow.children()[1]).empty();
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: data.format,
sourceId: id+"."+k,
tools: tools
}).appendTo(propRow.children()[1]);
}
});
}).fail(function(xhr,textStatus,err) {
})
});
return content.i18n();
}
});
popover.open();
});
var payload = v.msg;
var format = v.format;
payload = RED.utils.decodeObject(payload,format);
RED.utils.createObjectElement(payload, {
RED.utils.createObjectElement(RED.utils.decodeObject(payload,format), {
typeHint: v.format,
sourceId: id+"."+k
sourceId: id+"."+k,
tools: tools
}).appendTo(propRow.children()[1]);
if (contextStores.length > 1) {
$("<span>",{class:"sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))

View File

@@ -140,11 +140,12 @@ RED.tray = (function() {
// tray.body.parent().width(Math.min($("#editor-stack").position().left-8,tray.width));
$("#main-container").scrollLeft(0);
el.css({
right: -(el.width()+10)+"px",
transition: "right 0.25s ease"
});
$("#workspace").scrollLeft(0);
handleWindowResize();
openingTray = true;
setTimeout(function() {

View File

@@ -48,7 +48,7 @@ RED.typeSearch = (function() {
//shade = $('<div>',{class:"red-ui-type-search-shade"}).appendTo("#main-container");
dialog = $("<div>",{id:"red-ui-type-search",class:"red-ui-search red-ui-type-search"}).appendTo("#main-container");
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
searchInput = $('<input type="text">').attr("placeholder",RED._("search.addNode")).appendTo(searchDiv).searchBox({
searchInput = $('<input type="text" id="red-ui-type-search-input">').attr("placeholder",RED._("search.addNode")).appendTo(searchDiv).searchBox({
delay: 50,
change: function() {
search($(this).val());
@@ -79,6 +79,19 @@ RED.typeSearch = (function() {
$(children[selected]).addClass('selected');
ensureSelectedIsVisible();
evt.preventDefault();
} else if ((evt.metaKey || evt.ctrlKey) && evt.keyCode === 13 ) {
// (ctrl or cmd) and enter
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 (n.def.outputs === 0) {
confirm(n);
} else {
addCallback(n.type,true);
}
$("#red-ui-type-search-input").val("").keyup();
}
} else if (evt.keyCode === 13) {
// Enter
var index = Math.max(0,selected);
@@ -133,7 +146,7 @@ RED.typeSearch = (function() {
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, false, def);
RED.utils.createIconElement(icon_url, iconContainer, false);
if (def.inputs > 0) {
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
@@ -191,20 +204,27 @@ RED.typeSearch = (function() {
createDialog();
}
visible = true;
setTimeout(function() {
$(document).on('mousedown.type-search',handleMouseActivity);
$(document).on('mouseup.type-search',handleMouseActivity);
$(document).on('click.type-search',handleMouseActivity);
},200);
} else {
dialog.hide();
searchResultsDiv.hide();
}
refreshTypeList();
$(document).off('mousedown.type-search');
$(document).off('mouseup.type-search');
$(document).off('click.type-search');
setTimeout(function() {
$(document).on('mousedown.type-search',handleMouseActivity);
$(document).on('mouseup.type-search',handleMouseActivity);
$(document).on('click.type-search',handleMouseActivity);
},200);
refreshTypeList(opts);
addCallback = opts.add;
closeCallback = opts.close;
cancelCallback = opts.cancel;
RED.events.emit("type-search:open");
//shade.show();
if ($("#main-container").height() - opts.y - 150 < 0) {
opts.y = opts.y - 235;
}
dialog.css({left:opts.x+"px",top:opts.y+"px"}).show();
searchResultsDiv.slideDown(300);
setTimeout(function() {
@@ -230,7 +250,6 @@ RED.typeSearch = (function() {
$(document).off('click.type-search');
}
}
function getTypeLabel(type, def) {
var label = type;
if (typeof def.paletteLabel !== "undefined") {
@@ -254,21 +273,29 @@ RED.typeSearch = (function() {
return 1;
}
}
function refreshTypeList() {
function applyFilter(filter,type,def) {
return !filter ||
(
(!filter.type || type === filter.type) &&
(!filter.input || def.inputs > 0) &&
(!filter.output || def.outputs > 0)
)
}
function refreshTypeList(opts) {
var i;
searchResults.editableList('empty');
searchInput.searchBox('value','');
selected = -1;
var common = [
'inject','debug','function','change','switch'
];
].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
var recentlyUsed = Object.keys(typesUsed);
recentlyUsed.sort(function(a,b) {
return typesUsed[b]-typesUsed[a];
});
recentlyUsed = recentlyUsed.filter(function(t) {
return common.indexOf(t) === -1;
return applyFilter(opts.filter,t,RED.nodes.getType(t)) && common.indexOf(t) === -1;
});
var items = [];
@@ -313,8 +340,10 @@ RED.typeSearch = (function() {
searchResults.editableList('addItem', item);
}
for (i=0;i<items.length;i++) {
items[i].i = index++;
searchResults.editableList('addItem', items[i]);
if (applyFilter(opts.filter,items[i].type,items[i].def)) {
items[i].i = index++;
searchResults.editableList('addItem', items[i]);
}
}
setTimeout(function() {
selected = 0;
@@ -324,6 +353,7 @@ RED.typeSearch = (function() {
return {
show: show,
refresh: refreshTypeList,
hide: hide
};

View File

@@ -113,7 +113,7 @@ RED.utils = (function() {
var pinnedPaths = {};
var formattedPaths = {};
function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey) {
function addMessageControls(obj,sourceId,key,msg,rootPath,strippedKey,extraTools) {
if (!pinnedPaths.hasOwnProperty(sourceId)) {
pinnedPaths[sourceId] = {}
}
@@ -150,6 +150,10 @@ RED.utils = (function() {
}).toggleClass("selected",isPinned);
obj.toggleClass("debug-message-row-pinned",isPinned);
}
if (extraTools) {
extraTools.addClass("debug-message-tools-other");
extraTools.appendTo(tools);
}
}
function checkExpanded(strippedKey,expandPaths,minRange,maxRange) {
if (expandPaths && expandPaths.length > 0) {
@@ -188,6 +192,14 @@ RED.utils = (function() {
format = 'hex'
}
} else if (format === 'dateMS' || format == 'dateS') {
if ((obj.toString().length===13) && (obj<=2147483647000)) {
format = 'dateML';
} else if ((obj.toString().length===10) && (obj<=2147483647)) {
format = 'dateL';
} else {
format = 'hex'
}
} else if (format === 'dateML' || format == 'dateL') {
format = 'hex';
} else {
format = 'dec';
@@ -206,6 +218,12 @@ RED.utils = (function() {
element.text((new Date(obj)).toISOString());
} else if (format === 'dateS') {
element.text((new Date(obj*1000)).toISOString());
} else if (format === 'dateML') {
var dd = new Date(obj);
element.text(dd.toLocaleString() + " [UTC" + ( dd.getTimezoneOffset()/-60 <=0?"":"+" ) + dd.getTimezoneOffset()/-60 +"]");
} else if (format === 'dateL') {
var ddl = new Date(obj*1000);
element.text(ddl.toLocaleString() + " [UTC" + ( ddl.getTimezoneOffset()/-60 <=0?"":"+" ) + ddl.getTimezoneOffset()/-60 +"]");
} else if (format === 'hex') {
element.text("0x"+(obj).toString(16));
}
@@ -243,6 +261,7 @@ RED.utils = (function() {
var expandPaths = options.expandPaths;
var ontoggle = options.ontoggle;
var exposeApi = options.exposeApi;
var tools = options.tools;
var subElements = {};
var i;
@@ -262,7 +281,7 @@ RED.utils = (function() {
}
header = $('<span class="debug-message-row"></span>').appendTo(element);
if (sourceId) {
addMessageControls(header,sourceId,path,obj,rootPath,strippedKey);
addMessageControls(header,sourceId,path,obj,rootPath,strippedKey,tools);
}
if (!key) {
element.addClass("debug-message-top-level");
@@ -777,15 +796,22 @@ RED.utils = (function() {
}
var iconPath = getDefaultNodeIcon(def, node);
if (def.category === 'subflows') {
if (!isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/node-red/subflow.png";
if (isIconExists(iconPath)) {
if (iconPath.module === "font-awesome") {
return iconPath.module+"/"+iconPath.file;
} else {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
}
}
if (iconPath.module === "font-awesome") {
return iconPath.module+"/"+iconPath.file;
} else {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
// This could be a non-core node trying to use a core icon.
iconPath.module = 'node-red';
if (isIconExists(iconPath)) {
return RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
} else if (def.category === 'subflows') {
return RED.settings.apiRootUrl+"icons/node-red/subflow.png";
} else {
return RED.settings.apiRootUrl+"icons/node-red/arrow-in.png";
}
}
}
@@ -899,10 +925,8 @@ RED.utils = (function() {
* @param iconUrl - Url of icon.
* @param iconContainer - Icon container element with palette_icon_container class.
* @param isLarge - Whether the icon size is large.
* @param def - Default definition of a node.
* @param node - If the icon is a specific node instance, this parameter must be specified. If it is icon template such as an icon on the palette, this must be omitted.
*/
function createIconElement(iconUrl, iconContainer, isLarge, def, node) {
function createIconElement(iconUrl, iconContainer, isLarge) {
// Removes the previous icon when icon was changed.
var iconElement = iconContainer.find(".palette_icon");
if (iconElement.length !== 0) {
@@ -923,13 +947,8 @@ RED.utils = (function() {
faIconElement.addClass("palette_icon_fa fa fa-fw " + faLarge + iconPath.file);
return;
}
// If the specified name is not defined in font-awesome, show the default icon.
if (def) {
var iconPath = RED.utils.getDefaultNodeIcon(def, node);
iconUrl = RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file;
} else {
iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.png"
}
// If the specified name is not defined in font-awesome, show arrow-in icon.
iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.png"
}
var imageIconElement = $('<div/>',{class:"palette_icon"}).appendTo(iconContainer);
imageIconElement.css("backgroundImage", "url("+iconUrl+")");

View File

@@ -0,0 +1,137 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.view.tools = (function() {
function alignToGrid() {
var selection = RED.view.selection();
if (selection.nodes) {
var changedNodes = [];
selection.nodes.forEach(function(n) {
var x = n.w/2 + Math.round((n.x-n.w/2)/RED.view.gridSize())*RED.view.gridSize();
var y = Math.round(n.y/RED.view.gridSize())*RED.view.gridSize();
if (n.x !== x || n.y !== y) {
changedNodes.push({
n:n,
ox: n.x,
oy: n.y,
moved: n.moved
});
n.x = x;
n.y = y;
n.dirty = true;
n.moved = true;
}
});
if (changedNodes.length > 0) {
RED.history.push({t:"move",nodes:changedNodes,dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
var moving_set = null;
var endMoveSet = false;
function endKeyboardMove() {
endMoveSet = false;
if (moving_set.length > 0) {
var ns = [];
for (var i=0;i<moving_set.length;i++) {
ns.push({n:moving_set[i].n,ox:moving_set[i].ox,oy:moving_set[i].oy,moved:moving_set[i].moved});
moving_set[i].n.moved = true;
moving_set[i].n.dirty = true;
delete moving_set[i].ox;
delete moving_set[i].oy;
}
RED.view.redraw();
RED.history.push({t:"move",nodes:ns,dirty:RED.nodes.dirty()});
RED.nodes.dirty(true);
moving_set = null;
}
}
function moveSelection(dx,dy) {
if (moving_set === null) {
var selection = RED.view.selection();
if (selection.nodes) {
moving_set = selection.nodes.map(function(n) { return {n:n}});
}
}
if (moving_set && moving_set.length > 0) {
if (!endMoveSet) {
$(document).one('keyup',endKeyboardMove);
endMoveSet = true;
}
var minX = 0;
var minY = 0;
var node;
for (var i=0;i<moving_set.length;i++) {
node = moving_set[i];
if (node.ox == null && node.oy == null) {
node.ox = node.n.x;
node.oy = node.n.y;
node.moved = node.n.moved;
}
node.n.moved = true;
node.n.dirty = true;
node.n.x += dx;
node.n.y += dy;
node.n.dirty = true;
minX = Math.min(node.n.x-node.n.w/2-5,minX);
minY = Math.min(node.n.y-node.n.h/2-5,minY);
}
if (minX !== 0 || minY !== 0) {
for (var n = 0; n<moving_set.length; n++) {
node = moving_set[n];
node.n.x -= minX;
node.n.y -= minY;
}
}
RED.view.redraw();
}
}
return {
init: function() {
RED.actions.add("core:align-selection-to-grid", alignToGrid);
RED.actions.add("core:move-selection-up", function() { moveSelection(0,-1);});
RED.actions.add("core:move-selection-right", function() { moveSelection(1,0);});
RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);});
RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);});
RED.actions.add("core:step-selection-up", function() { moveSelection(0,-RED.view.gridSize());});
RED.actions.add("core:step-selection-right", function() { moveSelection(RED.view.gridSize(),0);});
RED.actions.add("core:step-selection-down", function() { moveSelection(0,RED.view.gridSize());});
RED.actions.add("core:step-selection-left", function() { moveSelection(-RED.view.gridSize(),0);});
},
/**
* Aligns all selected nodes to the current grid
*/
alignSelectionToGrid: alignToGrid,
/**
* Moves all of the selected nodes by the specified amount
* @param {Number} dx
* @param {Number} dy
*/
moveSelection: moveSelection
}
})();

File diff suppressed because it is too large Load Diff

View File

@@ -107,8 +107,8 @@ RED.workspaces = (function() {
changed = true;
workspace.info = info;
}
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('workspace-disabled',workspace.disabled);
// $("#workspace").toggleClass("workspace-disabled",workspace.disabled);
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('workspace-disabled',!!workspace.disabled);
$("#workspace").toggleClass("workspace-disabled",!!workspace.disabled);
if (changed) {
var historyEvent = {
@@ -125,6 +125,14 @@ RED.workspaces = (function() {
if (!selection.nodes && !selection.links) {
RED.sidebar.info.refresh(workspace);
}
if (changes.hasOwnProperty('disabled')) {
RED.nodes.eachNode(function(n) {
if (n.z === workspace.id) {
n.dirty = true;
}
});
RED.view.redraw();
}
}
RED.tray.close();
}
@@ -138,7 +146,6 @@ RED.workspaces = (function() {
height -= $(rows[i]).outerHeight(true);
}
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
height -= 28;
$(".node-text-editor").css("height",height+"px");
tabflowEditor.resize();
},
@@ -158,7 +165,6 @@ RED.workspaces = (function() {
var row = $('<div class="form-row node-text-editor-row">'+
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
'<div class="node-text-editor-toolbar"></div>'+
'<div style="min-height:250px;" class="node-text-editor" id="node-input-info"></div>'+
'</div>').appendTo(dialogForm);
tabflowEditor = RED.editor.createEditor({
@@ -167,10 +173,6 @@ RED.workspaces = (function() {
value: ""
});
var toolbar = RED.editor.types._markdown.buildToolbar(row.find(".node-text-editor-toolbar"),tabflowEditor);
$('<button id="node-info-input-info-expand" class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(toolbar);
$('#node-info-input-info-expand').click(function(e) {
e.preventDefault();
var value = tabflowEditor.getValue();
@@ -246,9 +248,9 @@ RED.workspaces = (function() {
}
activeWorkspace = tab.id;
event.workspace = activeWorkspace;
// $("#workspace").toggleClass("workspace-disabled",tab.disabled);
RED.events.emit("workspace:change",event);
window.location.hash = 'flow/'+tab.id;
$("#workspace").toggleClass("workspace-disabled",!!tab.disabled);
RED.sidebar.config.refresh();
RED.view.focus();
},

View File

@@ -19,7 +19,3 @@
div.btn-group, a.btn {
@include disable-selection;
}
.dropdown-menu>li>a {
color: #444;
}

View File

@@ -81,6 +81,9 @@
.debug-message-tools-pin {
display: inline-block;
}
.debug-message-tools-other {
display: inline-block;
}
}
}
}
@@ -134,6 +137,9 @@
.debug-message-tools-copy {
display: none;
}
.debug-message-tools-other {
display: none;
}
}
.debug-message-payload {
display: block;

View File

@@ -567,9 +567,15 @@ ul.node-dialog-configm-deploy-list {
td.lineno {
font-family: monospace;
text-align: right;
color: #aaa;
color: #999;
background: #f6f6f6;
padding: 1px 5px;
&.added {
background: #c0f6c0;
}
&.removed {
background: #ffcccc;
}
}
td.lineno:nth-child(3) {
border-left: 1px solid $secondary-border-color;
@@ -578,12 +584,20 @@ ul.node-dialog-configm-deploy-list {
font-family: monospace;
white-space: pre-wrap;
padding: 1px 5px;
border-left: 1px solid #ccc;
span.prefix {
width: 30px;
display: inline-block;
text-align: center;
color: #999;
}
&.added {
border-left-color: #aaeeaa
}
&.removed {
border-left-color: #eebbbb
}
}
td.blank {
background: #f6f6f6;
@@ -592,7 +606,7 @@ ul.node-dialog-configm-deploy-list {
background: #eefaee;
}
td.removed {
background: #fadddd;
background: #ffecec;
}
tr.mergeHeader td {
color: #800080;

View File

@@ -19,6 +19,15 @@
background: #999;
}
.dropdown-menu {
background: #f3f3f3;
border-color: $secondary-border-color;
&>li>a {
color: #444;
}
}
.dropdown-menu * .fa-check-square {
display: none;
color: #e0e0e0;

View File

@@ -209,11 +209,28 @@
}
.node-text-editor {
position: relative;
.node-text-editor-help {
position: absolute;
bottom: 0px;
right: 1px;
border-bottom-right-radius: 5px;
z-Index: 8;
border-bottom: none;
border-right: none;
}
}
.node-text-editor-container {
border:1px solid #ccc;
border-radius:5px;
overflow: hidden;
font-size: 14px !important;
font-family: Menlo, Consolas, 'DejaVu Sans Mono', Courier, monospace !important;
height: 100%;
&.node-text-editor-container-toolbar {
height: calc(100% - 40px);
}
}
.editor-button {
@@ -333,7 +350,7 @@
padding: 10px;
border:1px solid #ccc;
border-radius:5px;
height: calc(100% - 31px);
height: calc(100% - 21px);
overflow-y: scroll;
background: #fff;
}
@@ -343,7 +360,7 @@
top: -3000px;
}
.node-label-form-row {
margin: 5px 0;
margin: 5px 0 0 50px;
label {
margin-right: 20px;
text-align: right;

View File

@@ -92,6 +92,14 @@
stroke-dasharray:10,4;
stroke: #f33;
}
.node_placeholder {
stroke-dasharray:10,4;
stroke: #aaa;
fill: #eee;
opacity: 0.5;
stroke-width: 2;
}
.tool_arrow {
stroke-width: 1;
stroke: #999;
@@ -169,7 +177,16 @@
.node_subflow .node {
stroke-dasharray:8, 3;
}
.workspace-disabled {
.link_line {
stroke-dasharray: 10,5 !important;
stroke-width: 2 !important;
stroke: $link-subflow-color;
}
.node {
stroke-dasharray: 10,4;
}
}
.node_quickadd * {
stroke-dasharray: 12,3;
@@ -185,8 +202,8 @@
}
.port_hovered {
stroke: $port-selected-color;
fill: $port-selected-color;
stroke: $port-selected-color !important;
fill: $port-selected-color !important;
}
.port_quick_link {
@@ -202,7 +219,7 @@
}
.drag_line {
stroke: $node-selected-color;
stroke: $node-selected-color !important;
stroke-width: 3;
fill: none;
pointer-events: none;
@@ -227,10 +244,10 @@
stroke: $link-link-color;
fill: none;
stroke-dasharray: 15,2;
pointer-events: none;
// pointer-events: none;
}
.link_port {
fill: #fff;
fill: #eee;
stroke: $link-link-color;
stroke-width: 1;
}

View File

@@ -202,6 +202,7 @@ span.logo {
#header ul.dropdown-menu {
background: $headerMenuBackground;
border: 1px solid rgba(0,0,0,0.2);
width: 250px !important;
margin-top: 0;
}

View File

@@ -118,6 +118,13 @@
color: $editor-button-color-primary !important;
}
}
&.secondary {
background: none;
&:not(:hover) {
border-color: rgba(0,0,0,0);
}
}
}
.button-group-vertical {
@@ -135,7 +142,7 @@
&.single {
color: $workspace-button-color !important;
background: $workspace-button-background;
&.selected:not(.disabled):not(:disabled) {
color: $workspace-button-toggle-color !important;
background: $workspace-button-background-active;

View File

@@ -54,3 +54,65 @@
.notification-error {
border-color: #AD1625;
}
.notification-shake-horizontal {
-webkit-animation: notification-shake-horizontal 0.3s steps(2, end) both;
animation: notification-shake-horizontal 0.3s steps(2, end) both;
}
@-webkit-keyframes notification-shake-horizontal {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
10%,
30%,
50%,
70% {
-webkit-transform: translateX(-1px);
transform: translateX(-1px);
}
20%,
40%,
60% {
-webkit-transform: translateX(1px);
transform: translateX(1px);
}
// 80% {
// -webkit-transform: translateX(1px);
// transform: translateX(1px);
// }
// 90% {
// -webkit-transform: translateX(-1px);
// transform: translateX(-1px);
// }
}
@keyframes notification-shake-horizontal {
0%,
100% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
10%,
30%,
50%,
70% {
-webkit-transform: translateX(-1px);
transform: translateX(-1px);
}
20%,
40%,
60% {
-webkit-transform: translateX(1px);
transform: translateX(1px);
}
// 80% {
// -webkit-transform: translateX(1px);
// transform: translateX(1px);
// }
// 90% {
// -webkit-transform: translateX(-1px);
// transform: translateX(-1px);
// }
}

View File

@@ -82,7 +82,6 @@
@include component-footer-button;
}
.palette-category {
border-bottom: 1px solid #ccc;
}
@@ -101,6 +100,9 @@
padding-left: 30px;
overflow: hidden;
user-select: none;
&:hover {
background: $palette-header-background !important;
}
}
.palette-header > i {
position: absolute;

View File

@@ -42,11 +42,13 @@
.red-ui-panels.red-ui-panels-horizontal {
height: 100%;
.red-ui-panel {
vertical-align: top;
display: inline-block;
height: 100%;
width: calc(50% - 4px);
}
.red-ui-panels-separator {
vertical-align: top;
border-top: none;
border-bottom: none;
border-left: 1px solid $secondary-border-color;

View File

@@ -146,3 +146,21 @@
border-radius:3px;
padding: 1px 2px;
}
.red-ui-popover .editor-button {
&:not(.primary) {
color: #444 !important;
border-color: rgba(0,0,0,0);
}
&.primary {
border-color: #bbb;
}
&.primary:hover {
border-color: #666 !important;
}
}
.red-ui-popover code {
border: none;
background: none;
color: #ccc;
}

View File

@@ -58,6 +58,7 @@
@import "ui/common/nodeList";
@import "ui/common/checkboxSet";
@import "ui/common/stack";
@import "ui/common/treeList";
@import "dragdrop";

View File

@@ -22,32 +22,50 @@
@include disable-selection;
}
.config-node-list {
margin: 0;
list-style-type: none;
.palette_label {
margin-left: 8px;
line-height: 24px;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.palette_icon_container {
font-size: 12px;
line-height: 30px;
background-color: #e8e8e8;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
}
.config_node {
width: 160px;
height: 30px;
background: #f3f3f3;
color: #666;
cursor: pointer;
.config-node-list {
margin: 0;
list-style-type: none;
.palette_node {
overflow: hidden;
&.selected {
border-color: $node-selected-color;
background-color: #eee;
}
}
.palette_label {
margin-left: 8px;
line-height: 24px;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.palette_icon_container {
font-size: 12px;
line-height: 30px;
background-color: #e8e8e8;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
a {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
color: #666;
&:hover {
text-decoration: none;
background: #ccc;
}
}
}
}
.config_node {
width: 160px;
height: 30px;
background: #f3f3f3;
color: #666;
cursor: pointer;
}
.config_node_type {
color: #999;

View File

@@ -0,0 +1,108 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
.red-ui-treeList {
}
.red-ui-treeList-container {
width: 100%;
height: 100%;
position: relative;
background: #f9f9f9;
border: 1px solid $form-input-border-color;
border-radius: 4px;
box-sizing: border-box;
.red-ui-editableList-border {
border: none;
}
.red-ui-editableList-container {
padding: 0px;
}
.red-ui-editableList-container li {
padding: 0;
border-bottom: none;
.red-ui-editableList-container {
// margin-left: 15px;
}
}
.red-ui-editableList-item-content {
& > .red-ui-treeList-label .fa-angle-right {
transition: transform 0.1s ease-in-out;
}
.red-ui-editableList {
display: none;
}
&.expanded {
& > .red-ui-treeList-label .fa-angle-right {
transform: rotate(90deg)
}
& > .red-ui-editableList {
display: block
}
& > .red-ui-treeList-spinner {
display: block;
}
}
}
}
label.red-ui-treeList-label {
display: block;
width: auto;
}
.red-ui-treeList-label {
@include disable-selection;
padding: 6px 0;
display: block;
color: $form-text-color;
text-decoration: none;
cursor: pointer;
vertical-align: middle;
margin: 0;
&:hover {
background: #f9f9f9;
color: $form-text-color;
text-decoration: none;
}
&:focus {
outline: none;
color: $form-text-color;
text-decoration: none;
}
input {
margin: 0;
}
}
.red-ui-treeList-label-text {
margin-left: 4px;
}
.red-ui-treeList-icon {
display: inline-block;
width: 20px;
text-align: center;
}
.red-ui-treeList-spinner {
display: none;
height: 32px;
background: url(images/spin.svg) 50% 50% no-repeat;
background-size: auto 20px;
}

View File

@@ -33,6 +33,15 @@
transition: right 0.2s ease;
overflow: hidden;
label {
padding: 1px 8px;
margin: 0;
font-size: 12px;
}
input[type="checkbox"] {
margin: 0 3px 0 0 ;
padding: 0;
}
.button {
@include workspace-button;
margin-right: 10px;

View File

@@ -118,11 +118,13 @@
'$count':{ args:[ 'array' ]},
'$each':{ args:[ 'object', 'function' ]},
'$env': { args:[ 'arg' ]},
'$eval': { args: ['expr', 'context']},
'$exists':{ args:[ 'arg' ]},
'$filter':{ args:[ 'array', 'function' ]},
'$floor':{ args:[ 'number' ]},
'$flowContext': {args:['string']},
'$formatBase': {args:['number','radix']},
'$formatInteger': {args:['number', 'picture']},
'$formatNumber': {args:['number', 'picture', 'options']},
'$fromMillis': {args:['number']},
'$globalContext': {args:['string']},
@@ -141,6 +143,7 @@
'$now':{ args:[ ]},
'$number':{ args:[ 'arg' ]},
'$pad': {args:['str', 'width','char']},
'$parseInteger': {args:['string', 'picture']},
'$power':{ args:[ 'base', 'exponent' ]},
'$random':{ args:[ ]},
'$reduce':{ args:[ 'array', 'function' , 'init' ]},

View File

@@ -1,57 +0,0 @@
<script type="text/x-red" data-template-name="sentiment">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
</script>
<script type="text/x-red" data-help-name="sentiment">
<p>Analyses the chosen property, default <code>payload</code>, and adds a <code>sentiment</code> object.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>sentiment <span class="property-type">object</span></dt>
<dd>contains the resulting AFINN-111 sentiment.</dd>
<dt>sentiment.score <span class="property-type">number</span></dt>
<dd>the sentiment score.</dd>
</dl>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>overrides <span class="property-type">object</span></dt>
<dd>an object of word score overrides can be supplied - <code>{ word:score,... }</code>.</dd>
</dl>
<h3>Details</h3>
<p>A score greater than zero is positive and less than zero is negative.</p>
<p>The score typically ranges from -5 to +5, but can go higher and lower.</p>
<p>See <a href="https://github.com/thisandagain/sentiment/blob/master/README.md" target="_blank">the Sentiment docs here</a>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('sentiment',{
category: 'analysis-function',
color:"#E6E0F8",
defaults: {
name: {value:""},
property: {value:"payload",required:true}
},
inputs:1,
outputs:1,
icon: "arrow-in.png",
label: function() {
return this.name||"sentiment";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
}
});
</script>

View File

@@ -1,23 +0,0 @@
module.exports = function(RED) {
"use strict";
var sentiment = require('sentiment');
function SentimentNode(n) {
RED.nodes.createNode(this,n);
this.property = n.property||"payload";
var node = this;
this.on("input", function(msg) {
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
sentiment(value, msg.overrides || null, function (err, result) {
msg.sentiment = result;
node.send(msg);
});
}
else { node.send(msg); } // If no matching property - just pass it on.
});
}
RED.nodes.registerType("sentiment",SentimentNode);
}

View File

@@ -153,30 +153,6 @@
width: 40px !important;
}
</style>
<script type="text/x-red" data-help-name="inject">
<p>Injects a message into a flow either manually or at regular intervals. The message
payload can be a variety of types, including strings, JavaScript objects or the current time.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload<span class="property-type">various</span></dt>
<dd>The configured payload of the message.</dd>
<dt class="optional">topic <span class="property-type">string</span></dt>
<dd>An optional property that can be configured in the node.</dd>
</dl>
<h3>Details</h3>
<p>The Inject node can initiate a flow with a specific payload value.
The default payload is a timestamp of the current time in millisecs since January 1st, 1970.</p>
<p>The node also supports injecting strings, numbers, booleans, JavaScript objects, or flow/global context values.</p>
<p>By default, the node is triggered manually by clicking on its button within the editor. It can also be set to
inject at regular intervals or according to a schedule.</p>
<p>It can also be configured to inject once each time the flows are started.</p>
<p>The maximum <i>Interval</i> that can be specified is about 596 hours / 24 days. However if you are looking at intervals
greater than one day you should consider using a scheduler node that can cope with power outages and restarts.</p>
<p><b>Note</b>: The <i>"Interval between times"</i> and <i>"at a specific time"</i> options use the standard cron system.
This means that 20 minutes will be at the next hour, 20 minutes past and 40 minutes past - not in 20 minutes time.
If you want every 20 minutes from now - use the <i>"interval"</i> option.</p>
<p><b>Note</b>: To include a newline in a string you must use a Function node to create the payload.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('inject',{
@@ -507,20 +483,17 @@ If you want every 20 minutes from now - use the <i>"interval"</i> option.</p>
var key = RED.utils.parseContextKey(payload);
payload = this.payloadType+"."+key.key;
}
var label = (this.name||payload);
var label = this._def.label.call(this);
if (label.length > 30) {
label = label.substring(0,50)+"...";
}
label = label.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
if (this.payloadType === "date") { label = this._("inject.timestamp"); }
if (this.payloadType === "none") { label = this._("inject.blank"); }
var node = this;
$.ajax({
url: "inject/"+this.id,
type:"POST",
success: function(resp) {
RED.notify(node._("inject.success",{label:label}),"success");
RED.notify(node._("inject.success",{label:label}),{type:"success",id:"inject"});
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status == 404) {

View File

@@ -7,6 +7,10 @@
<option value="target" data-i18n="catch.scope.selected"></options>
</select>
</div>
<div class="form-row node-input-uncaught-row">
<input type="checkbox" id="node-input-uncaught" style="display: inline-block; width: auto; vertical-align: top; margin-left: 30px; margin-right: 5px;">
<label for="node-input-uncaught" style="width: auto" data-i18n="catch.label.uncaught"></label>
</div>
<div class="form-row node-input-target-row" style="display: none;">
<div id="node-input-catch-target-container-div" style="min-height: 100px;position: relative; box-sizing: border-box; border-radius: 2px; height: 180px; border: 1px solid #ccc;overflow:hidden; ">
<div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">
@@ -24,31 +28,7 @@
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
</script>
<script type="text/x-red" data-help-name="catch">
<p>Catch errors thrown by nodes on the same tab.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>error.message <span class="property-type">string</span></dt>
<dd>the error message.</dd>
<dt>error.source.id <span class="property-type">string</span></dt>
<dd>the id of the node that threw the error.</dd>
<dt>error.source.type <span class="property-type">string</span></dt>
<dd>the type of the node that threw the error.</dd>
<dt>error.source.name <span class="property-type">string</span></dt>
<dd>the name, if set, of the node that threw the error.</dd>
</dl>
<h3>Details</h3>
<p>If a node throws an error whilst handling a message, the flow will typically
halt. This node can be used to catch those errors and handle them with a
dedicated flow.</p>
<p>By default, the node will catch errors thrown by any node on the same tab. Alternatively
it can be targetted at specific nodes.</p>
<p>When an error is thrown, all matching catch nodes will receive the message.</p>
<p>If an error is thrown within a subflow, the error will get handled by any
catch nodes within the subflow. If none exists, the error will be propagated
up to the tab the subflow instance is on.</p>
<p>If the message already has a <code>error</code> property, it is copied to <code>_error</code>.</p>
</script>
<style>
#node-input-catch-target-container {
position: relative;
@@ -88,13 +68,20 @@
color:"#e49191",
defaults: {
name: {value:""},
scope: {value:null}
scope: {value:null},
uncaught: {value:false}
},
inputs:0,
outputs:1,
icon: "alert.png",
label: function() {
return this.name||(this.scope?this._("catch.catchNodes",{number:this.scope.length}):this._("catch.catch"));
if (this.name) {
return this.name;
}
if (this.scope) {
return this._("catch.catchNodes",{number:this.scope.length});
}
return this.uncaught?this._("catch.catchUncaught"):this._("catch.catch")
},
labelStyle: function() {
return this.name?"node_label_italic":"";
@@ -234,8 +221,10 @@
if (scope === "target") {
createNodeList();
$(".node-input-target-row").show();
$(".node-input-uncaught-row").hide();
} else {
$(".node-input-target-row").hide();
$(".node-input-uncaught-row").show();
}
node.resize();
});
@@ -251,6 +240,7 @@
if (scope === 'all') {
this.scope = null;
} else {
$("#node-input-uncaught").prop("checked",false);
var node = this;
node.scope = [];
$(".node-input-target-node-checkbox").each(function(n) {

View File

@@ -21,6 +21,7 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n);
var node = this;
this.scope = n.scope;
this.uncaught = n.uncaught;
this.on("input",function(msg) {
this.send(msg);
});

View File

@@ -24,24 +24,7 @@
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
</script>
<script type="text/x-red" data-help-name="status">
<p>Report status messages from other nodes on the same tab.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>status.text <span class="property-type">string</span></dt>
<dd>the status text.</dd>
<dt>status.source.type <span class="property-type">string</span></dt>
<dd>the type of the node that reported status.</dd>
<dt>status.source.id <span class="property-type">string</span></dt>
<dd>the id of the node that reported status.</dd>
<dt>status.source.name <span class="property-type">string</span></dt>
<dd>the name, if set, of the node that reported status.</dd>
</dl>
<h3>Details</h3>
<p>This node does not produce a <code>payload</code>.</p>
<p>By default the node reports status for all nodes on the same workspace tab.
It can be configured to selectively report status for individual nodes.</p>
</script>
<style>
#node-input-status-target-container {
position: relative;

View File

@@ -4,7 +4,9 @@
<label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
<input id="node-input-typed-complete" type="text" style="width: 70%">
<input id="node-input-complete" type="hidden">
<input id="node-input-targetType" type="hidden">
</div>
<div class="form-row">
<label for="node-input-tosidebar"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
<label for="node-input-tosidebar" style="width:70%">
@@ -29,16 +31,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="debug">
<p>Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays <code>msg.payload</code>.</p>
<h3>Details</h3>
<p>The debug sidebar provides a structured view of the messages it is sent, making it easier to understand their structure.</p>
<p>JavaScript objects and arrays can be collapsed and expanded as required. Buffer objects can be displayed as raw data or as a string if possible.</p>
<p>Alongside each message, the debug sidebar includes information about the time the message was received, the node that sent it and the type of the message.
Clicking on the source node id will reveal that node within the workspace.</p>
<p>The button on the node can be used to enable or disable its output. It is recommended to disable or remove any Debug nodes that are not being used.</p>
<p>The node can also be configured to send all messages to the runtime log, or to send short (32 characters) to the status text under the debug node.</p>
</script>
<script src="debug/view/debug-utils.js"></script>
<script type="text/javascript">
@@ -52,11 +44,15 @@
tosidebar: {value:true},
console: {value:false},
tostatus: {value:false},
complete: {value:"false", required:true}
complete: {value:"false", required:true},
targetType: {value:undefined}
},
label: function() {
var suffix = "";
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
if (this.targetType === "jsonata") {
return (this.name || "JSONata") + suffix;
}
if (this.complete === true || this.complete === "true") {
return (this.name||"msg") + suffix;
} else {
@@ -255,6 +251,11 @@
delete RED._debug;
},
oneditprepare: function() {
var none = {
value: "none",
label: RED._("node-red:debug.none"),
hasValue: false
};
if (this.tosidebar === undefined) {
this.tosidebar = true;
$("#node-input-tosidebar").prop('checked', true);
@@ -264,8 +265,21 @@
$("#node-input-console").prop('checked', this.console);
$("#node-input-tosidebar").prop('checked', true);
}
$("#node-input-typed-complete").typedInput({types:['msg', {value:"full",label:RED._("node-red:debug.msgobj"),hasValue:false}]});
if (this.complete === "true" || this.complete === true) {
var fullType = {
value: "full",
label: RED._("node-red:debug.msgobj"),
hasValue: false
};
$("#node-input-typed-complete").typedInput({
default: "msg",
types:['msg', fullType, "jsonata"],
typeField: $("#node-input-targetType")
});
if (this.targetType === "jsonata") {
var property = this.complete || "";
$("#node-input-typed-complete").typedInput('type','jsonata');
$("#node-input-typed-complete").typedInput('value',property);
} else if ((this.targetType === "full") || this.complete === "true" || this.complete === true) {
// show complete message object
$("#node-input-typed-complete").typedInput('type','full');
} else {
@@ -279,16 +293,18 @@
) {
$("#node-input-typed-complete").typedInput('value','payload');
}
if ($("#node-input-typed-complete").typedInput('type') === 'msg') {
if ($("#node-input-typed-complete").typedInput('type') === 'full') {
$("#node-tostatus-line").hide();
} else {
$("#node-tostatus-line").show();
}
else { $("#node-tostatus-line").hide(); }
});
$("#node-input-complete").on('change',function() {
if ($("#node-input-typed-complete").typedInput('type') === 'msg') {
if ($("#node-input-typed-complete").typedInput('type') === 'full') {
$("#node-tostatus-line").hide();
} else {
$("#node-tostatus-line").show();
}
else { $("#node-tostatus-line").hide(); }
});
},
oneditsave: function() {

View File

@@ -1,4 +1,3 @@
module.exports = function(RED) {
"use strict";
var util = require("util");
@@ -9,12 +8,14 @@ module.exports = function(RED) {
util.inspect.styles.boolean = "red";
function DebugNode(n) {
var hasEditExpression = (n.targetType === "jsonata");
var editExpression = hasEditExpression ? n.complete : null;
RED.nodes.createNode(this,n);
this.name = n.name;
this.complete = (n.complete||"payload").toString();
this.complete = hasEditExpression ? null : (n.complete||"payload").toString();
if (this.complete === "false") { this.complete = "payload"; }
this.console = ""+(n.console || false);
this.tostatus = n.tostatus || false;
this.tostatus = (this.complete !== "true") && (n.tostatus || false);
this.tosidebar = n.tosidebar;
if (this.tosidebar === undefined) { this.tosidebar = true; }
this.severity = n.severity || 40;
@@ -43,8 +44,44 @@ module.exports = function(RED) {
"50": "green",
"60": "blue"
};
var preparedEditExpression = null;
if (editExpression) {
try {
preparedEditExpression = RED.util.prepareJSONataExpression(editExpression, this);
}
catch (e) {
node.error(RED._("debug.invalid-exp", {error: editExpression}));
return;
}
}
this.on("input",function(msg) {
function prepareValue(msg, done) {
// Either apply the jsonata expression or...
if (preparedEditExpression) {
RED.util.evaluateJSONataExpression(preparedEditExpression, msg, (err, value) => {
if (err) {
done(RED._("debug.invalid-exp", {error: editExpression}));
} else {
done(null,{id:node.id, name:node.name, topic:msg.topic, msg:value, _path:msg._path});
}
});
} else {
// Extract the required message property
var property = "payload";
var output = msg[property];
if (node.complete !== "false" && typeof node.complete !== "undefined") {
property = node.complete;
try {
output = RED.util.getMessageProperty(msg,node.complete);
} catch(err) {
output = undefined;
}
}
done(null,{id:node.id, z:node.z, name:node.name, topic:msg.topic, property:property, msg:output, _path:msg._path});
}
}
this.on("input", function(msg) {
if (this.complete === "true") {
// debug complete msg object
if (this.console === "true") {
@@ -53,41 +90,36 @@ module.exports = function(RED) {
if (this.active && this.tosidebar) {
sendDebug({id:node.id, name:node.name, topic:msg.topic, msg:msg, _path:msg._path});
}
} else {
prepareValue(msg,function(err,msg) {
if (err) {
node.error(err);
return;
}
var output = msg.msg;
if (node.console === "true") {
if (typeof output === "string") {
node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output);
} else if (typeof output === "object") {
node.log("\n"+util.inspect(output, {colors:useColors, depth:10}));
} else {
node.log(util.inspect(output, {colors:useColors}));
}
}
if (node.tostatus === true) {
var st = (typeof output === 'string')?output:util.inspect(output);
var severity = node.severity;
if (st.length > 32) { st = st.substr(0,32) + "..."; }
node.status({fill:colors[severity], shape:"dot", text:st});
}
if (node.active) {
if (node.tosidebar == true) {
sendDebug(msg);
}
}
});
}
else {
// debug user defined msg property
var property = "payload";
var output = msg[property];
if (this.complete !== "false" && typeof this.complete !== "undefined") {
property = this.complete;
try {
output = RED.util.getMessageProperty(msg,this.complete);
} catch(err) {
output = undefined;
}
}
if (this.console === "true") {
if (typeof output === "string") {
node.log((output.indexOf("\n") !== -1 ? "\n" : "") + output);
} else if (typeof output === "object") {
node.log("\n"+util.inspect(output, {colors:useColors, depth:10}));
} else {
node.log(util.inspect(output, {colors:useColors}));
}
}
if (this.tostatus === true) {
var st = util.inspect(output);
var severity = node.severity;
if (st.length > 32) { st = st.substr(0,32) + "..."; }
node.status({fill:colors[severity], shape:"dot", text:st});
}
if (this.active) {
if (this.tosidebar == true) {
sendDebug({id:node.id, z:node.z, name:node.name, topic:msg.topic, property:property, msg:output, _path:msg._path});
}
}
}
});
})
}
RED.nodes.registerType("debug",DebugNode, {

View File

@@ -13,184 +13,85 @@
</div>
<div class="form-row node-input-link-row"></div>
</script>
<script type="text/x-red" data-help-name="link in">
<p>Create virtual wires between flows.</p>
<h3>Details</h3>
<p>The node can be connected to any <code>link out</code> node that exists on any tab.
Once connected, they behave as if they were wired together.</p>
<p>The wires between link nodes are only displayed when a link node is selected.
If there are any wires to other tabs, a virtual node is shown that can be clicked
on to jump to the appropriate tab.</p>
<p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
</script>
<script type="text/x-red" data-help-name="link out">
<p>Create virtual wires between flows.</p>
<h3>Details</h3>
<p>The node can be connected to any <code>link in</code> node that exists on any tab.
Once connected, they behave as if they were wired together.</p>
<p>The wires between link nodes are only displayed when a link node is selected.
If there are any wires to other tabs, a virtual node is show that can be clicked
on to jump to the appropriate tab.</p>
<p><b>Note: </b>Links cannot be created going into, or out of, a subflow.</p>
</script>
<style>
#node-input-link-container {
position: relative;
}
#node-input-link-container li {
padding: 2px 5px;
background: none;
font-size: 0.8em;
margin:0;
white-space: nowrap;
}
#node-input-link-container li label {
margin-bottom: 0;
width: 100%;
}
#node-input-link-container li label input {
vertical-align: top;
width:15px;
margin-right: 10px;
}
#node-input-link-container li:hover,
#node-input-link-container li:hover .node-input-target-node-sublabel {
background: #f0f0f0;
}
.node-input-link-node-sublabel {
position:absolute;
right: 0px;
padding-right: 10px;
padding-left: 10px;
font-size: 0.8em;
}
</style>
<script type="text/javascript">
(function() {
function sortNodeList(nodeList,sortOn,sortOnSecond) {
var currentSort = nodeList.data('currentSort');
var currentSortOrder = nodeList.data('currentSortOrder');
var treeList;
if (!currentSort) {
currentSort = sortOn;
currentSortOrder = 'a';
} else {
if (currentSort === sortOn) {
currentSortOrder = (currentSortOrder === 'a'?'d':'a');
} else {
currentSortOrder = 'a';
}
currentSort = sortOn;
}
nodeList.data('currentSort',currentSort);
nodeList.data('currentSortOrder',currentSortOrder);
$("#node-input-link-container-div .fa").hide();
$(".node-input-link-sort-"+currentSort+"-"+currentSortOrder).show();
var items = nodeList.find("li").get();
items.sort(function(a,b) {
var labelA = $(a).find(".node-input-link-node-"+currentSort).text().toLowerCase();
var labelB = $(b).find(".node-input-link-node-"+currentSort).text().toLowerCase();
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
if (sortOnSecond) {
labelA = $(a).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
labelB = $(b).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
}
return 0;
});
$.each(items, function(i, li) {
nodeList.append(li);
});
}
function onEditPrepare(node,targetType) {
if (!node.links) {
node.links = [];
}
node.oldLinks = [];
$('<div id="node-input-link-container-div" style="min-height: 100px;position: relative; box-sizing: border-box; border-radius: 2px; height: 180px; border: 1px solid #ccc;overflow:hidden; ">'+
' <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
' <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]node-red:link.label.sortByLabel"><span data-i18n="node-red:link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
' <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]node-red:link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="node-red:link.label.type">flow</span></a></div>'+
' </div>'+
' <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
' <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
' </div>'+
'</div>').appendTo('.node-input-link-row');
var activeSubflow = RED.nodes.subflow(node.z);
var nodeList = $("#node-input-link-container");
treeList = $("<div>")
.css({width: "100%", height: "100%"})
.appendTo(".node-input-link-row")
.treeList({})
.on('treelistitemmouseover',function(e,item) {
if (item.node) {
item.node.highlighted = true;
item.node.dirty = true;
RED.view.redraw();
}
})
.on('treelistitemmouseout',function(e,item) {
if (item.node) {
item.node.highlighted = false;
item.node.dirty = true;
RED.view.redraw();
}
});
var candidateNodes = RED.nodes.filterNodes({type:targetType});
var inSubflow = !!RED.nodes.subflow(node.z);
var flows = [];
var flowMap = {};
if (activeSubflow) {
flowMap[activeSubflow.id] = {
id: activeSubflow.id,
class: 'palette-header',
label: "Subflow : "+(activeSubflow.name || activeSubflow.id)+(node.z===ws.id ? " *":""),
expanded: true,
children: []
};
flows.push(flowMap[activeSubflow.id])
} else {
RED.nodes.eachWorkspace(function(ws) {
flowMap[ws.id] = {
id: ws.id,
class: 'palette-header',
label: (ws.label || ws.id)+(node.z===ws.id ? " *":""),
expanded: true,
children: []
}
flows.push(flowMap[ws.id])
})
}
candidateNodes.forEach(function(n) {
if (inSubflow) {
if (n.z !== node.z) {
return;
}
} else {
if (!!RED.nodes.subflow(n.z)) {
return;
if (flowMap[n.z]) {
var isChecked = false;
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
if (isChecked) {
node.oldLinks.push(n.id);
}
flowMap[n.z].children.push({
id: n.id,
node: n,
label: n.name||n.id,
selected: isChecked
})
}
var isChecked = false;
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
if (isChecked) {
node.oldLinks.push(n.id);
}
var container = $('<li/>',{class:"node-input-link-node"});
var row = $('<label/>',{for:"node-input-link-node-"+n.id}).appendTo(container);
$('<input>',{type:"checkbox",class:"node-input-link-node-checkbox",id:"node-input-link-node-"+n.id})
.data('node-id',n.id)
.prop('checked', isChecked)
.appendTo(row);
container.on('mouseover',function(e) {
n.highlighted = true;
n.dirty = true;
RED.view.redraw();
});
container.on('mouseout',function(e) {
n.highlighted = false;
n.dirty = true;
RED.view.redraw();
});
var labelSpan = $('<span>');
var label = n.name||n.id;
var sublabel;
var tab = RED.nodes.workspace(n.z);
if (tab) {
sublabel = tab.label||tab.id;
} else {
tab = RED.nodes.subflow(n.z);
sublabel = "subflow : "+tab.name;
}
$('<span>',{class:"node-input-link-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
if (sublabel) {
$('<span>',{class:"node-input-link-node-sublabel"}).text(sublabel).appendTo(row);
}
container.appendTo(nodeList);
});
sortNodeList(nodeList,'sublabel','label');
$("#node-input-link-sort-label").click(function(e) {
e.preventDefault();
sortNodeList(nodeList,'label');
});
$("#node-input-link-sort-type").click(function(e) {
e.preventDefault();
sortNodeList(nodeList,'sublabel');
});
flows = flows.filter(function(f) { return f.children.length > 0 })
treeList.treeList('data',flows);
setTimeout(function() {
treeList.treeList('show',node.z);
},100);
}
function resizeNodeList() {
@@ -201,16 +102,19 @@
}
var editorRow = $("#dialog-form>div.node-input-link-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$("#node-input-link-container-div").css("height",height+"px");
$(".node-input-link-row").css("height",height+"px");
}
function onEditSave(node) {
var flows = treeList.treeList('data');
node.links = [];
$(".node-input-link-node-checkbox").each(function(n) {
if ($(this).prop("checked")) {
node.links.push($(this).data('node-id'));
}
});
flows.forEach(function(f) {
f.children.forEach(function(n) {
if (n.selected) {
node.links.push(n.id);
}
})
})
node.oldLinks.sort();
node.links.sort();
var nodeMap = {};

View File

@@ -51,74 +51,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="exec">
<p>Runs a system command and returns its output.</p>
<p>The node can be configured to either wait until the command completes, or to
send its output as the command generates it.</p>
<p>The command that is run can be configured in the node or provided by the received
message.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">payload <span class="property-type">string</span></dt>
<dd>if configured to do so, will be appended to the executed command.</dd>
<dt class="optional">kill <span class="property-type">string</span></dt>
<dd>the type of kill signal to send an existing exec node process.</dd>
<dt class="optional">pid <span class="property-type">number|string</span></dt>
<dd>the process ID of an existing exec node process to kill.</dd>
</dl>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Standard output
<dl class="message-properties">
<dt>payload <span class="property-type">string</span></dt>
<dd>the standard output of the command.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">object</span></dt>
<dd>exec mode only, a copy of the return code object (also available on port 3)</dd>
</dl>
</li>
<li>Standard error
<dl class="message-properties">
<dt>payload <span class="property-type">string</span></dt>
<dd>the standard error of the command.</dd>
</dl>
<dl class="message-properties">
<dt>rc <span class="property-type">object</span></dt>
<dd>exec mode only, a copy of the return code object (also available on port 3)</dd>
</dl>
</li>
<li>Return code
<dl class="message-properties">
<dt>payload <span class="property-type">object</span></dt>
<dd>an object containing the return code, and possibly <code>message</code>, <code>signal</code> properties.</dd>
</dl>
</li>
</ol>
<h3>Details</h3>
<p>By default uses the <code>exec</code> system call which calls the command, waits for it to complete, and then
returns the output. For example a successful command should have a return code of <code>{ code: 0 }</code>.</p>
<p>Optionally can use <code>spawn</code> instead, which returns the output from stdout and stderr
as the command runs, usually one line at a time. On completion it then returns an object
on the 3rd port. For example, a successful command should return <code>{ code: 0 }</code>.</p>
<p>Errors may return extra information on the 3rd port <code>msg.payload</code>, such as a <code>message</code> string,
<code>signal</code> string.</p>
<p>The command that is run is defined within the node, with an option to append <code>msg.payload</code> and a further set of parameters.</p>
<p>Commands or parameters with spaces should be enclosed in quotes - <code>"This is a single parameter"</code></p>
<p>The returned <code>payload</code> is usually a <i>string</i>, unless non-UTF8 characters are detected, in which
case it is a <i>buffer</i>.</p>
<p>The node&apos;s status icon and PID will be visible while the node is active. Changes to this can be read by the <code>Status</code> node.</p>
<h4>Killing processes</h4>
<p>Sending <code>msg.kill</code> will kill a single active process. <code>msg.kill</code> should be a string containing
the type of signal to be sent, for example, <code>SIGINT</code>, <code>SIGQUIT</code> or <code>SIGHUP</code>.
Defaults to <code>SIGTERM</code> if set to an empty string.</p>
<p>If the node has more than one process running then <code>msg.pid</code> must also be set with the value of the PID to be killed.</p>
<p>If a value is provided in the <code>Timeout</code> field then, if the process has not completed when the specified number of seconds has elapsed, the process will be killed automatically</p>
<p>Tip: if running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('exec',{
category: 'advanced-function',

View File

@@ -19,49 +19,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="function">
<p>A JavaScript function block to run against the messages being received by the node.</p>
<p>The messages are passed in as a JavaScript object called <code>msg</code>.</p>
<p>By convention it will have a <code>msg.payload</code> property containing
the body of the message.</p>
<p>The function is expected to return a message object (or multiple message objects), but can choose
to return nothing in order to halt a flow.</p>
<h3>Details</h3>
<p>See the <a target="_blank" href="http://nodered.org/docs/writing-functions.html">online documentation</a>
for more information on writing functions.</p>
<h4>Sending messages</h4>
<p>The function can either return the messages it wants to pass on to the next nodes
in the flow, or can call <code>node.send(messages)</code>.</p>
<p>It can return/send:</p>
<ul>
<li>a single message object - passed to nodes connected to the first output</li>
<li>an array of message objects - passed to nodes connected to the corresponding outputs</li>
</ul>
<p>If any element of the array is itself an array of messages, multiple
messages are sent to the corresponding output.</p>
<p>If null is returned, either by itself or as an element of the array, no
message is passed on.</p>
<h4>Logging and Error Handling</h4>
<p>To log any information, or report an error, the following functions are available:</p>
<ul>
<li><code>node.log("Log message")</code></li>
<li><code>node.warn("Warning")</code></li>
<li><code>node.error("Error")</code></li>
</ul>
</p>
<p>The Catch node can also be used to handle errors. To invoke a Catch node,
pass <code>msg</code> as a second argument to <code>node.error</code>:</p>
<pre>node.error("Error",msg);</pre>
<h4>Accessing Node Information</h4>
<p>In the function block, id and name of the node can be referenced using the following properties:</p>
<ul>
<li><code>node.id</code> - id of the node</li>
<li><code>node.name</code> - name of the node</li>
</ul>
<h4>Using environment variables</h4>
<p>Environment variables can be accessed using <code>env.get("MY_ENV_VAR")</code>.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('function',{
color:"#fdd0a2",

View File

@@ -158,9 +158,8 @@ module.exports = function(RED) {
},
env: {
get: function(envVar) {
// For now, just return the env var. This will eventually
// also return project settings and subflow instance properties
return process.env[envVar]
var flow = node._flow;
return flow.getSetting(envVar);
}
},
setTimeout: function () {

View File

@@ -2,10 +2,10 @@
<script type="text/x-red" data-template-name="template">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>
</div>
<div class="form-row">
<label for="node-input-field"><i class="fa fa-edit"></i> <span data-i18n="template.label.property"></span></label>
<label for="node-input-field"><i class="fa fa-ellipsis-h"></i> <span data-i18n="template.label.property"></span></label>
<input type="text" id="node-input-field" placeholder="payload" style="width:250px;">
<input type="hidden" id="node-input-fieldType">
</div>
@@ -48,42 +48,6 @@
</script>
<script type="text/x-red" data-help-name="template">
<p>Sets a property based on the provided template.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">object</span></dt>
<dd>A msg object containing information to populate the template.</dd>
<dt class="optional">template <span class="property-type">string</span></dt>
<dd>A template to be populated from msg.payload. If not configured in the edit panel,
this can be set as a property of msg.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>msg <span class="property-type">object</span></dt>
<dd>a msg with a property set by populating the configured template with properties from the incoming msg.</dd>
</dl>
<h3>Details</h3>
<p>By default this uses the <i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>
format, but this can be switched off if required.</p>
<p>For example, when a template of:
<pre>Hello {{payload.name}}. Today is {{date}}</pre>
<p>receives a message containing:
<pre>{
date: "Monday"
payload: {
name: "Fred",
}
}</pre>
<p>The resulting property will be:
<pre>Hello Fred. Today is Monday</pre>
<p>It is possible to use a property from the flow context or global context. Just use <code>{{flow.name}}</code> or
<code>{{global.name}}</code>, or for persistable store <code>store</code> use <code>{{flow[store].name}}</code> or
<code>{{global[store].name}}</code>.
<p><b>Note: </b>By default, <i>mustache</i> will escape any HTML entities in the values it substitutes.
To prevent this, use <code>{{{triple}}}</code> braces.
</script>
<script type="text/javascript">
RED.nodes.registerType('template',{
color:"rgb(243, 181, 103)",

View File

@@ -95,36 +95,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="delay">
<p>Delays each message passing through the node or limits the rate at which they can pass.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">delay <span class="property-type">number</span></dt>
<dd>Sets the delay, in milliseconds, to be applied to the message. This
option only applies if the node is configured to allow the message to
override the configured default delay interval.</dd>
<dt class="optional">reset</dt>
<dd>If the received message has this property set to any value, all
outstanding messages held by the node are cleared without being sent.</dd>
<dt class="optional">flush</dt>
<dd>If the received message has this property set to any value, all
outstanding messages held by the node are sent immediately.</dd>
</dl>
<h3>Details</h3>
<p>When configured to delay messages, the delay interval can be a fixed value,
a random value within a range or dynamically set for each message.</p>
<p>When configured to rate limit messages, their delivery is spread across
the configured time period. The status shows the number of messages currently in the queue.
It can optionally discard intermediate messages as they arrive.</p>
</p>
<p>The rate limiting can be applied to all messages, or group them according to
their <code>msg.topic</code> value. When grouping, intermerdiate messages are
automatically dropped. At each time interval, the node can either release
the most recent message for all topics, or release the most recent message
for the next topic.
</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('delay',{
category: 'function',

View File

@@ -69,36 +69,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="trigger">
<p>When triggered, can send a message, and then optionally a second message, unless extended or reset.</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt class="optional">reset</dt>
<dd>If a message is received with this property, any timeout or repeat
currently in progress will be cleared and no message triggered.</dd>
</dl>
<h3>Details</h3>
<p>This node can be used to create a timeout within a flow. By default, when
it receives a message, it sends on a message with a <code>payload</code> of <code>1</code>.
It then waits 250ms before sending a second message with a <code>payload</code> of <code>0</code>.
This could be used, for example, to blink an LED attached to a Raspberry Pi GPIO pin.</p>
<p>The payloads of each message sent can be configured to a variety of values, including
the option to not send anything. For example, setting the initial message to <i>nothing</i> and
selecting the option to extend the timer with each received message, the node will
act as a watchdog timer; only sending a message if nothing is received within the
set interval.</p>
<p>If set to a <i>string</i> type, the node supports the mustache template syntax.</p>
<p>If the node receives a message with a <code>reset</code> property, or a <code>payload</code>
that matches that configured in the node, any timeout or repeat currently in
progress will be cleared and no message triggered.</p>
<p>The node can be configured to resend a message at a regular interval until it
is reset by a received message.</p>
<p>Optionally, the node can be configured to treat messages with <code>msg.topic</code> as if they
are separate streams.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('trigger',{
category: 'function',

View File

@@ -1,24 +1,13 @@
<script type="text/x-red" data-template-name="comment">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-comment"></i> <span data-i18n="comment.label.title"></span></label>
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name">
</div>
<div class="form-row" style="margin-bottom: 0px;">
<label for="node-input-info" style="width: 100% !important;"><i class="fa fa-comments"></i> <span data-i18n="comment.label.body"></span></label>
<input type="hidden" id="node-input-info" autofocus="autofocus">
</div>
<div class="form-row node-text-editor-row">
<input type="hidden" id="node-input-info" autofocus="autofocus">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-info-editor"></div>
</div>
<div class="form-tips" data-i18n="[html]comment.tip"></div>
</script>
<script type="text/x-red" data-help-name="comment">
<p>A node you can use to add comments to your flows.</p>
<h3>Details</h3>
<p>The edit panel will accept Markdown syntax. The text will be rendered into
this information side panel.</p>
</script>
<script type="text/javascript">
@@ -39,7 +28,7 @@
return this.name?"node_label_italic":"";
},
info: function() {
return (this.name?"# "+this.name+"\n":"")+(this.info||"");
return this.name?"# "+this.name+"\n\n---\n\n":"";
},
oneditprepare: function() {
var that = this;

View File

@@ -3,19 +3,6 @@
<div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
</script>
<script type="text/x-red" data-help-name="unknown">
<p>This node is a type unknown to your installation of Node-RED.</p>
<h3>Details</h3>
<p><i>If you deploy with the node in this state, its configuration will be preserved, but
the flow will not start until the missing type is installed.</i></p>
<p>Use the <code>Menu - Manage Palette</code> option
to search for and install nodes, or <b>npm install &lt;module&gt;</b> to
install, any missing modules and restart Node-RED and reimport the nodes.</p>
<p>It is possible this node type is already installed, but is missing a dependency. Check the Node-RED start-up
log for any error messages associated with the missing node type.</p>
<p>Otherwise, you should contact the author of the flow to obtain a copy of the missing node type.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('unknown',{
category: 'unknown',

View File

@@ -171,21 +171,6 @@
<div class="form-tips"><span data-i18n="[html]rpi-gpio.tip.in"></span></div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio in">
<p>Raspberry Pi input node. Generates a <code>msg.payload</code> with either a
0 or 1 depending on the state of the input pin.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>the payload will be a 1 or a 0.</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>the topic will be set to <code>pi/{the pin number}</code>.</dd>
</dl>
<h3>Details</h3>
<p>You may also enable the input pullup resistor or the pulldown resistor.</p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
@@ -380,24 +365,6 @@
<div class="form-tips" id="pwm-tip"><span data-i18n="[html]rpi-gpio.tip.pwm"></span></div>
</script>
<script type="text/x-red" data-help-name="rpi-gpio out">
<p>Raspberry Pi output node. Can be used in Digital or PWM modes.
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number | string | boolean</span></dt>
</dl>
<h3>Details</h3>
<p>Digital mode - expects a <code>msg.payload</code> with either a 0 or 1 (or true or false),
and will set the selected physical pin high or low depending on the value passed in.</p>
<p>The initial value of the pin at deploy time can also be set to 0 or 1.</p>
<p>PWM mode - expects an input value of a number 0 - 100. It can be floating point.</p>
<p>PWM mode can be used to drive a servo using input values between 10 and 20 only,
but will accept floating point values.
The GPIO2 pin is best for this as it uses hardware to do the PWM. For better servo support
consider the alternative node-red-node-pi-gpiod node.</p>
<p>Requires the RPi.GPIO python library version 0.5.10 (or better) in order to work.</p>
</script>
<script type="text/javascript">
var bcm2pin = {
"2":"3", "3":"5", "4":"7", "14":"8", "15":"10", "17":"11", "18":"12", "27":"13", "22":"15",
@@ -526,20 +493,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="rpi-mouse">
<p>Raspberry Pi mouse button node. Requires a USB mouse.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>1 or 0 when the selected mouse button is pressed and released.</dd>
<dt>button <span class="property-type">number</span></dt>
<dd>1, 2, 4 corresponding to left, right and middle buttons, so you
can work out which button or combination was pressed.</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>set to <code>pi/mouse</code></dd>
</dl>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-mouse',{
category: 'Raspberry Pi',
@@ -571,19 +524,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="rpi-keyboard">
<p>Raspberry Pi keyboard handling node. Requires a USB keyboard.</p>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload <span class="property-type">number</span></dt>
<dd>contains the keycode value</dd>
<dt>action <span class="property-type">string</span></dt>
<dd>set to "up", "down", or "repeat"</dd>
<dt>topic <span class="property-type">string</span></dt>
<dd>set to <code>pi/key</code></dd>
</dl>
</script>
<script type="text/javascript">
RED.nodes.registerType('rpi-keyboard',{
category: 'Raspberry Pi',

View File

@@ -74,10 +74,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="tls-config">
<p>Configuration options for TLS connections.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('tls-config',{
category: 'config',

View File

@@ -45,13 +45,6 @@
</div>
</script>
<script type="text/x-red" data-help-name="http proxy">
<p>Configuration options for HTTP proxy.</p>
<h3>Details</h3>
<p>When accessing to the host in the ignored host list, no proxy will be used.</p>
</script>
<script type="text/javascript">
RED.nodes.registerType('http proxy', {
category: 'config',

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