Compare commits

..

723 Commits

Author SHA1 Message Date
Dave Conway-Jones
d22979659b This add jsonata output to trigger node output options 2020-12-13 12:31:14 +00:00
Nick O'Leary
bbe3ee701f Merge pull request #2768 from natcl/librarySymlinks
Library: properly handle symlinked folders
2020-11-25 21:07:50 +00:00
Nathanaël Lécaudé
b1c0d6b452 Library: properly handle symlinked folders 2020-11-24 18:29:39 -05:00
Nick O'Leary
c6129b44a1 Merge branch 'master' into dev 2020-11-18 16:36:56 +00:00
Nick O'Leary
8ebcee32c2 Bump for 1.2.5 2020-11-17 23:09:07 +00:00
Nick O'Leary
2b801a756a Fix import of config nodes with unknown z property 2020-11-17 23:07:43 +00:00
Nick O'Leary
98b639540b Set ACTIONS_ALLOW_UNSECURE_COMMANDS in GH Action 2020-11-17 22:09:17 +00:00
Nick O'Leary
e0b797fc7e Update changelog 2020-11-17 21:06:21 +00:00
Nick O'Leary
795416a84d Bump for 1.2.4 2020-11-17 21:03:24 +00:00
Nick O'Leary
545dda166f Support bigint types in Debug sidebar 2020-11-17 20:50:29 +00:00
Nick O'Leary
f19ec5d9b6 Clear retained status of deleted nodes 2020-11-17 13:29:13 +00:00
Nick O'Leary
6ea978d83d Prevent needless retention of node status messages 2020-11-16 21:05:13 +00:00
Alex Kaul
42f3b70a22 Update Russian Locale (#2761) 2020-11-16 18:44:18 +00:00
Nick O'Leary
1cd10f074b Update projects dialogs to use TypedInput-cred input 2020-11-16 11:37:32 +00:00
Nick O'Leary
bed1d31bc8 Restore cursor position in TypedInput cred-mode 2020-11-16 11:37:04 +00:00
Nick O'Leary
99478897c5 Ensure config nodes with invalid z are imported somewhere 2020-11-14 14:10:32 +00:00
Nick O'Leary
d79cd463a0 Disable projects when flowFile passed into grunt dev
Useful for quickly testing a standalone flow file
2020-11-14 14:09:24 +00:00
Nick O'Leary
2a8290a4b7 Bump version to 1.3.0-beta.1 2020-11-13 18:23:35 +00:00
Alex Kaul
4023ab3f28 Add Russian Locale (#2531) 2020-11-12 17:01:44 +00:00
Kazuhito Yokoi
70f3b7450f Add Japanese translation for http-in node (#2758) 2020-11-12 10:21:45 +00:00
Dave Conway-Jones
ca4960e097 Fix CSV node repeating array output
and add tests to cover it
2020-11-10 14:43:59 +00:00
Nick O'Leary
ebe604e1af Ensure user keyboard shortcuts override defaults
Fixes #2753
2020-11-09 21:13:20 +00:00
Nick O'Leary
f878ffc01b Update changelog 2020-11-05 13:53:22 +00:00
Nick O'Leary
15b49f4db8 Disable 'use strict' checking in Function node
Fixes #2743
2020-11-05 13:48:55 +00:00
Dave Conway-Jones
b1cc7b3296 de-duplicate colour keys 2020-11-05 09:38:34 +00:00
Dave Conway-Jones
65d90a6dff Add gray/grey alternate options for status 2020-11-05 09:20:47 +00:00
Dave Conway-Jones
a58f4c2ec2 remove " from npm install prefix option
to fix npm 7
2020-11-05 09:19:47 +00:00
Nick O'Leary
75d7ac2d8a Merge pull request #2747 from node-red-hitachi/update-message-jp
update Japanese message catalogue for 1.2.3 release
2020-11-03 10:42:45 +00:00
Hiroyasu Nishiyama
6720c1aa46 update Japanese message catalogue for 1.2.3 release 2020-11-03 09:16:13 +09:00
Nick O'Leary
280203e64e Move mosca to ui-test-dependencies list 2020-11-02 21:32:20 +00:00
Nick O'Leary
281d8b7cec Bump for 1.2.3 2020-11-02 21:31:27 +00:00
Nick O'Leary
2c6cda1f27 Handle import errors on initial load and report to user 2020-11-02 21:14:24 +00:00
Nick O'Leary
fa532da8c7 Merge pull request #2739 from node-red/settings-file
Modify default settings comment
2020-10-29 15:42:00 +00:00
Nick O'Leary
cbf84647de Modify default settings comment 2020-10-29 11:51:30 +00:00
Nick O'Leary
c38a490a6f Merge pull request #2737 from node-red/fix-async-settings
Add mutex lock to saveSettings storage call
2020-10-28 22:14:25 +00:00
Nick O'Leary
9d7a450821 Add mutex lock to saveSettings storage call
Fixes #2736
2020-10-28 21:59:22 +00:00
Nick O'Leary
0ecd9673b8 Only apply recovery tab on initial load
Fixes #2731
2020-10-21 10:36:47 +01:00
Nick O'Leary
97aa1230ef Reinstate coveralls reporting to travis build 2020-10-19 21:22:15 +01:00
Nick O'Leary
ff0be73b1f Migrate to nyc instead of istanbul for code coverage 2020-10-19 21:10:34 +01:00
Nick O'Leary
8049e44dec Update CHANGELOG for 1.2.2 2020-10-19 13:25:38 +01:00
Nick O'Leary
dc26022fb4 Prevent node z property getting set to 0 or "" 2020-10-19 13:24:04 +01:00
Nick O'Leary
e8e44f9a32 Only apply z-recovery logic to flow nodes 2020-10-19 13:23:43 +01:00
Nick O'Leary
12d56b8b03 Fix api call to reload flows
Fixes #2726
2020-10-19 12:56:40 +01:00
Nick O'Leary
e62fd7ed15 Remove bad z property from import config nodes 2020-10-19 12:53:03 +01:00
Nick O'Leary
978eb95acd Bump for 1.2.1 2020-10-15 16:22:37 +01:00
Nick O'Leary
e34f4acb22 Fix race condition in .config file migration
Fixes #2724
2020-10-15 16:21:28 +01:00
Nick O'Leary
15a600c763 Fix tab selection after sidebar tab reorder 2020-10-14 22:10:03 +01:00
Nick O'Leary
82ad5839fa Update changelog and bump dependencies 2020-10-13 21:49:11 +01:00
Nick O'Leary
9af883231d Merge pull request #2722 from node-red-hitachi/fix-link-selection
fix selection of link node not existing within active workspace
2020-10-13 13:56:53 +01:00
Hiroyasu Nishiyama
9bfe8ac007 fix selection of link node not existing within active workspace 2020-10-12 20:16:21 +09:00
Nick O'Leary
f46367d77b Fix import of merged flow 2020-10-12 11:20:44 +01:00
Nick O'Leary
eb2e1c0c45 Merge pull request #2718 from node-red-hitachi/fix-upload-button-width-on-safari
Fix upload button width on Safari
2020-10-09 17:12:35 +01:00
Jiye Yu
baffc2d6ca update Chinese translation for NodeRed v1.2 (#2719) 2020-10-08 13:17:04 +01:00
Hiroyasu Nishiyama
96ab508c91 move width specification of upload button to scss 2020-10-08 09:07:11 +09:00
Nick O'Leary
57e42659e3 Merge pull request #2716 from node-red-hitachi/fix-sidebar-tab-popup
Fix unexpected line break of sidebar tab name popover
2020-10-07 12:33:02 +01:00
Nick O'Leary
f059e97697 Merge pull request #2717 from node-red-hitachi/i18n-module-list-refresh-tooltip
i18n module refresh tooltip
2020-10-07 11:37:01 +01:00
Hiroyasu Nishiyama
516e6430eb fix upload button width on safari 2020-10-07 13:42:43 +09:00
Hiroyasu Nishiyama
f194a8ecf4 i18n module refresh tooltip 2020-10-07 11:08:23 +09:00
Hiroyasu Nishiyama
13f046f310 fix unexpected line break of sidebar tab name popover 2020-10-07 09:57:34 +09:00
Nick O'Leary
1edf5acb87 Merge pull request #2714 from node-red-hitachi/update-function-node-help-text
Update info text of Function node
2020-10-06 17:46:11 +01:00
Nick O'Leary
af636870d4 Add better error message if context file gets corrupted 2020-10-06 15:42:52 +01:00
Hiroyasu Nishiyama
379b8ada61 update info text of function node 2020-10-06 13:45:00 +09:00
Nick O'Leary
5e63471983 Use markdown editor if editText called with md mode 2020-10-05 20:38:05 +01:00
Nick O'Leary
086f0f8450 Prevent group actions when in non-default mouse mode 2020-10-02 16:07:22 +01:00
Nick O'Leary
97a4b3dc2a Merge branch 'pr_2709' into dev 2020-10-02 11:14:16 +01:00
Kazuhito Yokoi
4eb8d681c1 Update Japanese translations needed for 1.2 (#2710) 2020-10-02 11:07:01 +01:00
Hiroyasu Nishiyama
2066584164 fix to make Japanese import dialogue message single line 2020-10-01 19:25:21 +09:00
Nick O'Leary
a954c198fb Bump version for 1.2.0-beta.1 2020-09-30 10:20:41 +01:00
Nick O'Leary
bb1f8cd5e8 Update CHANGELOG and bump package.json 2020-09-29 20:43:03 +01:00
Nick O'Leary
101e96dcb3 Merge pull request #2665 from node-red/msg-router
Pluggable Message Routing
2020-09-29 20:36:23 +01:00
Nick O'Leary
59adf82895 Merge pull request #2707 from node-red/trigger-delay-override
Allow trigger node delay to be overridden with msg.delay
2020-09-29 20:29:10 +01:00
Nick O'Leary
3b68f56b15 Update top-level copyright statements 2020-09-29 20:28:15 +01:00
Nick O'Leary
2962c4372c Support setting trigger loop interval with msg.delay 2020-09-29 17:47:09 +01:00
Nick O'Leary
517e376582 Restore support for runtimeSyncDelivery flag 2020-09-29 17:39:29 +01:00
Nick O'Leary
7a90fe5aec Fix flow api unit tests 2020-09-29 17:35:43 +01:00
Nick O'Leary
ea45dde63a Remove when.js from runtime/lib/flow/index 2020-09-29 17:20:01 +01:00
Nick O'Leary
22a301b55e Add flows:* events and deprecate nodes-* events 2020-09-29 16:29:10 +01:00
Nick O'Leary
605177dcf0 Validate hook names when they are added 2020-09-29 16:28:52 +01:00
Nick O'Leary
460e1f5563 Fixup merge error 2020-09-29 12:19:27 +01:00
Nick O'Leary
6f25337b99 Add docs for RED.hooks 2020-09-29 12:19:27 +01:00
Nick O'Leary
08148a07b2 Update Node/Flow to trigger msg routing hooks 2020-09-29 12:19:27 +01:00
Nick O'Leary
27c0e45940 Remove unused router component 2020-09-29 12:19:27 +01:00
Nick O'Leary
bdd736315a Add RED.hooks engine 2020-09-29 12:19:27 +01:00
Nick O'Leary
d57ec0cd53 Refactor lib/flows code to include initial router component 2020-09-29 12:19:26 +01:00
Nick O'Leary
952c9d8bdb Upgrade to latest nodemon to fix restart
The grunt-nodemon module we were using is no longer
maintained and is stuck on a 1.x version of nodemon.

At that version, node-red doesn't restart properly
due to our increased signal handling in the core.

This change removes grunt-nodemon and replaces it
with nodemon itself, with a custom task to wrap it
that does the same work as grunt-nodemon was doing.
2020-09-29 12:11:10 +01:00
Nick O'Leary
cf84ec78fa Allow trigger node delay to be overridden with msg.delay 2020-09-28 21:10:23 +01:00
Nick O'Leary
b595e84c30 Update jsdoc of util.getMessageProperty 2020-09-28 14:26:20 +01:00
Nick O'Leary
6e5f115bd5 Improve jsdoc of util.getObjectProperty to clarify thrown error
See #2703
2020-09-28 14:23:23 +01:00
Nick O'Leary
a1ab00c93e sort package dependencies 2020-09-28 14:13:53 +01:00
Nick O'Leary
6e5c4e832e Update dependencies 2020-09-28 11:58:22 +01:00
Nick O'Leary
48ea487974 Update changelog 2020-09-28 11:40:38 +01:00
Nick O'Leary
54dc98a90b Merge pull request #2035 from node-red/simple-git
Add option for simplified git workglow
2020-09-28 11:39:17 +01:00
Nick O'Leary
c5bdd3d056 Allow user to manage project version string 2020-09-28 11:30:46 +01:00
Nick O'Leary
64d6e1f8e1 Changing timing in trigger node test 2020-09-28 10:50:19 +01:00
Nick O'Leary
69d60ffb24 Add simplified git workflow to auto-commit changes 2020-09-28 10:41:33 +01:00
Nick O'Leary
e6ffa3d143 Cache settings when doing initial load 2020-09-28 10:41:05 +01:00
Nick O'Leary
bb4330e486 Clone settings before passing to storage layer
Avoids the storage layer modifying the in-memory object
2020-09-28 10:40:03 +01:00
Nick O'Leary
1a4d720978 Improve timings in trigger node test 2020-09-25 23:32:59 +01:00
Nick O'Leary
91c2f479bb Fix settings file migration test 2020-09-25 18:29:47 +01:00
Nick O'Leary
b61701fa17 Remove UI tests from travis config 2020-09-25 18:00:32 +01:00
Nick O'Leary
cb9f910642 Update changelog 2020-09-25 17:19:02 +01:00
Nick O'Leary
4b8d07f301 Merge branch 'pr_2644' into dev 2020-09-25 17:11:39 +01:00
Nick O'Leary
2db3a4f1ef Add unit tests for function node init code 2020-09-25 17:11:10 +01:00
Nick O'Leary
ead1f65887 Disable selection FA icons when dbl clicking node 2020-09-25 16:03:21 +01:00
Nick O'Leary
aae9866866 Update changelog 2020-09-25 15:58:33 +01:00
Nick O'Leary
085ff84bc9 Merge pull request #2704 from node-red/split-config
Split .config.json into separate files
2020-09-25 15:56:32 +01:00
Nick O'Leary
e12975cf0b Do not remove old config.json file to ease downgrade path 2020-09-25 15:50:26 +01:00
Nick O'Leary
a33cf6b532 Merge branch 'master' into dev 2020-09-25 11:53:37 +01:00
Nick O'Leary
5be25d9538 Merge branch 'pr_2705' 2020-09-25 11:53:15 +01:00
Nick O'Leary
2b29eeb795 Add unit test for module path 2020-09-25 11:52:48 +01:00
t.kawamorita
785561a0cc fix getModuleFiles function 2020-09-25 11:58:03 +09:00
Nick O'Leary
b46dc88346 Update changelog 2020-09-24 18:12:52 +01:00
Nick O'Leary
96d81ef72b Add slight delay to fix config.json file tests 2020-09-24 16:54:24 +01:00
Nick O'Leary
81dc3de26a Ensure errors in ACE NRJavaScript mode are on valid lines
In the case of an "Unmatched {" error, it flags the { on
the line we wrap the user's code in. That doesn't help the
user.

This fix moves such an error to the first valid { in the file.

It handles ignoring { in comments or strings. It fails to ignore
{ inside regex. But that's an edge case on top of an edge case.
2020-09-24 16:29:05 +01:00
Nick O'Leary
4d0c572c2e Fix .config.json unit tests 2020-09-24 15:42:52 +01:00
Nick O'Leary
fb2da0ee9e Split .config.json into separate files 2020-09-23 17:29:09 +01:00
Nick O'Leary
b8b0247717 Default flowFilePretty to true if projects enabled 2020-09-23 10:57:58 +01:00
Nick O'Leary
103e212aee Fix size of context sidebar refresh toggle buttons 2020-09-22 23:44:10 +01:00
Nick O'Leary
2f33575907 Merge branch 'dev' of github.com:node-red/node-red into dev 2020-09-21 20:12:42 +01:00
Nick O'Leary
576c528573 Merge pull request #2698 from node-red/import-dupes
Improved handling of importing duplicate subflow/config nodes
2020-09-21 18:30:15 +01:00
Nick O'Leary
3c444d3fb3 Merge branch 'master' into dev 2020-09-21 14:25:30 +01:00
Nick O'Leary
7cb499cde9 Sanitize unknown node type when displaying 2020-09-21 14:00:01 +01:00
Nick O'Leary
5a174ba014 Add comment highlighting to JSONata and fix regex handling
Closes #2701
2020-09-21 11:52:33 +01:00
Nick O'Leary
041feb4e86 Ensure sf instance nodes update in outliner when import-replace sf 2020-09-18 20:56:16 +01:00
Nick O'Leary
19726cf428 Fix setting of esversion in nrjavascript worker 2020-09-16 23:21:52 +01:00
Nick O'Leary
aaf134b1c5 Update ACE to 1.4.12-src-min-noconflict
Fixes #1988
2020-09-16 19:13:40 +01:00
Nick O'Leary
9d5f5ee94b Update to MQTT 4.2.1
Closes #2694
2020-09-16 16:56:39 +01:00
Nick O'Leary
a48f0827ae Detect importing duplicate nodes and help user resolve 2020-09-16 11:59:13 +01:00
Nick O'Leary
5686158245 Allow toggleButton icons to be optional 2020-09-16 11:59:13 +01:00
Nick O'Leary
3824cdde68 Allow treeList to have a header component 2020-09-16 11:59:13 +01:00
Nick O'Leary
e619b9bf7b Merge pull request #2691 from node-red/recover-nodes
Recover nodes with invalid z property
2020-09-16 11:56:29 +01:00
Nick O'Leary
b7243c2226 Merge branch 'master' into dev 2020-09-16 11:54:34 +01:00
Nick O'Leary
70b6674f44 Replace Math.random with crypto.getBytes for session tokens 2020-09-11 14:09:54 +01:00
Nick O'Leary
ef67b8481e Check file exists before trying to take backup 2020-09-11 13:26:00 +01:00
Nick O'Leary
baffe4861c Handle undefined value in Debug view of Array and Object
Fixes #2696
2020-09-07 21:05:27 +01:00
Nick O'Leary
5cf489a270 Fixup node test 2020-09-04 09:52:30 +01:00
Nick O'Leary
44b1819926 Merge pull request #2693 from mgroenhoff/master
Fix `this` context when calling multiple event listeners (fixes #2692).
2020-09-04 09:33:23 +01:00
Melvin Groenhoff
d84c2b780b Fix this context when calling multiple event listeners (fixes #2692). 2020-09-04 10:20:30 +02:00
Nick O'Leary
dc8991a1da Ensure recoverd nodes tab is added to outliner properly 2020-09-03 20:24:50 +01:00
Nick O'Leary
7bd0ca2212 Handle nodes with invalid z property
Closes #2170
2020-09-03 18:31:33 +01:00
Nick O'Leary
4dd619b8c6 Merge branch 'master' into dev 2020-09-03 16:14:55 +01:00
Nick O'Leary
3a86ab186c Merge branch 'dev' of github.com:node-red/node-red into dev 2020-09-03 15:23:41 +01:00
Nick O'Leary
2f2a6367c2 Merge pull request #2684 from node-red/skip-html
Avoid loading node html if disableEditor set
2020-09-03 15:02:09 +01:00
Nick O'Leary
be880c25f9 Merge pull request #2682 from node-red/upload-npm
Add support for file upload on /nodes api
2020-09-03 15:01:31 +01:00
Nick O'Leary
17812f0d77 Merge pull request #2683 from node-red/write-to-temp
Update util.writeFile to write to tmp file before rename
2020-09-03 15:00:31 +01:00
Nick O'Leary
0c5eae2349 Merge pull request #2679 from rorysavage77/mutex-for-flow-modification
Updated flow modification methods to support mutex serialization
2020-09-03 14:17:56 +01:00
Nick O'Leary
3ad1803057 Merge pull request #2655 from node-red/reorder-sidebar
Reorderable sidebar tabs
2020-09-03 14:07:17 +01:00
Nick O'Leary
02c20e97b7 Only recalculate group label offsets when needed 2020-09-03 13:49:42 +01:00
Nick O'Leary
716dc781e4 Reuse first group name/style when merging elements
Fixes #2680
2020-09-03 13:28:35 +01:00
Nick O'Leary
d9900d8e4c Fix copy/paste of node into active group
Fixes #2686
2020-09-03 13:12:08 +01:00
Nick O'Leary
3b9065b057 Prevent Enter on search box from reloading page
Fixes #2678
2020-09-03 11:22:41 +01:00
Nick O'Leary
e73b748b95 Merge branch 'dev' of github.com:node-red/node-red into dev 2020-09-03 11:17:09 +01:00
Nick O'Leary
b309161f00 Merge pull request #2685 from node-red-hitachi/dev-tracemetric
Add 'done' metric log for message tracing
2020-09-03 11:16:26 +01:00
Nick O'Leary
b21667834e Tweak upload dialog margin 2020-08-26 10:37:21 +01:00
Nick O'Leary
183fa59c83 Add tgz upload button to palette manager 2020-08-26 00:15:36 +01:00
Kunihiko Toumura
0c5586ddfb Add 'done' metric log for message tracing 2020-08-18 09:28:50 +09:00
Nick O'Leary
33855bcb8b Skip loading node html if disableEditor set 2020-08-14 00:20:44 +01:00
Nick O'Leary
dc81b7a699 Add --userDir=/tmp/foo support to grunt dev 2020-08-14 00:08:09 +01:00
Nick O'Leary
b0b2c32654 Update util.writeFile to write to tmp file before rename 2020-08-13 17:17:40 +01:00
Nick O'Leary
6f1ed76b4c Add support for file upload in /nodes api 2020-08-13 15:54:54 +01:00
Rory A. Svage
f81cee0be2 Message 2020-08-07 16:44:52 -04:00
Nick O'Leary
bcd85b11a1 Merge branch 'master' into dev 2020-08-05 15:19:54 +01:00
Nick O'Leary
ec368ae3fd Bump for 1.1.3 2020-08-05 14:59:25 +01:00
Nick O'Leary
d28c264422 Fix jshint error on polyfill 2020-08-05 14:58:43 +01:00
Nick O'Leary
fba505bc90 Fix vertical align of fa node icons
Fixes #2670
2020-08-05 13:53:49 +01:00
Kazuhito Yokoi
c50ed2c328 Add Japanese translation for empty rules in switch node 2020-08-05 11:22:23 +01:00
Nick O'Leary
7e11ff2b20 Update packages/node_modules/@node-red/nodes/locales/en-US/function/10-switch.html 2020-08-05 11:22:14 +01:00
Nathanaël Lécaudé
3fb83c46e2 Update 10-switch.html
Clarify switch help regarding booleans for the is empty / is not empty rules.
2020-08-05 11:22:04 +01:00
Nick O'Leary
763f2bd5c5 Merge pull request #2669 from kazuhitoyokoi/dev-jpn4switch
Update Japanese translation for switch node
2020-08-05 11:20:17 +01:00
Nick O'Leary
85edee288f Allow lasso selection to be restricted to active group 2020-08-05 11:16:53 +01:00
Nick O'Leary
1aa494a97a Make ctrl-click on nexted group more intuitive 2020-08-05 10:38:14 +01:00
Nick O'Leary
a8e7627184 Support select-all inside active group 2020-08-04 21:01:08 +01:00
Nick O'Leary
d590bbdd2c Fix copy/paste of nested groups 2020-08-04 20:59:51 +01:00
Nick O'Leary
80d65b5acb Add Set(iterable) polyfill for IE11 2020-08-04 20:59:32 +01:00
Kazuhito Yokoi
1d250f7491 Add Japanese translation for empty rules in switch node 2020-08-04 21:41:23 +09:00
Nick O'Leary
dd741ec6d8 Merge pull request #2649 from natcl/patch-5
Clarify empty rules in switch node documentation
2020-08-03 16:59:25 +01:00
Nick O'Leary
e741af6d55 Update packages/node_modules/@node-red/nodes/locales/en-US/function/10-switch.html 2020-08-03 16:58:09 +01:00
Nick O'Leary
e691b1b7c3 Add additional check for git auth failure response
Fixes #2656
2020-08-03 16:55:36 +01:00
Sebastian Raff
29142128f2 german translation, wording (#2660) (#2666)
to also close #2660
2020-08-02 18:34:10 +01:00
Nick O'Leary
758f44e25f Improve performance of moving groups 2020-07-31 23:22:33 +01:00
Nick O'Leary
16c26d8098 Move runtime settings back to adminApi from editorApi
Fixes #2662
2020-07-31 15:26:21 +01:00
Nick O'Leary
a004c61afc Merge pull request #2653 from node-red-hitachi/dev-messaging-api-cherrypick
Messaging API support of core nodes (cherrypicked)
2020-07-23 15:19:52 +01:00
Nick O'Leary
a9d1a64c32 Merge pull request #2661 from node-red-hitachi/master-zh
Update Chinese message for debug node
2020-07-23 15:09:36 +01:00
Dave Conway-Jones
889224715b Fix hhp-in to handle application/cbor as binary
as per discussion https://discourse.nodered.org/t/http-request-node-invalid-message-body-was-specified-to-be-cbor-but-could-not-decode-message-failed-to-parse/30503
2020-07-23 10:04:24 +01:00
Nick O'Leary
442b9d23f1 Remove filtering of duplicate fa icons 2020-07-22 14:51:07 +01:00
JIYE YU
e4dd895709 update Chinese message for debug node 2020-07-22 12:54:07 +09:00
Nick O'Leary
82677c304e Show node help when switching node edit dialogs
Fixes #2652
2020-07-16 16:12:16 +01:00
Nick O'Leary
73d8dfe381 Allow sidebar tabs to be reordered
The sidebar tab buttons can now be dragged to reorder them.

Changes to the order are stored in user preferences.
2020-07-15 11:26:08 +01:00
Kunihiko Toumura
1177aa8aca new-style callback function (yaml node) 2020-07-14 19:20:37 +09:00
Kunihiko Toumura
0eda0a4935 new-style callback function (xml node) 2020-07-14 19:18:05 +09:00
Kunihiko Toumura
a19dab0dc9 new-style callback function (json node) 2020-07-14 19:15:31 +09:00
Kunihiko Toumura
d8eb80b72e new-style callback function (html node) 2020-07-14 19:10:56 +09:00
Kunihiko Toumura
4f3a6821d1 new-style callback function (http response node) 2020-07-14 19:08:22 +09:00
Kunihiko Toumura
77bd7541ca new-style callback function (template node) 2020-07-14 19:05:41 +09:00
Kunihiko Toumura
ca46bc5366 new-style callback function (range node) 2020-07-14 19:02:41 +09:00
Kunihiko Toumura
2e19bc07df new-style callback function (link in/out node) 2020-07-14 18:59:09 +09:00
Kunihiko Toumura
3f4de43b67 new-style callback function (status node) 2020-07-14 18:56:16 +09:00
Kunihiko Toumura
0d0bf62fc4 new-style callback function (catch node) 2020-07-14 18:53:21 +09:00
Kunihiko Toumura
3c8654fa25 new-style callback function (complete node) 2020-07-14 18:50:03 +09:00
Kunihiko Toumura
756a6ec5aa new-style callback function (inject node) 2020-07-14 18:46:38 +09:00
Nick O'Leary
98c7364924 Ensure group theme picks up theme defaults properly
Fixes #2651
2020-07-13 20:44:53 +01:00
Nick O'Leary
62c01b59b2 Extend release action to update website 2020-07-10 21:46:00 +01:00
Nick O'Leary
43db1824be Bump for 1.1.2 2020-07-10 20:13:05 +01:00
Nick O'Leary
7f671c9f3f Ensure unknown nodes removed from outliner when node registers
Fixes #2646
2020-07-10 20:00:18 +01:00
Nathanaël Lécaudé
410009dd61 Update 10-switch.html
Clarify switch help regarding booleans for the is empty / is not empty rules.
2020-07-10 13:08:40 -04:00
Nick O'Leary
580cc00967 Fix all the touch screen issues 2020-07-10 16:00:38 +01:00
Nick O'Leary
612c565cfd Add RED.view.redrawStatus to avoid full redraw on update 2020-07-10 16:00:18 +01:00
Nick O'Leary
979c5351a8 Ensure node/group xrefs are consistent on import 2020-07-10 15:59:28 +01:00
Nick O'Leary
97b7479081 Disable keyboard handler when dialogs are open 2020-07-09 20:41:55 +01:00
Nick O'Leary
1df2f5e96a Allow Comms websocket auth to be done via token header
Fixes #2642
2020-07-09 19:07:51 +01:00
cinhcet
0601833387 msgid added in vm to avoid unneccesary if statement 2020-07-08 19:09:07 +02:00
Nick O'Leary
8b36279e52 Bump version 1.2.0-alpha.1 2020-07-08 15:57:32 +01:00
Nick O'Leary
32163d5f21 Update changelog 2020-07-08 13:26:48 +01:00
Nick O'Leary
1c337f6817 Fix connecting wires to subflow status or io ports on touchscreen 2020-07-08 13:22:26 +01:00
Nick O'Leary
6df26f2400 Bump for 1.1.1 2020-07-08 10:30:20 +01:00
Nick O'Leary
a9431a5aee Merge pull request #2645 from kazuhitoyokoi/master-addjpn
Update Japanese message for debug node
2020-07-08 10:08:49 +01:00
Kazuhito Yokoi
5a3c832a98 Update Japanese message for debug node 2020-07-08 16:20:02 +09:00
Nick O'Leary
c4b5bb22db Tidy up commit msg on docker update action 2020-07-08 01:13:14 +01:00
Nick O'Leary
2b5a976f35 Add github action to auto-update docker repo version on release publish 2020-07-08 01:02:18 +01:00
cinhcet
5d7a625883 expose send, status, log, warn, error, debug, trace, name, id in setup code of function node 2020-07-08 01:30:29 +02:00
Nick O'Leary
ae1ca85924 Update Debug status label 2020-07-07 22:10:47 +01:00
Nick O'Leary
c9acfdb1d7 Set apiRootUrl for debug pop-out to load locales properly
Fixes #2629, #2630
2020-07-07 21:14:44 +01:00
Nick O'Leary
11ac8fbf13 Ensure groups are removed when deleting subflows 2020-07-07 18:23:42 +01:00
Nick O'Leary
dc541444ba Merge pull request #2638 from node-red/Clean-up-debug-status-/-remove-loops
fix debug status to not loop, make migration more seamless, detect status type objects
2020-07-07 13:37:05 +01:00
Nick O'Leary
8ea25bcd1c Update build-custom-theme to handle keyframes properly
Fixes #2636

Also adds a header to the generated CSS identifing the version
of NR and date/time it was generated.
2020-07-07 11:38:07 +01:00
Nick O'Leary
f5e46a663a Remove hardcoded css and allow group to default from theme
Fixes #2633
2020-07-07 11:01:05 +01:00
Nick O'Leary
64ec415a54 Add RED.view.DEBUG_SYNC_REDRAW to disable requestAnimationFrame
References #2631
2020-07-06 21:01:14 +01:00
Nick O'Leary
57154b2853 Authenticate websocket comms using user-provided token if present
Fixes #2642
2020-07-06 20:45:07 +01:00
Nick O'Leary
0243a902b2 Fix up subflow port wiring 2020-07-06 18:11:47 +01:00
Nick O'Leary
6c04402a98 Prevent wiring to node with no corresponding port
Fixes #2641
2020-07-06 17:32:44 +01:00
Nick O'Leary
ef7c9b5c2a Get group order right in history events to ensure proper handling 2020-07-06 16:00:15 +01:00
Nick O'Leary
73448a6039 Avoid copying duplicate nodes to internal clipboard 2020-07-06 16:00:15 +01:00
Dave Conway-Jones
176a0ff99b add words avout indepedence of messages being delayed. 2020-07-06 10:33:26 +01:00
Dave Conway-Jones
b96d562700 fix debug status to not loop, make migration more seamless, detect status type objects 2020-07-04 15:26:02 +01:00
Nick O'Leary
7a3ead8f3b Merge branch 'dev' 2020-06-30 17:46:43 +01:00
Nick O'Leary
f72903ccc2 Update changelog 2020-06-29 21:12:12 +01:00
Nick O'Leary
668678b2c4 Bump version to 1.1.0 2020-06-29 21:08:34 +01:00
Nick O'Leary
e2802175a5 Clear outline focus on config node sidebar panel 2020-06-29 21:07:21 +01:00
Nick O'Leary
71c5b1be86 Merge pull request #2628 from Steve-Mcl/dev
Add developer options
2020-06-29 20:51:11 +01:00
Nick O'Leary
44da085e0b Tweak group margin to fit node status and look better 2020-06-29 20:43:25 +01:00
Nick O'Leary
362554ad3b Switch JSONata expr does not require msg.parts.count 2020-06-29 09:32:52 +01:00
Dave Conway-Jones
f01866d76f Fix backwards migration of inject without topic 2020-06-28 15:09:07 +01:00
Steve-Mcl
53490cd368 Add developer options
- permits npm run build-dev.  skips minification & doesnt launch nodemon
- permits npm run dev for those without grunt installed globally
2020-06-27 11:24:06 +01:00
Nick O'Leary
c171088838 Fix reparenting nodes in outliner when they change 2020-06-26 17:21:20 +01:00
Nick O'Leary
0cc944dc5a Update changelog 2020-06-26 16:02:19 +01:00
Nick O'Leary
8bd8834237 Add default shortcut for core:show-help-tab 2020-06-26 15:58:02 +01:00
Nick O'Leary
84fc739c8d Merge pull request #2626 from node-red-hitachi/dev-zhcn
Update both zh-CN and zh-TW translation for release 1.1.0
2020-06-26 15:54:47 +01:00
JIYE YU
a7fa2cf0c9 update zh-TW translation for release-1.1.0 2020-06-26 10:40:17 +09:00
JIYE YU
1137cd5ca5 update zh-CN translation for nodes information 2020-06-26 10:38:41 +09:00
Nick O'Leary
d47906b525 Simplify example https settings 2020-06-25 20:10:26 +01:00
JIYE YU
13a59a882e update zh-CN translation for editor-client 2020-06-25 18:31:36 +09:00
Nick O'Leary
3b7348f862 Update changelog 2020-06-24 14:51:18 +01:00
Nick O'Leary
07585f01e3 Ensure all examples are consistently named 2020-06-24 14:43:27 +01:00
Nick O'Leary
5042137006 Merge branch 'pr_2585' into examples 2020-06-24 13:50:50 +01:00
Nick O'Leary
a2ea79130c Merge branch 'pr_2550' into examples 2020-06-24 13:49:56 +01:00
Nick O'Leary
979401f3ac Merge branch 'pr_2549' into examples 2020-06-24 13:49:33 +01:00
Nick O'Leary
4c98db2269 Outliner - add empty item when last config node moved 2020-06-24 13:18:46 +01:00
Nick O'Leary
209c5f337c Join node - clear timeout when msg.reset received
Fixes #2471
2020-06-24 10:02:42 +01:00
Nick O'Leary
8080ed4787 Merge pull request #2623 from node-red-hitachi/update-subflow-message-jp
Update subflow message jp
2020-06-23 16:18:36 +01:00
Nick O'Leary
f3be5f0e67 Merge pull request #2624 from node-red-hitachi/fix-subflow-input
Fix error on empty subflow input types
2020-06-23 16:18:02 +01:00
Hiroyasu Nishiyama
fb2d185c5f fix typedInput error on empty subflow input types 2020-06-23 09:24:29 +09:00
Hiroyasu Nishiyama
f2d696b48e update JP message catalogue for subflow input type 2020-06-23 08:39:44 +09:00
Dave Conway-Jones
5596d2df8e catch tiny possible escape for "\n " in exec command node label. 2020-06-22 22:41:22 +01:00
Dave Conway-Jones
b72ca439e2 Fix tcp in node finishing packets when in streaming base64 receive mode. 2020-06-22 22:40:15 +01:00
Nick O'Leary
432ed264c2 Remove hardcoded css
Fixes #2603
2020-06-22 20:39:41 +01:00
Nick O'Leary
0a411cbe4f Fix node button mouse pointer css 2020-06-22 14:37:52 +01:00
Nick O'Leary
581f71911a Change node linebreak handling to use "\n " 2020-06-22 13:08:35 +01:00
Nick O'Leary
e548bf8bc2 Handle import of node with non-default number of outputs 2020-06-22 10:43:09 +01:00
Nick O'Leary
1d944bab51 Improve display of focussed form element 2020-06-18 22:25:19 +01:00
Nick O'Leary
6f407750f5 Fix clearing group label 2020-06-18 22:25:00 +01:00
Nick O'Leary
19ffe8f308 Default group label to be shown and improve toggle button 2020-06-18 22:24:44 +01:00
Nick O'Leary
c9069d472f Make color/icon/label-pos pickers keyboard navigable 2020-06-18 22:23:50 +01:00
Nick O'Leary
68d3cc7507 Fix node toggle button initial opacity 2020-06-18 10:18:35 +01:00
Nick O'Leary
0c90376752 Align node labels on FF
FF doesn't seems to use alignment-baseline CSS class in the same
way Chrome does. Switched to domintant-baseline.
2020-06-18 10:11:22 +01:00
Hiroyasu Nishiyama
f5eb832cd2 merge template node example #1 and #4 2020-06-18 14:03:33 +09:00
Hiroyasu Nishiyama
c3a058a479 fixed switch node example#8 imported as flow 2020-06-18 13:56:01 +09:00
Hiroyasu Nishiyama
bc87210ce3 change scale and limit to limit and fixed typo 2020-06-18 13:35:09 +09:00
Hiroyasu Nishiyama
d595eb2614 change exec node example to use echo command and merge two examples 2020-06-18 13:12:58 +09:00
Hiroyasu Nishiyama
cb4d118ccc remove delay node example#6 from cookbook 2020-06-18 11:25:05 +09:00
Hiroyasu Nishiyama
62c723866a fix typos 2020-06-18 10:41:36 +09:00
Nick O'Leary
c9e54f2ba9 Bump for 1.1.0-beta.3 2020-06-17 10:54:15 +01:00
Nick O'Leary
e2c86c4b96 Fix wiring nodes from input back to output 2020-06-17 10:52:41 +01:00
Nick O'Leary
4469a334fd Fix sometimes unable to keyboard-move group to left/up 2020-06-17 09:57:25 +01:00
Nick O'Leary
aca379db6e Fix group position in outliner 2020-06-16 20:48:28 +01:00
Nick O'Leary
9ce5210c33 Handle unknown nodes with no icon 2020-06-16 20:34:45 +01:00
Nick O'Leary
4dd68452b4 Prevent node creep when switching tabs 2020-06-16 20:23:18 +01:00
Nick O'Leary
714b3d3fe0 Bump version to 1.1.0-beta.2 2020-06-16 15:21:03 +01:00
Nick O'Leary
2378e0d961 Fix up linting in search.js 2020-06-16 15:08:30 +01:00
Nick O'Leary
f78bbdc29f Update CHANGELOG for 1.1.0-beta.2 2020-06-16 15:03:56 +01:00
Nick O'Leary
708620f929 Merge pull request #2605 from johanneskropf/patch-1
use bash as shell for exec command if on linux
2020-06-16 14:45:34 +01:00
Nick O'Leary
9f0490fc12 Merge pull request #2619 from kazuhitoyokoi/dev-fixuitest3
Fix page object of inject node
2020-06-16 14:38:15 +01:00
Nick O'Leary
d37eebd8ed Merge pull request #2618 from kazuhitoyokoi/dev-addjpntranslations
Fix i18n bug in outliner
2020-06-16 14:22:55 +01:00
Kazuhito Yokoi
bfeda23ce5 Fix page object of inject node 2020-06-16 21:58:08 +09:00
Kazuhito Yokoi
52eb158231 Add Japanese translations for outliner, jsonata and runtime 2020-06-16 21:32:10 +09:00
Nick O'Leary
afb782410d Merge pull request #2617 from kazuhitoyokoi/dev-fixuitest2
Fix page object of debug node
2020-06-16 10:57:16 +01:00
Nick O'Leary
aebb7da3c7 Fix deleting node in group after changing selection 2020-06-16 10:54:50 +01:00
Kazuhito Yokoi
b90710945a Fix page object of debug node 2020-06-16 11:45:27 +09:00
Nick O'Leary
56efd51c06 Fixup padding of quick-add search box 2020-06-15 22:31:47 +01:00
Nick O'Leary
76728d1783 Move config nodes under type-level hierarchy in outline
Also adds user-count label and button to open search
2020-06-15 22:31:47 +01:00
Nick O'Leary
5b1fe9aa0a Emit nodes:change event for config node users list modified 2020-06-15 22:31:47 +01:00
Nick O'Leary
e3c8466819 Merge pull request #2616 from kazuhitoyokoi/dev-fixuitest
Fix page object of inject node
2020-06-15 22:27:30 +01:00
Kazuhito Yokoi
6a70cd1975 Fix page object of inject node 2020-06-15 20:36:41 +09:00
Nick O'Leary
2c45771024 Merge pull request #2593 from kazuhitoyokoi/master-adduitest4travis
Enable automated UI testing on Travis CI
2020-06-15 11:14:14 +01:00
Nick O'Leary
ebca8c0217 Increase group margin to avoid clash with status text 2020-06-14 23:44:26 +01:00
Nick O'Leary
752a080876 Fix event order when quick-adding node to group 2020-06-14 23:44:01 +01:00
Nick O'Leary
0541d9189d Switch RED.events.DEBUG messages to warn to get stacktraces 2020-06-14 23:43:15 +01:00
Nick O'Leary
0e454b08c8 Fix empty item handling for subflows/config in outliner 2020-06-14 22:46:59 +01:00
Nick O'Leary
2d0ca20a03 Fix search indexing of group nodes 2020-06-14 22:46:46 +01:00
Nick O'Leary
61d9ccf263 Avoid regenerating every node label on redraw 2020-06-13 23:02:10 +01:00
Hiroyasu Nishiyama
1c30584153 fix code indentation 2020-06-13 23:33:45 +09:00
Hiroyasu Nishiyama
5c5bebd689 fix handling of multi-line node label 2020-06-13 23:28:10 +09:00
johanneskropf
d9548a2891 moved check for shell 2020-06-12 17:14:11 +02:00
johanneskropf
d25e027201 moved building of execOpt object to line 36 2020-06-12 17:10:31 +02:00
Nick O'Leary
93211470d1 Merge pull request #2611 from node-red-hitachi/fix-group-merge-activation
Disable group merge for single item or non-group items
2020-06-12 08:50:12 +01:00
Nick O'Leary
b5800205c4 Merge pull request #2610 from node-red-hitachi/fix-charAt-error-on-undefined
Fix char at error on undefined when opening websocket-listner
2020-06-12 08:49:34 +01:00
Nick O'Leary
eeebf04509 Merge pull request #2609 from node-red-hitachi/fix-remove-from-group
fix empty placeholder not shown on remove from group
2020-06-12 08:49:09 +01:00
Nick O'Leary
f4f99f594d Merge pull request #2612 from node-red-hitachi/fix-group-position
Fix group position of empty group with multi-line label
2020-06-12 08:48:33 +01:00
Hiroyasu Nishiyama
5e8e739f78 fix position of empty group with multi-line label 2020-06-12 16:07:46 +09:00
Hiroyasu Nishiyama
a15adc43af merge code for checking menu activation 2020-06-12 09:54:11 +09:00
Hiroyasu Nishiyama
07556592c1 disable merge group menu for single item or non-group item 2020-06-12 08:42:15 +09:00
Hiroyasu Nishiyama
7694349078 prevent charAt call on websocket listener 2020-06-11 23:00:56 +09:00
Hiroyasu Nishiyama
4f3cb3103e make treelist of subflow/config nodes initialy has empty placeholder 2020-06-11 22:18:31 +09:00
Hiroyasu Nishiyama
842cd1ecf0 fix empty placeholder not shown on remove from group 2020-06-11 09:57:43 +09:00
Nick O'Leary
81a4f42673 Merge pull request #2607 from node-red-hitachi/fix-stringify-error
prevent conversion of circular structure
2020-06-10 13:38:32 +01:00
Hiroyasu Nishiyama
152e695f4c prevent conversion of circular structure 2020-06-10 19:56:16 +09:00
Nick O'Leary
5a0c10b80e Handle null status text in the editor
Fixes #2606
2020-06-10 10:58:44 +01:00
johanneskropf
06adf3d346 use bash as shell for exec command if on linux
This relates to:
https://github.com/node-red/node-red/issues/2604
and
https://discourse.nodered.org/t/exec-node-timeout-not-working-in-exec-mode/28040
and is a possible workaround for most issues related to kill described there.
This has only been tested on linux where this change applies so it would most definitely need more testing on windows/mac and maybe linux distributions where there is no bash(?).
2020-06-10 11:24:56 +02:00
Nick O'Leary
7be824640c Fix snapToGrid 2020-06-10 01:02:48 +01:00
Nick O'Leary
c061487a16 Massively reduce our dependency on d3 to render the view
This is a slightly scary set of changes to be making. It overhauls
how the view is rendered.

Rather than use d3 for every single part of generating the view,
we new use native DOM functions as much as possible.

d3 is still used for the basic heavy lifting of working out what
nodes/links etc need to be added/removed from the view. But once
it comes to rendering them, d3 is side-lined as much as possible.

There's room for further improvement. This change focusses on Nodes
and Links. It has not touched groups, subflow-ports and link-nodes.
2020-06-10 00:45:20 +01:00
Nick O'Leary
97fd34150f EditableList/TreeList - defer adding elements to DOM
Whenever a DOM element is modified, it causes the browser to re-examine
the whole hierarchy around the element to see if anything needs to change.

This can cause a lot of extra work if an element is added to the DOM and
then a lot of updates are applied to the element.

It is much better to get the element as close to its final state as
possible *before* adding it to the DOM.
2020-06-10 00:42:11 +01:00
Nick O'Leary
6d294a0c74 Prevent RED.stop being called multiple times if >1 signal received 2020-06-09 08:23:12 +01:00
Nick O'Leary
fe4ef354ac Flag a node as removed when it is disabled 2020-06-08 20:59:00 +01:00
Nick O'Leary
d28b8b5e8d Some performance improvements for TreeList 2020-06-08 17:13:05 +01:00
Nick O'Leary
f2b30d9a3f Resize info/help sidebars whenever sidebar is opened 2020-06-08 13:17:06 +01:00
Nick O'Leary
0a614f2741 Add search defaults to outliner searchBox 2020-06-08 12:02:21 +01:00
Nick O'Leary
a9fb50787b Add search presets option to searchBox widget 2020-06-08 12:01:29 +01:00
Nick O'Leary
ce7d7a8e01 Add RED.popover.menu as a new type of menu widget 2020-06-08 12:01:05 +01:00
Nick O'Leary
7006c00233 Add support for is:XYZ search flags
- is:unused
 - is:config
 - is:subflow
 - is:invalid
2020-06-08 11:55:24 +01:00
Nick O'Leary
21866634b3 Track subflow instances on the subflow node itself 2020-06-08 11:49:43 +01:00
Nick O'Leary
34dfd50702 Bump node-red-admin 0.2.6 2020-06-06 10:35:07 +01:00
Nick O'Leary
d9502a6c00 Refresh outline filter whenever something changes
Fixes #2601
2020-06-05 22:19:46 +01:00
Nick O'Leary
95f7b9205a Fix Help tab search box appearance 2020-06-05 17:14:25 +01:00
Nick O'Leary
d14d4944a0 Rename Node Information to Information in sidebar 2020-06-05 17:14:08 +01:00
Nick O'Leary
b4b2729e96 Do a sync-redraw after clearing to ensure clean state 2020-06-05 16:56:12 +01:00
Nick O'Leary
299b81f51b Fix Link node filter
Fixes #2600
2020-06-05 16:20:40 +01:00
Nick O'Leary
ad6b18e66f Make catch/status/complete/link filter case-insensitive 2020-06-05 16:00:02 +01:00
Nick O'Leary
091a462a42 Add 'add' option to touch radialMenu for quick-add dialog 2020-06-05 15:48:45 +01:00
Dave Conway-Jones
cb218a57f1 Merge branch 'dev' of https://github.com/node-red/node-red into dev 2020-06-05 11:22:42 +01:00
Dave Conway-Jones
ba8649117d ensure trigger node detects changes to number of outputs 2020-06-05 11:22:38 +01:00
Nick O'Leary
20daebd965 Ignore whitespace when checking function setup/close code 2020-06-05 10:36:49 +01:00
Nick O'Leary
7c2786969a Preserve event handlers when moving outliner items 2020-06-05 09:55:36 +01:00
Nick O'Leary
565aae5967 Add tooltips to outliner buttons 2020-06-05 09:55:36 +01:00
Dave Conway-Jones
16a634063a Fix debug node status to migrate old nodes to correct default mode. 2020-06-05 09:48:12 +01:00
Nick O'Leary
4c28b5b227 Only validate nodes once they have all been imported
This ensures any checks for dependent config nodes will pass
2020-06-04 17:06:29 +01:00
Nick O'Leary
a7a949377b Ensure configNode.users is updated properly on import 2020-06-04 17:06:11 +01:00
Nick O'Leary
c048b1a25b Exit with non-0 rc if admin command fails 2020-06-04 11:02:48 +01:00
Nick O'Leary
f7e7f7ed01 Add $moment docs 2020-06-04 09:46:17 +01:00
Nick O'Leary
5dfcb80de8 Fixing typos in the changelog 2020-06-04 00:05:12 +01:00
Nick O'Leary
c8f6100a6a Assume -d params are strings if they don't otherwise parse 2020-06-04 00:04:41 +01:00
Nick O'Leary
c0f4e07e10 Bump dependencies 2020-06-03 21:26:31 +01:00
Nick O'Leary
3c259b2c22 Update changelog 2020-06-03 21:10:28 +01:00
Nick O'Leary
3b3a2d4edc Merge pull request #2592 from node-red/admin-cli
Add node-red admin support
2020-06-03 20:59:19 +01:00
Nick O'Leary
e930098b51 Merge pull request #2447 from node-red/catch-more-signals
catch more signals to allow clean context flush on shutdown
2020-06-03 19:28:16 +01:00
Nick O'Leary
43d5df4a12 Merge branch 'dev' into catch-more-signals 2020-06-03 19:26:50 +01:00
Nick O'Leary
914cfdbc55 Merge pull request #2595 from jeancarl/dev
Filter list of nodes on node type
2020-06-03 18:59:57 +01:00
JeanCarl Bisson
aa8f4af339 Filter list of nodes on node type 2020-06-03 10:33:35 -07:00
Nick O'Leary
b6fbe7d07d Merge pull request #2588 from node-red/status-filter-nodes
Add compact searchBox to status/catch/complete nodes
2020-06-03 17:29:05 +01:00
Nick O'Leary
bf9d6c7ac4 Bump to node-red-admin 0.2.4 2020-06-03 16:53:45 +01:00
Nick O'Leary
139ae547c6 Add filter to link node 2020-06-03 15:54:51 +01:00
Nick O'Leary
8b252b458c Merge pull request #2590 from kazuhitoyokoi/dev-addjpnmsg
Add Japanese transaltions for http-request node, change node and https refresh logic
2020-06-03 14:31:52 +01:00
Nick O'Leary
efecfa328b Fix up event handling when deleting groups and tabs 2020-06-03 14:23:26 +01:00
Nick O'Leary
5651e7107f Bump to 0.2.3 node-red-admin 2020-06-03 10:48:52 +01:00
Nick O'Leary
b6b3ceef4d Add some proper validation of module/url properties in install api 2020-06-03 10:45:28 +01:00
Kazuhito Yokoi
e44d89c2af Enable automated UI testing on Travis CI 2020-06-03 17:22:25 +09:00
Nick O'Leary
3e74d75f28 Add node-red admin support 2020-06-02 23:39:36 +01:00
Nick O'Leary
6d737b9e4c Remove unneeded code on object delete in outliner
See https://github.com/node-red/node-red/pull/2578#discussion_r434163293
2020-06-02 22:53:34 +01:00
Nick O'Leary
dec82589d1 Handle missing projects runtime setting 2020-06-02 22:53:05 +01:00
Nick O'Leary
f0193b0f67 Add better messages for 404 errors on admin api 2020-06-02 22:52:22 +01:00
Kazuhito Yokoi
fdf8eb0657 Add japanese transaltions for http-request node and https refresh logic 2020-06-02 14:55:53 +09:00
Nick O'Leary
2ce424b567 Add compact searchBox to status/catch/complete nodes 2020-06-01 21:09:58 +01:00
Nick O'Leary
8995fa9ed1 Update changelog for 1.1.0-beta.1 2020-06-01 14:57:37 +01:00
Nick O'Leary
dc412b305c Tidy up unhandledRejection warning from context unit tests 2020-06-01 13:48:12 +01:00
Nick O'Leary
d7505da997 Merge pull request #2586 from Steve-Mcl/dev
Support setting title on typedInput option/ check
2020-06-01 13:29:01 +01:00
Nick O'Leary
4b54a81dfd Add test cases for setMessageProperty with non-object properties 2020-06-01 13:13:14 +01:00
Nick O'Leary
132254b3a5 Merge branch 'dev' into pr_2439 2020-06-01 11:56:19 +01:00
Steve-Mcl
9128b12960 Support setting title on typedInput option/ check 2020-06-01 11:25:42 +01:00
Nick O'Leary
e9104df047 Merge branch 'dev' into pr_2583 2020-06-01 10:20:40 +01:00
Nick O'Leary
bae52613ab Merge branch 'master' into dev 2020-06-01 09:38:39 +01:00
Nick O'Leary
18af906fd3 Merge branch 'dev' of github.com:node-red/node-red into dev 2020-06-01 09:37:14 +01:00
Nick O'Leary
d45415ab22 Merge branch 'master' into dev 2020-06-01 09:36:20 +01:00
Nick O'Leary
c6c42740c5 Merge pull request #2584 from sammachin/sammachin-settings_set_fix
fix for settings.set subsequent updates
2020-06-01 09:30:41 +01:00
Hiroyasu Nishiyama
b4c033ca50 add missing inject node examples 2020-06-01 15:20:53 +09:00
Hiroyasu Nishiyama
b67f2d874b add examples for function category nodes 2020-06-01 14:44:18 +09:00
Sam Machin
a8d8540346 fix for settings.set subsequent updates
By cloning the value the assert.deepEqual will now fail even for subsequent updates of the value without restarting Node-RED
2020-05-31 13:44:31 +01:00
Dave Conway-Jones
cbf1afc9fe turn off installer funding messages 2020-05-31 11:21:53 +01:00
Dave Conway-Jones
8a798e620a Ensure delay node rate limit timer is cleared on reset 2020-05-30 15:18:15 +01:00
Nick O'Leary
774751a25c Tweak HTTP Request GET payload handling labels 2020-05-29 17:35:18 +01:00
Nick O'Leary
13718032f6 Merge branch 'dev' into pr_2478 2020-05-29 17:04:54 +01:00
Nick O'Leary
1b497b340b Merge branch 'pr_2551' into dev 2020-05-29 16:52:19 +01:00
Nick O'Leary
bb41ab482c Rework the https refresh logic
- puts the node version check first
 - validates the refresh interval and keeps it in valid range
 - simplifies the error messages
 - uses parseFloat not parseInt so we can use fractions of hour
2020-05-29 16:50:53 +01:00
Nick O'Leary
215aab0fe4 Modify format of -D settings file 2020-05-29 11:44:13 +01:00
Nick O'Leary
666822cf51 Merge branch 'dev' into pr_2463 2020-05-29 11:04:38 +01:00
bartbutenaers
40101df6ec Refresh interval in hours 2020-05-29 00:11:14 +02:00
bartbutenaers
4adcb9c439 Refresh interval in hours 2020-05-29 00:08:07 +02:00
dxdc
a6cd0bf7e9 Add moment-timezone dependencies 2020-05-28 06:08:40 -05:00
Nick O'Leary
8158744829 Merge pull request #2578 from node-red-hitachi/fix-group-delete
fix deletion of group
2020-05-28 11:18:08 +01:00
Nick O'Leary
70c0c7bc14 Merge pull request #2582 from node-red-hitachi/fix-IE11-support
remove JSONata Array.from check for IE11
2020-05-28 11:04:15 +01:00
dxdc
fdda29f048 Support for moment-timezone 2020-05-28 01:20:10 -05:00
dxdc
95cc8ea80d Add missing unit test for accesses undefined environment variable from an expression 2020-05-28 00:48:11 -05:00
dxdc
18f8dde712 Add test for $moment jsonata expression 2020-05-27 23:47:04 -05:00
dxdc
effff3405b Add support for moment in jsonata expressions, based on elasticio/jsonata-moment 2020-05-27 23:26:57 -05:00
Hiroyasu Nishiyama
9d8cbcb993 remove JSONata Arra.from check for IE11 2020-05-28 13:22:14 +09:00
Hiroyasu Nishiyama
3345f2f3b8 simplify code structure 2020-05-28 10:23:50 +09:00
Nick O'Leary
bcf1d986a4 Merge branch 'master' of github.com:node-red/node-red 2020-05-27 17:15:45 +01:00
Nick O'Leary
a51e74bfa1 Bump jquery/migrate to latest versions 2020-05-27 17:15:33 +01:00
Nick O'Leary
cf00acac04 Bump jquery/migrate to latest versions 2020-05-27 17:13:33 +01:00
Nick O'Leary
876a7a4646 Add bulk-activate actions for debug node
Adds the actions:

 - core:activate-all-debug-nodes
 - core:activate-all-flow-debug-nodes

to match the deactivate* actions.

Also adds:

 - core:activate-selected-debug-nodes
 - core:deactivate-selected-debug-nodes

Adds a new httpAdmin route - /debug/(enable/disable) - that can be
use to bulk enable/disable nodes via HTTP Post.
2020-05-27 12:20:23 +01:00
Nick O'Leary
95d1b7bc36 Merge branch 'dev' into pr_2570 2020-05-27 11:52:37 +01:00
Nick O'Leary
d4ae0b0a2e Merge pull request #2576 from kazuhitoyokoi/dev-addjapanesetranslations
Update Japanese translations for trigger, tcp request, debug nodes
2020-05-27 10:26:42 +01:00
Nick O'Leary
36739fb444 Merge pull request #2577 from node-red-hitachi/update-function-message-jp
Update Japanese message catalogue for function node
2020-05-27 10:26:25 +01:00
Nick O'Leary
7906c28abb Merge pull request #2579 from ristomatti/bug/editor-underscore-hidden
Fix editor underscore visibility on Linux systems
2020-05-27 10:26:01 +01:00
Kazuhito Yokoi
f87b40941f Update Japanese translations for trigger, tcp request, debug nodes 2020-05-27 10:44:17 +09:00
Hiroyasu Nishiyama
05f816fc5d not emit change event on group delete 2020-05-25 08:46:35 +09:00
Ristomatti Airo
f9a157fe18 Fix editor underscore visibility on Linux systems
- seems to relate to DejaVu Sans font rendering
- fixes #2104
2020-05-23 18:41:44 +03:00
Hiroyasu Nishiyama
ca213589ac update Japanese message catalogue for function node 2020-05-23 21:39:52 +09:00
Hiroyasu Nishiyama
c5ca9fafee fix deletion of group 2020-05-23 21:20:23 +09:00
Nick O'Leary
82b3a97d99 Remove _info/type tests for subflow node tests 2020-05-22 21:23:55 +01:00
Nick O'Leary
1c94064c57 Remove unused _info/_type subflow env var magic values 2020-05-22 20:54:06 +01:00
Nick O'Leary
7969dd431f [function] Make the function node top-level async
This allows you to use 'await' in a function node without
having to wrap it in another promise/async function.
2020-05-22 20:49:18 +01:00
Nick O'Leary
22e7ddcb1d Merge branch 'dev' into pr_2490 2020-05-22 15:43:17 +01:00
Nick O'Leary
b1eafac67a Fixup IE11 Array.from polyfill 2020-05-22 15:31:38 +01:00
Nick O'Leary
5d81cec00c Merge branch 'pr_2498' into dev 2020-05-22 15:22:12 +01:00
Nick O'Leary
9512450d7c Reduce duplicated code in Function node html 2020-05-22 14:57:28 +01:00
Nick O'Leary
ed1998162f Fix gutter button position in outline tree 2020-05-21 23:05:29 +01:00
Nick O'Leary
ac2a21f992 Fixup positioning of create-project icons in startup dialog 2020-05-21 17:46:29 +01:00
Nick O'Leary
ad78ce0eb6 [projects] Allow remote branch dialog to create non-default remote branches 2020-05-21 17:26:27 +01:00
Nick O'Leary
8ce49c25d4 Ensure auth failure on project fetch identifies the remote
Fixes #2545
2020-05-21 17:25:51 +01:00
Nick O'Leary
4c24bd4ab9 [projects] Allow remote branch dialog to create non-default remote branches 2020-05-21 17:19:54 +01:00
Nick O'Leary
0de49e2a75 Ensure auth failure on project fetch identifies the remote
Fixes #2545
2020-05-21 17:18:46 +01:00
Nick O'Leary
05c3f459ad Add #! lines to project shell scripts
Fixes #2548
2020-05-21 15:33:10 +01:00
Nick O'Leary
50aaef5103 Make all dialogs handle smaller height screens better
Covers library, clipboard and project dialogs
2020-05-21 15:14:39 +01:00
Nick O'Leary
38872049fd Fix checkbox appearance on status/complete/catch/link nodes 2020-05-21 10:39:49 +01:00
Nick O'Leary
5dc1cc54d5 Add basic Array.from polyfill for IE11 2020-05-21 10:26:24 +01:00
Nick O'Leary
57f0fbbb98 Add some more trap form elements to workaround Chrome autofill 2020-05-21 10:21:18 +01:00
Nick O'Leary
977fef03b0 Merge pull request #2572 from jeancarl/dev
Moved options property from Outputs to Inputs section
2020-05-20 13:47:54 +01:00
Nick O'Leary
7d67e6a276 [outliner] handle items being added out-of-order
Closes #2573
2020-05-20 12:37:19 +01:00
Nick O'Leary
0832be5970 Merge pull request #2574 from node-red-hitachi/fix-grunt-error
add variable declaration to prohibit jshint error
2020-05-20 11:38:56 +01:00
Hiroyasu Nishiyama
2343fbd86a add variable declaration to prohibit jshint error 2020-05-20 13:47:56 +09:00
JeanCarl Bisson
367ebc1dd4 Moved options property from Outputs to Inputs section 2020-05-19 20:31:02 -07:00
Nick O'Leary
15cc88de6c [info-sidebar] Handle node/group/flows with \\n in their name 2020-05-19 17:53:20 +01:00
cinhcet
64b3c11682 debug nodes in subflows are ignored and changed name of callback to callback 2020-05-19 18:00:03 +02:00
Nick O'Leary
b8784185e8 Merge pull request #2568 from node-red-hitachi/fix-line-break-of-group-name
fix line break of group label
2020-05-19 15:09:06 +01:00
Nick O'Leary
fdc721baa1 Merge pull request #2569 from node-red/node-labels
Add action to toggle node label visiblity
2020-05-19 14:43:54 +01:00
Nick O'Leary
03b64bc493 Merge pull request #2564 from node-red/debugStatus
Let debug node show status indpendent of main output
2020-05-19 14:41:52 +01:00
cinhcet
a6a781f67c fix debug undo/redo by introducing a sideEffectCallback in history object 2020-05-19 13:51:08 +02:00
Nick O'Leary
fe1f8ca0a8 Delay when nodes are added to internal model on import
Closes #2567

This ensures when the node:added event fires, all possible
changes have already been applied such as remapping node ids.

This avoids the need to emit a separate node:changed event.
2020-05-19 11:28:38 +01:00
Dave Conway-Jones
a600feb5de Move debug status text length check to just before dsiplay. 2020-05-19 10:57:33 +01:00
Nick O'Leary
1f2c0a78c2 Only refresh sidebar on tab edit if nothing else selected 2020-05-19 10:13:35 +01:00
Nick O'Leary
e4b9c6a2ee Merge pull request #2571 from node-red-hitachi/fix-delete-tab
Fix error on deleting tab
2020-05-19 10:06:49 +01:00
cinhcet
a69db4d572 move to debug.html, rename to "deactivate" instead of "disable" 2020-05-19 11:03:15 +02:00
Hiroyasu Nishiyama
12c92072d0 not update sidebar for deleted tab 2020-05-19 09:27:50 +09:00
Nick O'Leary
e674d9246b [treeList] Ensure removed item is also removed from selected set 2020-05-19 00:15:51 +01:00
cinhcet
b71f81af57 added editor action to disable all debug nodes on a global/current workspace level 2020-05-18 22:49:10 +02:00
Nick O'Leary
b3535281ef Add action to toggle node label visiblity 2020-05-18 21:08:01 +01:00
Hiroyasu Nishiyama
5f5e6ea845 fix error on empty label 2020-05-17 11:44:23 +09:00
Dave Conway-Jones
64d2e80690 Merge branch 'dev' of https://github.com/node-red/node-red into dev 2020-05-16 22:24:38 +01:00
Dave Conway-Jones
b6e0568e76 Trigger node - hide second outpiut option when not appropriate
Update status to better reflect multiple streams in flight
2020-05-16 22:24:29 +01:00
Nick O'Leary
dca5b3b2a0 Merge pull request #2566 from node-red-hitachi/fix-outliner-subflow
Fix outliner display of newly created subflow
2020-05-16 21:17:56 +01:00
Hiroyasu Nishiyama
243915516e fix line break of group label 2020-05-16 20:56:21 +09:00
Hiroyasu Nishiyama
bc3683d8f6 fix outliner for subflow addition 2020-05-16 11:25:55 +09:00
Dave Conway-Jones
1d36ce0fdf debug node: add auto handling of error and status msgs if set to show status 2020-05-15 23:32:27 +01:00
Dave Conway-Jones
88d4d306f3 Add some colour smarts to debug status 2020-05-15 16:06:34 +01:00
Dave Conway-Jones
184d928cf7 ensure debug status in sync with main option 2020-05-14 23:01:26 +01:00
Nick O'Leary
fbd911ed27 [outliner] Keep outliner selection in sync with workspace 2020-05-14 22:08:25 +01:00
bartbutenaers
dec3762b7a Remove debugger statement 2020-05-14 22:43:50 +02:00
Dave Conway-Jones
a849872c21 ensure old config work with new fields 2020-05-14 16:28:38 +01:00
Dave Conway-Jones
1d71fb3554 Let debug node show status indpendent of main output 2020-05-14 15:38:48 +01:00
bartbutenaers
0d3bf0cd00 Https refresh settings 2020-05-13 23:49:30 +02:00
bartbutenaers
6c766eba86 Logs internationalisation 2020-05-13 23:46:33 +02:00
bartbutenaers
cc760acb62 Update packages/node_modules/node-red/red.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2020-05-13 23:24:57 +02:00
bartbutenaers
f4d4bf8779 Update packages/node_modules/node-red/red.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2020-05-13 23:24:40 +02:00
bartbutenaers
90f62e5e4a Update packages/node_modules/node-red/red.js
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
2020-05-13 23:23:29 +02:00
Nick O'Leary
f1bd3e1711 Merge pull request #2562 from kazuhitoyokoi/dev-addjapanesetranslations
Add Japanese translations for Outliner, help sidebar and loading progress bar
2020-05-13 09:18:50 +01:00
Nick O'Leary
01dde8bea5 Merge pull request #2563 from kazuhitoyokoi/dev-fixgroping
Fix description of grouping nodes
2020-05-13 08:54:19 +01:00
Kazuhito Yokoi
341c66a199 Fix description of grouping nodes 2020-05-13 12:32:02 +09:00
Nick O'Leary
bc1fb2770b [groups] Change style of disabled groups 2020-05-12 13:53:20 +01:00
Nick O'Leary
9f1373945b [help-sidebar] Tidy up some errors 2020-05-12 13:42:01 +01:00
Kazuhito Yokoi
266ee2ca81 Add Japanese translations for Outliner 2020-05-12 20:06:07 +09:00
Kazuhito Yokoi
35738cc1a3 Update Japanese translations for CSV node 2020-05-12 20:03:00 +09:00
Nick O'Leary
ff310f89bd [groups] Support RED.view.reveal(group-id) 2020-05-12 10:59:41 +01:00
Hiroyasu Nishiyama
2dd004f6cd rename example name of complete node 2020-05-12 15:56:35 +09:00
Hiroyasu Nishiyama
964b7e0e23 rename example name of status node 2020-05-12 15:53:59 +09:00
Hiroyasu Nishiyama
bc039bde81 rename example name of catch node 2020-05-12 15:52:01 +09:00
Hiroyasu Nishiyama
9505f82d9b split debug node examples 2020-05-12 15:43:28 +09:00
Kazuhito Yokoi
9189db5531 Add Japanese translations for Outliner, help sidebar and loading progress bar 2020-05-12 11:06:29 +09:00
bartbutenaers
bfa5f39b6d Asynchronous https support 2020-05-11 23:33:54 +02:00
bartbutenaers
15f97bbf26 Asynchronous https support 2020-05-11 23:29:38 +02:00
Nick O'Leary
90ba761325 Merge pull request #2555 from node-red/authMiddleware
Add httpAdminMiddleware to allow custom headers on admin routes
2020-05-11 16:28:07 +01:00
Nick O'Leary
ddd428f76e Merge pull request #2558 from node-red/progress-bar
Add a loading progress bar
2020-05-11 16:27:40 +01:00
Nick O'Leary
0c83fa7060 Add loading progress bar 2020-05-11 16:27:22 +01:00
Nick O'Leary
f2e2c7e4d0 Merge pull request #2556 from node-red/outliner
Add Outliner to Info sidebar and add help sidebar
2020-05-11 16:26:41 +01:00
Nick O'Leary
717bfffa63 [Inject] Use legacy properies by preference 2020-05-11 16:07:26 +01:00
Hiroyasu Nishiyama
a764a4a44b update initial contents for setup & close code 2020-05-11 22:55:07 +09:00
Dave Conway-Jones
247fa0ce7c Fix inject so more backwards compatible
reuse old payload property and copy over topic if a string.
2020-05-11 14:37:19 +01:00
Nick O'Leary
13932b2cfb Merge pull request #2553 from node-red-hitachi/add-reset-to-batch-node
Add reset feature to batch node
2020-05-11 09:39:09 +01:00
Nick O'Leary
0bd0540d2f Merge pull request #2559 from kazuhitoyokoi/dev-addjapanesetranslations
Add Japanese translations for grouping nodes
2020-05-11 09:38:14 +01:00
Nick O'Leary
9a17cc503c Merge pull request #2561 from node-red-hitachi/inject-node-error-handling
Update inject node error handling
2020-05-11 09:37:43 +01:00
Dave Conway-Jones
89a048e5fa add docker to environment list 2020-05-11 09:01:35 +01:00
Nick O'Leary
88bc022e2a [help-sidebar] hide toc when directly setting content 2020-05-11 09:00:12 +01:00
Hiroyasu Nishiyama
00e080459e update handling of invalid jsonata expression 2020-05-11 14:51:47 +09:00
Hiroyasu Nishiyama
5b197adf33 update according to comments 2020-05-11 14:37:14 +09:00
Kazuhito Yokoi
9019c31f91 Add Japanese translations for core nodes 2020-05-08 15:12:47 +09:00
Kazuhito Yokoi
2e14703b16 Add Japanese translations for grouping nodes 2020-05-08 15:01:39 +09:00
Nick O'Leary
f87698438d [outliner] Handle switching projects properly 2020-05-07 21:48:47 +01:00
Nick O'Leary
4af1cf1d1f [help-sidebar] Refresh help on node selection 2020-05-07 15:19:56 +01:00
Nick O'Leary
d6ad7dc6eb Add show-examples-import-dialog action 2020-05-07 13:46:18 +01:00
Nick O'Leary
f25e4ea520 [help-sidebar] Include subflow help in sidebar 2020-05-07 12:34:15 +01:00
Nick O'Leary
17891d373b [outliner] Fix positioning of tips box 2020-05-07 10:32:25 +01:00
Nick O'Leary
9f29149d87 [help-sidebar] Add help sidebar 2020-05-06 16:15:12 +01:00
Nick O'Leary
010e20989a [popover] Allow hover-type popovers to contain buttons 2020-05-06 16:12:07 +01:00
Hiroyasu Nishiyama
c885f2edaa add example of link node across tabs 2020-05-06 12:36:28 +09:00
Hiroyasu Nishiyama
ae5a7176ba change comment of debug node example 2020-05-06 12:36:05 +09:00
Hiroyasu Nishiyama
ee13cd10fe add checks for interval value 2020-05-06 11:21:58 +09:00
Nick O'Leary
b0f9bf2c62 Add httpAdminMiddleware for admin routes 2020-05-05 18:13:21 +01:00
Nick O'Leary
9fbfc3d677 Merge branch 'dev' into outliner 2020-05-05 17:47:59 +01:00
Nick O'Leary
189389f96a [inject] Tidy up node label 2020-05-05 17:35:30 +01:00
Nick O'Leary
2af7066512 Merge branch 'dev' into outliner 2020-05-05 15:11:35 +01:00
Hiroyasu Nishiyama
9cbc40a229 fix message catalogue for CSV ndoe & update japanese message catalogue (#2554) 2020-05-05 14:43:08 +01:00
Nick O'Leary
18bf220ca4 Merge pull request #2413 from dvv/patch-1
Allow to know particular session from status node
2020-05-05 14:42:26 +01:00
Hiroyasu Nishiyama
8750c4b121 add reset feature to batch node 2020-05-05 21:07:55 +09:00
Dave Conway-Jones
417d2cb40a Add nodejs14 to Travis test matrix 2020-05-04 21:48:34 +01:00
Dave Conway-Jones
36b0698432 Trigger - redo second output code update 2020-05-04 17:11:09 +01:00
Nick O'Leary
0edc57f0e3 Merge pull request #2547 from node-red-hitachi/fix-project-menu-item-activation
Activate project menu after initial clone
2020-05-04 16:14:42 +01:00
Nick O'Leary
3d76137247 [inject] Modify output labels for multi-value inject 2020-05-04 15:37:09 +01:00
Dave Conway-Jones
df9d231389 Merge branch 'dev' of https://github.com/node-red/node-red into dev 2020-05-04 14:17:17 +01:00
Dave Conway-Jones
e2aebaf0e7 CSV : add warn when unpaired quotes detected on input.
helps handling now that we allow line breaks within fields (as per spec).
2020-05-04 14:17:11 +01:00
Nick O'Leary
20e84a847a Merge pull request #2435 from PaulWieland/dev
Adding user definable properties to inject node
2020-05-04 14:02:10 +01:00
Nick O'Leary
ad4779e32f Merge pull request #2436 from node-red/add-trigger-second-output
Add second output to trigger node
2020-05-04 12:48:25 +01:00
Nick O'Leary
90537e42ba Merge branch 'dev' into add-trigger-second-output 2020-05-04 12:45:01 +01:00
Nick O'Leary
4615465599 Merge pull request #2527 from node-red/enhance-csv
Enhance csv to allow output of column headers once only
2020-05-04 12:43:16 +01:00
Nick O'Leary
95418724fa Merge pull request #2540 from vladimir-kazan/fix-deprecation-warning
Fix: Remove nodejs deprecation warning in 21-httpin node
2020-05-04 12:42:30 +01:00
Nick O'Leary
989cb05257 Merge pull request #2544 from kazuhitoyokoi/master-fixuitest
Fix test cases for UI testing
2020-05-04 12:41:30 +01:00
Nick O'Leary
d7df20413d Merge branch 'master' into dev 2020-05-04 11:41:44 +01:00
bartbutenaers
f7e0f55c13 httpsRefreshInterval in seconds 2020-05-02 22:24:04 +02:00
bartbutenaers
e16f48c9fd httpsRefreshInterval in seconds 2020-05-02 22:22:36 +02:00
bartbutenaers
4694644043 Refresh interval in milliseconds 2020-05-02 14:41:46 +02:00
bartbutenaers
f468d6e947 Rename fix 2020-05-02 14:40:01 +02:00
bartbutenaers
9a19477796 Refresh https settings 2020-05-02 07:54:58 +02:00
bartbutenaers
00d41c6de2 Refresh https settings 2020-05-02 07:52:20 +02:00
Nick O'Leary
fc2a9a85ff [outline] Reveal selected item after clearing outline filter 2020-05-01 17:51:44 +01:00
Nick O'Leary
78c86880e4 [outline] Update information section of info sidebar 2020-05-01 17:39:54 +01:00
Nick O'Leary
aca61c0354 Modify RED.panels to use flexbox position 2020-05-01 17:38:23 +01:00
Nick O'Leary
73dde4de51 Allow node edit dialog to be opened on a non-default tab 2020-05-01 17:37:42 +01:00
Nick O'Leary
597c4a2e4f Add createNodeIcon and getDarkerColor to RED.utils 2020-05-01 17:37:15 +01:00
Hiroyasu Nishiyama
62ec7f4d37 add examples of common category nodes 2020-05-02 00:18:24 +09:00
Hiroyasu Nishiyama
319c7e9e9f add join node example 2020-05-01 16:49:17 +09:00
Hiroyasu Nishiyama
580492b0c8 create sequence category 2020-05-01 10:39:52 +09:00
Hiroyasu Nishiyama
655ce7b87a add split node example 2020-05-01 10:37:47 +09:00
Hiroyasu Nishiyama
4e09b404a2 fix comment description of sort sequence example 2020-05-01 09:55:38 +09:00
Hiroyasu Nishiyama
748f831495 rename examples & correct sequence size 2020-05-01 09:48:31 +09:00
Hiroyasu Nishiyama
bb3b87814c add sort node examples 2020-05-01 09:24:29 +09:00
Hiroyasu Nishiyama
0bfe20182f rename batch examples 2020-04-30 23:55:22 +09:00
Hiroyasu Nishiyama
4245c0a0ad activate project menu after initial clone 2020-04-30 12:59:10 +09:00
Paul Wieland
25aadc690a Added i18n and legacy output label support 2020-04-27 12:16:20 -04:00
Nick O'Leary
12dc4ab1fa [outline] Connect search dialog to outline filter box 2020-04-27 15:23:39 +01:00
Nick O'Leary
55a5917282 [search] Refactor search to use editor events to generate index 2020-04-27 14:43:22 +01:00
Nick O'Leary
a5b33d11fc [outline] Add outline section to info sidebar 2020-04-27 11:17:19 +01:00
Nick O'Leary
d2d872f51c TreeList updates for the outliner sidebar
All data items now get their `item.treeList` api added even if deferBuild is set.
This means the apis can be used regardless of whether the tree has built their
ui pieces.

Also adds a number of new api calls

Top-level methods:

 - clearSelection - clears selection from the list
 - filter(filterFunc) - filters the tree using the provided function

Data item functions:

 - item.treeList.sortChildren(sortFunction)
 - item.treeList.replaceElement(element)
2020-04-27 11:14:47 +01:00
Nick O'Leary
5c0b500f48 Reorder group creation so groups:add is fired before nodes:change 2020-04-27 11:06:28 +01:00
Nick O'Leary
28418288e3 Allow RED.notify.popover to have a position offset 2020-04-27 11:05:32 +01:00
Nick O'Leary
0150769c17 EditableList api calls must not return nested list items 2020-04-27 11:04:41 +01:00
Nick O'Leary
2eaea02489 Make selected list item more distinct 2020-04-27 11:04:04 +01:00
Nick O'Leary
1a9c4b7714 All node button to be clicked via api call 2020-04-27 11:03:43 +01:00
Nick O'Leary
d9f710aa52 Only update disabled workspace css if it is the active ws 2020-04-27 11:03:02 +01:00
Nick O'Leary
2069cc4392 Add flows:reorder event 2020-04-27 10:49:54 +01:00
Nick O'Leary
f78be9050a Reorder inital load so projects:load event emits before any nodes:add 2020-04-27 10:49:14 +01:00
Nick O'Leary
feb5d13e1c Bump for 1.0.6 2020-04-24 13:55:34 +01:00
Nick O'Leary
a3b0448f53 Handle clone of null in utils
Fixes #2536
2020-04-24 13:54:49 +01:00
Nick O'Leary
3dfbefb9f5 Handle error objects when reporting in palette manager 2020-04-24 13:54:49 +01:00
Nick O'Leary
9f6bac1b1b Revert to cron 1.7.2
See https://github.com/kelektiv/node-cron/issues/478
2020-04-24 13:54:49 +01:00
Nick O'Leary
0f2ed14d16 Update to JSONata 1.8.3 2020-04-24 13:54:49 +01:00
Paul Wieland
3e898c487a Corrected output label logic 2020-04-22 15:13:11 -04:00
Nick O'Leary
efb9dce92f Merge pull request #2543 from node-red/editor-events
Add more consistent events in the editor
2020-04-22 13:51:36 +01:00
Kazuhito Yokoi
f024e0bbed Fix test cases for UI testing 2020-04-21 21:58:18 +09:00
Dave Conway-Jones
3f1bb6771a Dont try and clone properties of a null object 2020-04-21 11:14:11 +01:00
Dave Conway-Jones
0b3ced5203 add test for cr lf in input and enhance odd quotes tests 2020-04-21 10:58:36 +01:00
Nick O'Leary
373267c53b Add more consistent events in the editor
This introduces a much more consistent set of events within the editor
for whenever a element is added, removed or modified.

The events emited on the `RED.events` event system. The event names
take the form: `"<thing>:<action>"`.

`<thing>` can be one of:
 - nodes
 - flows
 - subflows
 - groups
 - links

`<action>` can be one of:
 - add
 - remove
 - change

The payload of the events is the object in question.

There is also:
 - flows:reorder    - when tabs are reordered. Payload is array of flow ids.
 - workspace:clear  - when the workspace is emptied - part of switching projects

The `nodes:change` event was already used by RED.nodes.dirty() to cause
the Deploy button to become active. This renames that event to:
 - workspace:dirty  - Payload is boolean flag for the dirty state

This commit also updates the Palette to use the subflows:change event to
only redraw subflows that have actually changed rather than refresh them
all whenever one of them *might* have changed. This removes a noticable
flicker of the icon which was needlessly being redrawn.
2020-04-20 22:23:34 +01:00
Nick O'Leary
ae3e250269 Merge branch 'dev' of github.com:node-red/node-red into dev 2020-04-20 21:57:46 +01:00
Vladimir Kuznetsov
33200b2d08 Fix: Remove nodejs deprecation warning in 21-httpin node 2020-04-15 15:39:59 +02:00
Nick O'Leary
b032e00d01 [groups] increase group border radius 2020-04-14 22:39:42 +01:00
Dave Conway-Jones
fda95dfc5d csv node: remove xs console.log 2020-04-14 11:02:14 +01:00
Dave Conway-Jones
bc96f2d0cb udp node: when reusing input socket honour the broadcast mode. 2020-04-11 22:33:11 +01:00
Hiroyasu Nishiyama
c649e1b4a2 update promise & message handling 2020-04-10 23:06:43 +09:00
Nick O'Leary
f54ed8ebd1 Prevent button label wrapping in typedInput 2020-04-09 20:20:52 +01:00
Paul Wieland
2efc2bc186 Update 20-inject.html
Scroll the div down automatically when the user changes the repeat drop down.
2020-04-08 13:37:51 -04:00
Paul Wieland
4595a77c41 tip removed from inject node, cleanup i18n 2020-04-08 12:30:13 -04:00
Paul Wieland
7c1853431a Update 20-inject.html
Cleanup old payload, topic & type.
Move name and remove tip.
2020-04-08 12:29:55 -04:00
Dave Conway-Jones
5eed4672ed rtrigger node - reapply - passing topic through to output fix that seemed to only half merge 2020-04-06 22:58:00 +01:00
Nick O'Leary
aafa4fe0b9 Bump dev branch to 1.1.0 2020-04-06 20:29:22 +01:00
Hiroyasu Nishiyama
161f6090c1 update initialize & finalize processing of function node 2020-04-06 16:34:41 +09:00
Nick O'Leary
efad7270b7 Add polyfills for IE11 2020-04-03 16:57:15 +01:00
Dave Conway-Jones
24eb78d137 add ja translations 2020-04-03 16:55:43 +01:00
Dave Conway-Jones
e969a1c97c Let CSV node only send headers once
(and then reset that on msg.reset)
and also accept msg.columns csv string to set column headers if not specified in node.
And Add tests
2020-04-03 15:54:19 +01:00
tmdoit
4f31632863 Fix: Allow CR and LF control chars to be a part of the value (#2526)
To properly parse CSV data.
2020-04-03 15:10:33 +01:00
Nick O'Leary
344c9fe57e Merge branch 'master' into dev 2020-04-02 23:24:42 +01:00
Nick O'Leary
24f7000918 [groups] Remove padStart because IE11 2020-04-02 23:23:41 +01:00
Nick O'Leary
6ff3286d78 Merge branch 'master' into dev 2020-04-02 16:52:55 +01:00
Nick O'Leary
fbfc74e5ca [groups] Ensure newly imported nodes have width/height 2020-04-02 11:58:08 +01:00
Nick O'Leary
1b5654001c Merge pull request #2396 from amodelbello/master
Replace pvorb/clone with lodash.clonedeep to prevent errors when cloning complex objects
2020-04-01 20:22:40 +01:00
Nick O'Leary
e0f3e94e2b Merge branch 'dev' into master 2020-04-01 20:22:25 +01:00
Nick O'Leary
5da89892b4 [groups] Draw group selection above all other groups 2020-04-01 14:10:35 +01:00
Nick O'Leary
04da13eaf9 Merge branch 'pr_2523' into dev 2020-03-31 15:59:31 +01:00
Nick O'Leary
7fa4df082e Force sync redraw of view when replacing unknown nodes 2020-03-31 15:58:51 +01:00
Nick O'Leary
ae001c5e82 Merge branch 'master' into dev 2020-03-31 15:35:46 +01:00
Hiroyasu Nishiyama
fa8236ee2c update for recent change of dev branch 2020-03-31 20:32:07 +09:00
Hiroyasu Nishiyama
08ec04c889 merge upstream/dev 2020-03-31 19:05:22 +09:00
Hiroyasu Nishiyama
e5150ea012 force redraw after node installation 2020-03-31 16:48:20 +09:00
Nick O'Leary
222ece2533 Merge pull request #2493 from node-red/groups
Grouping Nodes
2020-03-30 23:43:27 +01:00
Nick O'Leary
294696daf5 Merge branch 'dev' into groups 2020-03-30 23:42:52 +01:00
Nick O'Leary
d099356207 Merge branch 'master' into dev 2020-03-30 23:42:40 +01:00
Nick O'Leary
05fc3c5eca Merge branch 'master' into dev 2020-03-30 23:41:33 +01:00
Nick O'Leary
b1d0013214 [groups] Better ordering of group elements on the DOM 2020-03-26 22:51:06 +00:00
Nick O'Leary
94ef25bbb9 [groups] i18n group messages 2020-03-26 22:50:46 +00:00
Nick O'Leary
13830ffc9c [groups] Tidy up Info sidebar summary of group selection 2020-03-26 21:00:22 +00:00
Nick O'Leary
e0bef941b4 [groups] Include groups when copying whole tabs 2020-03-26 20:26:58 +00:00
Nick O'Leary
03e9522d98 [groups] Include groups when exporting 2020-03-26 18:01:57 +00:00
Nick O'Leary
1bdbd31b96 [groups] Overhaul group drag handling for empty groups 2020-03-26 15:27:34 +00:00
Nick O'Leary
ef9db701f8 [groups] Add default group style to css so can be themed 2020-03-26 15:25:08 +00:00
Nick O'Leary
afb564a4fc [groups] Add copy/paste group style actions 2020-03-26 15:24:02 +00:00
Nick O'Leary
3e7f58dedd [groups] Include group counts in copy/paste notifications 2020-03-26 15:22:59 +00:00
Nick O'Leary
2e364b6d9a Merge pull request #2516 from alexk111/patch-1
Remove duplicate
2020-03-25 17:53:12 +00:00
Alex Kaul
b4177836a8 Remove duplicate 2020-03-26 00:42:01 +07:00
Nick O'Leary
5b2ee21204 Merge pull request #2506 from node-red-hitachi/fix-subflow-template-tab
fix tab appearance of subflow template panel
2020-03-24 15:15:21 +00:00
Nick O'Leary
7c91c4ae5a [groups] Prevent subflow port nodes being added group 2020-03-24 14:05:35 +00:00
Nick O'Leary
7bc3b662e4 [groups] Fix up various delete/undo actions with groups 2020-03-24 14:05:09 +00:00
Nick O'Leary
64af1f7e9b [groups] Lasso should select top-most group of selection 2020-03-23 22:04:40 +00:00
Nick O'Leary
f0038e9796 [groups] Use requestAnimationFrame for view redraw
This moves the expensive redraw code out of the event handling phase
and onto the browser's repaint phase. This makes the event handling
more responsive, particularly when dragging a large number of nodes.

It also removes lots of unnecessary anonymous functions in the redraw
code that should also improve performance.
2020-03-23 21:30:52 +00:00
Nick O'Leary
768aa4ac92 [groups] getGroupAt should return top most group 2020-03-23 21:30:09 +00:00
Nick O'Leary
f61c137ea3 [groups] Improve styling of group selection/highlight 2020-03-23 14:51:18 +00:00
Nick O'Leary
20a8059758 [groups] Add style options for group label 2020-03-20 20:00:03 +00:00
Nick O'Leary
58696c6ad4 [groups] Add better colour picker for group fill/stroke 2020-03-19 13:41:54 +00:00
Dave Conway-Jones
b5ed018bae csv node - add tests for blank columns and null values 2020-03-18 16:18:16 +00:00
tmdoit
91b7dd988e [CSV node] Add support for parsing empty strings and null values (#2510)
* [CSV node] Add support for parsing empty strings and null values

* Add new lines at the end and fix script type.

* Last one script type fix

* Naming change
2020-03-18 15:54:10 +00:00
Nick O'Leary
5cd2791506 [groups] Add groups to flow util unit tests 2020-03-16 23:01:57 +00:00
Nick O'Leary
9b2e9ec41a [groups] Support undo of convert to subflow from inside group 2020-03-16 22:51:54 +00:00
Kazuhito Yokoi
08ef9ee682 Add backslash handling to library 2020-03-16 21:58:28 +09:00
Nick O'Leary
a8bc753720 [groups] Show group info when selected in sidebar 2020-03-16 11:16:18 +00:00
Nick O'Leary
266df86d98 [groups] Add menu options for group actions 2020-03-16 10:20:48 +00:00
Hiroyasu Nishiyama
c4ca0b6e91 fix tab apperance of subflow template panel 2020-03-15 08:02:26 +09:00
Nick O'Leary
1bf3b3077e [groups] Include groups when converting selection to subflow 2020-03-14 00:17:16 +00:00
Nick O'Leary
c9194c3635 [groups] Fix undo/redo handling of addTo/removeFrom group 2020-03-13 23:09:18 +00:00
Nick O'Leary
27c462fee9 [groups] Support dragging node from palette into group 2020-03-13 23:01:19 +00:00
Nick O'Leary
7886e5d57c [groups] Add undo support for group actions 2020-03-13 23:01:01 +00:00
Nick O'Leary
6912dec166 Merge pull request #2479 from node-red-hitachi/dev-admin-api-auth
Add admin api authentication function
2020-03-13 13:36:53 +00:00
Nick O'Leary
0ef3471f8f [groups] Add undo of group import 2020-03-13 11:27:13 +00:00
Nick O'Leary
fc3d0ab053 [groups] Keep groups ordered by depth in DOM 2020-03-09 15:10:54 +00:00
Nick O'Leary
de971fa53f Merge pull request #2487 from bonanitech/patch-1
Fix workspace CSS properties syntax
2020-03-09 11:17:14 +00:00
Nick O'Leary
d005eb46cf Merge pull request #2488 from bonanitech/patch-2
Consolidate duplicate selectors
2020-03-09 11:16:49 +00:00
Nick O'Leary
d1dd7d1d51 [groups] Support copy/paste/import/export of groups 2020-03-09 11:14:18 +00:00
Hiroyasu Nishiyama
134c68c98e merge origin 2020-03-07 19:22:12 +09:00
Hiroyasu Nishiyama
82539fc420 update for merging settings object & better error handling 2020-03-07 19:19:48 +09:00
Hiroyasu Nishiyama
7a5604697f Update packages/node_modules/node-red/red.js
Co-Authored-By: Nick O'Leary <nick.oleary@gmail.com>
2020-03-07 16:38:24 +09:00
Hiroyasu Nishiyama
84d2b8ad6d add support of initialization & finalization to function node 2020-03-07 01:55:45 +09:00
Nick O'Leary
9a0c843f29 [groups] Support deleting groups as part of selection 2020-03-05 22:49:31 +00:00
Nick O'Leary
4d96d95370 [groups] Add merge-selection-to-group and remove-selection-from-group 2020-03-05 15:52:26 +00:00
Nick O'Leary
51ea5dc342 [groups] Add ungroup-selection action 2020-03-05 10:43:28 +00:00
Nick O'Leary
97d58e34f2 [groups] Support nested groups in editor 2020-03-04 21:48:38 +00:00
Nick O'Leary
86ce5c591b [groups] add basic group functionality to editor 2020-03-03 20:55:01 +00:00
Kazuhito Yokoi
6675fdf3c2 Saving the node description property to the library 2020-03-02 05:50:32 +00:00
Mauricio Bonani
8a82552bdc Consolidate duplicates 2020-02-29 15:14:57 -05:00
Mauricio Bonani
bd4fc2e5cc Fix workspace CSS properties syntax 2020-02-29 09:15:42 -05:00
KAZUHIRO ITO
83942c2551 Fix plugin only receives the actual token 2020-02-27 19:55:21 +09:00
KAZUHIRO ITO
458d794f52 Fix tokensStrategy order 2020-02-27 19:41:59 +09:00
KAZUHIRO ITO
95982ad464 Update adminAuth tokensStrategy test spec 2020-02-27 19:20:08 +09:00
Ben Hardill
7723ff461b Remove console.logs 2020-02-26 19:46:54 +00:00
Ben Hardill
0ca36a89e3 Updates to match Nick's suggestions 2020-02-26 19:45:01 +00:00
KAZUHIRO ITO
bba6855872 Add admin api authentication function 2020-02-26 12:59:40 +09:00
Ben Hardill
37bcd5c603 First pass at adding support for GET requests
with a body
2020-02-25 21:28:15 +00:00
Nick O'Leary
c9ad5bea93 Merge branch 'dev' of github.com:node-red/node-red into dev 2020-02-25 15:09:26 +00:00
Nick O'Leary
a09b3bb6c7 Merge branch 'dev' into pr_2242 2020-02-25 14:32:33 +00:00
Dave Conway-Jones
8405826fab Ensure trigger sends complete 2nd msg if set to send latest msg
and add test
to close #2474
2020-02-24 21:17:54 +00:00
Nick O'Leary
54978e4d64 Merge pull request #2466 from node-red-hitachi/update-message-catalogue
Update message catalogue for SUBFLOW UI
2020-02-24 16:19:59 +00:00
Hiroyasu Nishiyama
2da1554caa update message catalogue for subflow UI 2020-02-18 21:38:32 +09:00
Hiroyasu Nishiyama
c0d007ffa9 add option support for overwriting settiings.js 2020-02-16 23:07:05 +09:00
Dave Conway-Jones
127b361979 change PR to only use a single property for the 2nd output 2020-02-14 20:13:37 -05:00
Nick O'Leary
4f9395e881 Merge pull request #2455 from node-red-hitachi/core-node-example
Add support of example flows for core node
2020-02-10 15:17:14 +00:00
Nick O'Leary
8035531a27 Merge pull request #2368 from node-red/subflow-cred-props
Add credential-type to subflow env properties
2020-02-10 11:33:42 +00:00
Nick O'Leary
cc177533e8 Dont export subflow template creds by default 2020-02-10 11:28:56 +00:00
Nick O'Leary
cd210d9fbf Add support for credential-stored env var in subflow 2020-02-10 11:28:56 +00:00
Nick O'Leary
87b9b56b65 Merge pull request #2367 from node-red/cred-typedInput
Add credential type to TypedInput
2020-02-10 11:17:10 +00:00
Nick O'Leary
bffcaa1c17 Refocus credential typedInput when hide/show button clicked 2020-02-10 11:16:19 +00:00
Nick O'Leary
33cbb2ada8 Fixup typedInput cred css 2020-02-10 11:09:49 +00:00
Nick O'Leary
d08e77cf36 Add credential type to TypedInput 2020-02-10 11:09:49 +00:00
Nick O'Leary
1f8ed9dcb9 Merge branch 'master' into dev 2020-02-10 11:07:50 +00:00
Dave Conway-Jones
b2f53a183e rename BreakingExit call
(undo Brexit :-)
2020-02-05 13:58:45 +00:00
Dave Conway-Jones
0622be843b Add catcher for PM2 graceful shutdown 2020-02-04 13:42:34 +00:00
Hiroyasu Nishiyama
272fbc0cb0 add examples of batch node 2020-02-04 09:45:23 +09:00
Hiroyasu Nishiyama
36bf2a3c38 add support for examples of core nodes 2020-02-03 12:59:12 +09:00
Dave Conway-Jones
fcf757f715 catch mode signals to allow clean context flush on shutdown
(yes the name is intentionally ironic)
Code pattern copied from https://nodejs.org/api/process.html#process_signal_events
2020-01-31 18:11:58 +00:00
Dave Conway-Jones
88e729664a complete tidy up of trigger node
remove unnecessary console.log
2020-01-31 17:56:06 +00:00
Dave Conway-Jones
87aacb4270 change property name to leave space if we want to also do main payload property 2020-01-30 22:20:55 +00:00
Dave Conway-Jones
3f756aac21 Allow trigger node to use other than msg.topic to separate streams
and add test
2020-01-30 21:39:34 +00:00
Dave Conway-Jones
d5d9ac5c76 let setMessageProperty return success flag
so calling node can warn if operation tries to overwrite primitive type
2020-01-26 18:20:25 +00:00
Dave Conway-Jones
bb12ec702a Add second output to trigger node
and add tests
2020-01-24 18:20:14 +00:00
Paul Wieland
82490b0a58 Implemented RED.util.setMessageProperty 2020-01-23 11:23:02 -05:00
Paul Wieland
2cbf625483 Removing form-row label. 2020-01-23 10:56:31 -05:00
Paul Wieland
44f2a986a2 Update messages.json
This label is used for the user definable properties form-row. The word properties is duplicated with the tab description, but it best descrbies the form-rows content. If another name is chosen, this entry can be deleted.
2020-01-23 08:56:55 -05:00
Paul Wieland
c3df1c6cde Add support for user definable properties to inject node 2020-01-23 08:55:50 -05:00
Nick O'Leary
6b52206186 Merge branch 'master' into dev 2020-01-22 11:54:24 +00:00
Nick O'Leary
468beee045 Merge pull request #2381 from node-red/keyboard-nav
Scroll the view with WASD/Cursor keys when nothing selected
2020-01-17 10:36:05 +00:00
Nick O'Leary
70ad66bcff Merge pull request #2425 from kazuhitoyokoi/dev-fixnodehtml
Change types from text/x-red to text/html in node html files
2020-01-17 10:35:28 +00:00
Kazuhito Yokoi
74a015c329 Change types from text/x-red to text/html in node html files 2020-01-15 11:40:48 +09:00
Vladimir Dronnikov
fe0d4f08f3 Allow to know particular session from status node
The rationale is to keep own list of active sessions.
As a workaround for https://discourse.nodered.org/t/tcp-connection-pool-better-separation/19432
TIA
2019-12-25 06:21:55 +03:00
Amo DelBello
14c2005bbc Merge pull request #1 from node-red/master
node-red/node-red changes
2019-12-09 14:54:53 -07:00
Amo DelBello
d017dd75cd Remove 'clone' from util 2019-11-29 20:15:56 -07:00
Amo DelBello
021df83c3f Replace 'clone' with 'lodash.clonedeep' 2019-11-29 18:50:30 -07:00
Nick O'Leary
d45274494d Merge branch 'master' into dev 2019-11-21 21:57:58 +00:00
Nick O'Leary
f478afb58a Merge pull request #2378 from node-red-hitachi/dev-node-installation
Add node installation from other than public site
2019-11-13 12:06:33 +00:00
Nick O'Leary
a54ca699b5 Scroll the view with WASD/Cursor keys when nothing selected 2019-11-13 10:06:25 +00:00
KAZUHIRO ITO
ff96773295 Add node installation from other than public site 2019-11-11 18:25:36 +09:00
Yuma Matsuura
7957ec4369 Modify id 2019-09-27 19:17:17 +09:00
Yuma Matsuura
17653761b9 Update a line break function 2019-09-03 18:43:46 +09:00
Yuma Matsuura
a42d7d867e Add a libe break function 2019-07-26 11:36:22 +09:00
523 changed files with 21796 additions and 6033 deletions

View File

@@ -32,3 +32,4 @@ To help us understand the issue, please fill-in as much of the following informa
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:
- [ ] running in Docker:

View File

@@ -0,0 +1,29 @@
const fs = require("fs");
const newVersion = require("../../package.json").version;
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
process.exit(0);
}
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
console.log(`Not updating for a non-stable release - ${newVersion}`);
process.exit(0);
}
const currentVersion = require("../../../node-red-docker/package.json").version;
console.log(`Update from ${currentVersion} to ${newVersion}`)
updateFile(__dirname+"/../../../node-red-docker/package.json", currentVersion, newVersion);
updateFile(__dirname+"/../../../node-red-docker/docker-custom/package.json", currentVersion, newVersion);
updateFile(__dirname+"/../../../node-red-docker/README.md", currentVersion, newVersion);
console.log(`::set-env name=newVersion::${newVersion}`);
function updateFile(path,from,to) {
let contents = fs.readFileSync(path,"utf8");
contents = contents.replace(new RegExp(from.replace(/\./g,"\\."),"g"), to);
fs.writeFileSync(path, contents);
}

View File

@@ -0,0 +1,18 @@
const fs = require("fs");
const newVersion = require("../../package.json").version;
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
process.exit(0);
}
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
console.log(`Not updating for a non-stable release - ${newVersion}`);
process.exit(0);
}
const path = __dirname+"/../../../node-red.github.io/index.html";
let contents = fs.readFileSync(path, "utf8");
contents = contents.replace(/<span class="node-red-latest-version">v\d+\.\d+\.\d+<\/span>/, `<span class="node-red-latest-version">v${newVersion}<\/span>` );
fs.writeFileSync(path, contents);

61
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: PublishDockerImage
on:
release:
types: [published]
jobs:
generate:
name: 'Update node-red-docker image'
runs-on: ubuntu-latest
steps:
- name: Check out node-red repository
uses: actions/checkout@v2
with:
path: 'node-red'
- name: Check out node-red-docker repository
uses: actions/checkout@v2
with:
repository: 'node-red/node-red-docker'
path: 'node-red-docker'
- name: Check out node-red.github.io repository
uses: actions/checkout@v2
with:
repository: 'node-red/node-red.github.io'
path: 'node-red.github.io'
- uses: actions/setup-node@v1
with:
node-version: '12'
- run: node ./node-red/.github/scripts/update-node-red-docker.js
with:
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
path: 'node-red-docker'
commit-message: 'Bump to ${{ env.newVersion }}'
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
body: |
Updates the Node-RED Docker repo for the ${{ env.newVersion }} release.
Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`.
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
- run: node ./node-red/.github/scripts/update-node-red-website.js
- name: Create Website Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
path: 'node-red.github.io'
commit-message: 'Bump to ${{ env.newVersion }}'
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
body: |
Updates the Node-RED Website repo for the ${{ env.newVersion }} release.
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary

2
.gitignore vendored
View File

@@ -22,3 +22,5 @@ packages/node_modules/@node-red/editor-client/public
!test/**/node_modules
docs
!packages/node_modules/**/docs
.vscode
.nyc_output

View File

@@ -1,11 +1,15 @@
sudo: false
addons:
chrome: stable
language: node_js
matrix:
include:
- node_js: "14"
script:
- ./node_modules/.bin/grunt && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
# - scripts/install-ui-test-dependencies.sh && grunt test-ui
before_script:
- npm install -g coveralls
- node_js: "12"
- node_js: "10"
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
- node_js: "8"

View File

@@ -1,3 +1,410 @@
### 1.2.5: Maintenance Release
Editor
- Fix import of config nodes with unknown z property
Runtime
- Set ACTIONS_ALLOW_UNSECURE_COMMANDS in GH Action
### 1.2.4: Maintenance Release
Editor
- Support bigint types in Debug sidebar
- Clear retained status of deleted nodes
- Prevent needless retention of node status messages
- Update projects dialogs to use TypedInput-cred input
- Restore cursor position in TypedInput cred-mode
- Ensure config nodes with invalid z are imported somewhere
- Ensure user keyboard shortcuts override defaults Fixes #2753
Runtime
- Disable projects when flowFile passed into grunt dev
- Add Russian Locale (#2761) (#2531) (@alexk111)
- Add Japanese translation for http-in node (#2758) (@kazuhitoyokoi)
Nodes
- CSV: Fix CSV node repeating array output
### 1.2.3: Maintenance Release
Editor
- Disable 'use strict' checking in Function node Fixes #2743
- Add gray/grey alternate options for status
- Handle import errors on initial load and report to user
- Only apply recovery tab on initial load Fixes #2731
- Reinstate coveralls reporting to travis build
- Update Japanese message catalogue for 1.2.3 release #2747 (@HiroyasuNishiyama)
Runtime
- Modify default settings comment (#2739)
- Add mutex lock to saveSettings storage call Fixes #2736 (#2737)
- Migrate to nyc instead of istanbul for code coverage
- Move mosca to ui-test-dependencies
- Remove " from npm install prefix option
### 1.2.2: Maintenance Release
Editor
- Prevent node z property getting set to 0 or ""
- Only apply z-recovery logic to flow nodes
- Fix api call to reload flows Fixes #2726
- Remove bad z property from import config nodes
### 1.2.1: Maintenance Release
Runtime
- Fix race condition in .config file migration Fixes #2724
### 1.2.0: Milestone Release
Editor
- Fix selection of link node not existing within active workspace #2722 (@HiroyasuNishiyama)
- Fix import of merged flow
- Fix width of upload button in Safari #2718 (@HiroyasuNishiyama)
- Update Chinese translations #2719 (@JiyeYu)
- Update Japanese translations needed for 1.2 #2710 (@kazuhitoyokoi)
- Fix unexpected line break of sidebar tab name popover #2716 (@HiroyasuNishiyama)
- i18n module refresh tooltip #2717 (@HiroyasuNishiyama)
- Add better error message if context file gets corrupted
- Update info text of function node #2714 (@HiroyasuNishiyama)
- Use markdown editor if editText called with md mode
- Prevent group actions when in non-default mouse mode
### 1.2.0-beta.1: Beta Release
Editor
- Detect importing duplicate nodes and help user resolve #2698
- Allow sidebar tabs to be reordered #2655
- Add tgz upload button to palette manager #2682
- Add 'automatic' git workflow for projects #2035
- Allow project version string to be edited
- Sanitize unknown node type when displaying
- Handle nodes with invalid z property Closes #2170
- Outline: Ensure sf instance nodes update in outliner when import-replace sf
- Outline: Ensure recovered nodes tab is added to outliner properly
- Groups: Only recalculate group label offsets when needed
- Groups: Reuse first group name/style when merging elements Fixes #2680
- Groups: Fix copy/paste of node into active group Fixes #2686
- ACE: Update ACE to 1.4.12-src-min-noconflict Fixes #1988
- ACE: Add comment highlighting to JSONata and fix regex handling Closes #2701
- ACE: Ensure errors in ACE NRJavaScript mode are on valid lines
- Prevent Enter on search box from reloading page Fixes #2678
- Allow toggleButton icons to be optional
- Allow treeList to have a header component
- Disable selection of FA icons when dbl clicking node
Runtime
- Add RED.hooks API for pluggable routing #2665
- Add flows:* events and deprecate nodes-* events
- Split .config.json into separate files #2794
- Add support for file upload in /nodes api #2682
- Add 'done' metric log for message tracing #2685 (@k-toumura)
- Add mutex locking around /flow apis #2679
- Default flowFilePretty to true if projects enabled
- Replace Math.random with crypto.getBytes for session tokens
- Fix `this` context when calling multiple event listeners Fixes #2692. #2693 (@mgroenhoff)
- Add --userDir=/tmp/foo support to grunt dev
- Skip loading node html if disableEditor set #2684
- Update util.writeFile to write to tmp file before rename #2683
- Fix getModuleFiles function to include path property #2705 (@t-kawamorita)
- Update nodemon to latest so grunt dev task behaves
- Improve jsdoc of util.getObjectProperty to clarify thrown error See #2703
Nodes
- Trigger: allow msg.delay to be used to set delay/loop interval #2707
- Function: allow to send & log in its initialize code #2644 (@cinhcet)
- MQTT: Update to MQTT 4.2.1 Closes #2694
- Debug: Handle undefined value in Debug view of Array and Object Fixes #2696
- Switch: Clarify empty rules in switch node documentation #2649 (@natcl) #2669 (@kazuhitoyokoi)
- Updated core nodes to use Done callback #2653 (@k-toumura)
- yaml, xml, json, html, http, template, range, link, status, catch, complete, inject
### 1.1.3: Maintenance Release
Editor
- Fix vertical align of fa node icons Fixes #2670
- Allow lasso selection to be restricted to active group
- Make ctrl-click on nested group more intuitive
- Fix copy/paste of nested groups
- Add Set(iterable) polyfill for IE11
- Support select-all inside active group
- Improve performance of moving groups
- Add additional check for git auth failure response Fixes #2656
- german translation, wording (#2660) (#2666)
- Remove filtering of duplicate fa icons
- Show node help when switching node edit dialogs Fixes #2652
- Ensure group theme picks up theme defaults properly Fixes #2651
Nodes
- Clarify Switch node isEmpty help
- HTTP In: handle application/cbor as binary
Runtime
- Move runtime settings back to adminApi from editorApi Fixes #2662
- Update Chinese message for debug node
### 1.1.2: Maintenance Release
Editor
- Fix all the touch screen issues Fixes #2647
- Add RED.view.redrawStatus to avoid full redraw on update
- Ensure node/group xrefs are consistent on import
- Disable keyboard handler when dialogs are open
- Ensure unknown nodes removed from outliner when node registers Fixes #2646
Runtime
- Allow Comms websocket auth to be done via token header Fixes #2642
### 1.1.1: Maintenance Release
Editor
- Set apiRootUrl for debug pop-out to load locales properly Fixes #2629, #2630
- Update build-custom-theme to handle keyframes properly Fixes #2636
- Remove hardcoded css and allow group to default from theme Fixes #2633
- Add RED.view.DEBUG_SYNC_REDRAW to disable requestAnimationFrame References #2631
- Fix up subflow port wiring
- Ensure groups are removed when deleting subflows
- Get group order right in history events to ensure proper handling
- Prevent wiring to node with no corresponding port Fixes #2641
- Avoid copying duplicate nodes to internal clipboard
- Fix connecting wires to subflow status or io ports on touchscreen Fixes #2637
Runtime
- Authenticate websocket comms using user-provided token if present Fixes #2642
Nodes
- Delay: add words about independence of messages being delayed.
- Debug: fix debug status to not loop, make migration more seamless, detect status type objects #2638
- Debug: Update Japanese message for debug node #2645 (@kazuhitoyokoi)
### 1.1.0: Milestone Release
Editor
- Align node labels on FF
- Fix node toggle button initial opacity
- Make color/icon/label-pos pickers keyboard navigable
- Default group label to be shown and improve toggle button
- Fix clearing group label
- Remove hardcoded css Fixes #2603
- Fix node button mouse pointer css
- Change node linebreak handling to use "\n "
- Handle import of node with non-default number of outputs
- Improve display of focussed form element
- Fix typedInput error on empty subflow input types #2624 (@HiroyasuNishiyama)
- Update JP message catalogue for subflow input type #2471 (@HiroyasuNishiyama)
- Outliner - add empty item when last config node moved
- Update zh-CN/zh-TW translations #2626 (@JiyeYu)
- Add default shortcut for `core:show-help-tab`
- Clear outline focus on config node sidebar panel
- Tweak group margin to fit node status and look better
- Fix reparenting nodes in outliner when they change
Runtime
- Add developer options - permits npm run build-dev #2628 (@Steve-Mcl)
Nodes
- Add example flows for lots of core nodes #2585 #2550 #2549 (@HiroyasuNishiyama)
- TCP: Fix tcp in node finishing packets when in streaming base64 receive mode.
- Join: Clear timeout when msg.reset received Fixes #2471
- Switch: JSONata expr does not require msg.parts.count
- Inject: fix backwards migration of inject without topic
#### 1.1.0-beta.3: Beta Release
Editor
- Fix wiring nodes from input back to output
- Fix sometimes unable to keyboard-move group to left/up
- Fix group position in outliner
- Handle unknown nodes with no icon
- Prevent node creep when switching tabs
#### 1.1.0-beta.2: Beta Release
Editor
- Add UI tests to travis build #2593 #2616 #2617 #2619 (@kazuhitoyokoi)
- Add Japanese translations for outliner, jsonata and runtime #2618 (@kazuhitoyokoi)
- Fix deleting node in group after changing selection
- Fixup padding of quick-add search box
- Move config nodes under type-level hierarchy in outline
- Emit nodes:change event for config node users list modified
- Increase group margin to avoid clash with status text
- Fix event order when quick-adding node to group
- Switch RED.events.DEBUG messages to warn to get stacktraces
- Fix empty item handling for subflows/config in outliner
- Fix search indexing of group nodes
- Avoid regenerating every node label on redraw
- Fix handling of multi-line node label
- Disable merge group menu for single item or non-group item #2611 (@HiroyasuNishiyama)
- Merge pull request #2609 from node-red-hitachi/fix-remove-from-group
- Fix position of empty group with multi-line label #2612 (@HiroyasuNishiyama)
- Make treelist of subflow/config nodes initially have empty placeholder
- Fix empty placeholder not shown on remove from group #2609 (@HiroyasuNishiyama)
- Prevent conversion of circular structure #2607 (@HiroyasuNishiyama)
- Handle null status text in the editor Fixes #2606
- Massively reduce our dependency on d3 to render the view
- EditableList/TreeList - defer adding elements to DOM
- Prevent RED.stop being called multiple times if >1 signal received
- Flag a node as removed when it is disabled
- Some performance improvements for TreeList
- Resize info/help sidebars whenever sidebar is opened
- Add search defaults to outliner searchBox
- Add search presets option to searchBox widget
- Add RED.popover.menu as a new type of menu widget
- Add support for is:XYZ search flags
- Track subflow instances on the subflow node itself
- Refresh outline filter whenever something changes Fixes #2601
- Fix Help tab search box appearance
- Rename Node Information to Information in sidebar
- Do a sync-redraw after clearing to ensure clean state
- Make catch/status/complete/link filter case-insensitive
- Add 'add' option to touch radialMenu for quick-add dialog
- Merge branch 'dev' of https://github.com/node-red/node-red into dev
- ensure trigger node detects changes to number of outputs
- Ignore whitespace when checking function setup/close code
- Preserve event handlers when moving outliner items
- Add tooltips to outliner buttons
- Only validate nodes once they have all been imported
- Ensure configNode.users is updated properly on import
Runtime
- Bump node-red-admin 0.2.6
Nodes
- WebSocket: Prevent charAt call on websocket listener #2610 ()
- Debug: fix status to migrate old nodes to correct default mode.
- Link: Fix Link node filter Fixes #2600
#### 1.1.0-beta.1: Beta Release
Runtime
- Allow HTTPS settings to be refreshed #2551 (@bartbutenaers)
- Add support for moment in JSONata expressions #2583 (@dxdc)
- Add httpAdminMiddleware for admin routes #2555
- Add admin api authentication function #2479 (@KazuhiroItoh)
- Add option support for overwriting settings.js #2463 (@HiroyasuNishiyama)
- Add support for credential-stored env var in subflow #2368
- Add node installation from other than public site #2378 (@KazuhiroItoh)
- Catch more signals to allow clean context flush on shutdown #2447
- Add `node-red admin` command #2592
- Move to `lodash.clonedeep` #2396 (@amodelbello)
- Tidy up unhandledRejection warning from context unit tests
- Add test cases for setMessageProperty with non-object properties
- Fix for settings.set subsequent updates #2584 (@sammachin)
- Turn off installer funding messages
- Remove unused \_info/\_type subflow env var magic values
- Add #! lines to project shell scripts #2548
- Add nodejs14 to Travis test matrix
- Remove duplicate NLS message #2516 (@alexk111)
- Let setMessageProperty return success flag #2439
Editor
- Add ability to group nodes #2493
- Add loading progress bar #2558
- Add Outliner to Info sidebar and add help sidebar #2556
- Add action to toggle node label visibility #2569
- Add show-examples-import-dialog action
- Add more consistent events in the editor #2543
- Save the node description property to the library #2490 (@kazuhitoyokoi)
- Add credential type to TypedInput #2367
- Scroll the view with WASD/Cursor keys when nothing selected #2381
- Bump jquery/migrate to latest versions
- Fix editor underscore visibility on Linux systems #2579 (@ristomatti)
- Support setting title on typedInput multi-option #2586 (@Steve-Mcl)
- Projects: Allow remote branch dialog to create non-default remote branches
- Ensure auth failure on project fetch identifies the remote #2545
- Make all dialogs handle smaller height screens better
- Add basic Array.from polyfill for IE11
- Add some more trap form elements to workaround Chrome autofill
- [info-sidebar] Handle node/group/flows with \n in their name
- [popover] Allow hover-type popovers to contain buttons
- Modify RED.panels to use flexbox position
- Allow node edit dialog to be opened on a non-default tab
- Add createNodeIcon and getDarkerColor to RED.utils
- [search] Refactor search to use editor events to generate index
- Allow RED.notify.popover to have a position offset
- Make selected list item more distinct
- Allow node button to be clicked via api call
- Reorder initial load so projects:load event emits before any nodes:add
- Add polyfills for IE11
- Activate project menu after initial clone #2547 (@HiroyasuNishiyama)
- Fix replacement of unknown node in workspace when module installed #2524 (@HiroyasuNishiyama)
- Fix appearance of subflow template panel #2506 (@HiroyasuNishiyama)
- Fix workspace CSS properties syntax #2487 (@bonanitech)
- Consolidate duplicate selectors #2488 (@bonanitech)
- Update message catalogue for subflow UI #2466 (@HiroyasuNishiyama)
Nodes
- Batch: Add reset feature to batch node #2553 (@HiroyasuNishiyama)
- Catch/Complete/Link/Status: #2588 Add compact searchBox to filter node lists
- Catch/Complete/Link/Status: Allow searchBox filter to filter on node type #2595 (@jeancarl)
- CSV: Add warn when unpaired quotes detected on input.
- CSV: allow node to only send headers once
- CSV: Allow CR and LF control chars to be a part of the value #2526 (@tmdoit)
- CSV: Add support for parsing empty strings and null values #2510 (@tmdoit)
- CSV: Update Japanese translations for CSV node #2562 (@kazuhitoyokoi)
- Debug: Add bulk-activate/deactive actions for debug node #2570 (@cinhcet)
- Debug: Show status independently of main output #2564
- Delay: Ensure delay node rate limit timer is cleared on reset
- Function: Make the function node top-level async
- Function: Add support of initialization & finalization to function node #2498 (@HiroyasuNishiyama)
- HTTP In: Remove nodejs deprecation warning #2540 (@vladimir-kazan)
- HTTP Request: Support sending body in GET requests #2478 (@hardillb)
- Inject: Adding user definable properties to inject node #2435 (@PaulWieland)
- TCP: Allow to know particular session from status node #2413 (@dvv)
- Trigger: Add optional second output
- Trigger: Ensure trigger sends complete 2nd msg if set to send latest msg
- Trigger: Allow trigger node to use other than msg.topic to separate streams
- XML: Moved XML options documentation property from Outputs to Inputs section #2572 (@jeancarl)
- Add some core node example flows #2455 (@HiroyasuNishiyama)
- Change types from text/x-red to text/html in node html files #2425 (@kazuhitoyokoi)
#### 1.0.6: Maintenance Release
Runtime
- Update to JSONata 1.8.3
- #2536 Handle clone of null in utils
Editor
- Prevent button label wrapping in typedInput
- Handle error objects when reporting in palette manager
Nodes
- Inject: Revert to cron 1.7.2
- UDP: when reusing input socket honour the broadcast mode.
#### 1.0.5: Maintenance Release
Runtime
@@ -102,7 +509,7 @@ Runtime
- #2332 Fix error handling of nodes with multiple input handlers
- Add script to generate npm publish script
- #2371 Ensure folder is present before write (e.g. flows file not in user folder)
- #2371 Handle windows UNC '\\' paths
- #2371 Handle windows UNC '\' paths
- #2366 Handle logging of non-JSON encodable objects
Editor

View File

@@ -20,10 +20,16 @@ var sass = require("node-sass");
module.exports = function(grunt) {
var nodemonArgs = ["-v"];
var nodemonArgs = ["-V"];
var flowFile = grunt.option('flowFile');
if (flowFile) {
nodemonArgs.push(flowFile);
process.env.NODE_RED_ENABLE_PROJECTS=false;
}
var userDir = grunt.option('userDir');
if (userDir) {
nodemonArgs.push("-u");
nodemonArgs.push(userDir);
}
var browserstack = grunt.option('browserstack');
@@ -47,8 +53,8 @@ module.exports = function(grunt) {
ui: 'bdd',
reporter: 'spec'
},
all: { src: ['test/**/*_spec.js'] },
core: { src: ["test/_spec.js","test/unit/**/*_spec.js"]},
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
},
webdriver: {
@@ -56,19 +62,19 @@ module.exports = function(grunt) {
configFile: 'test/editor/wdio.conf.js'
}
},
mocha_istanbul: {
nyc: {
options: {
globals: ['expect'],
timeout: 3000,
ignoreLeaks: false,
ui: 'bdd',
reportFormats: ['lcov','html'],
print: 'both',
istanbulOptions: ['--no-default-excludes', '-i','**/packages/node_modules/**']
cwd: '.',
include: ['packages/node_modules/**'],
excludeNodeModules: false,
exclude: ['packages/node_modules/@node-red/editor-client/**'],
reporter: ['lcov', 'html','text-summary'],
reportDir: 'coverage',
all: true
},
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
all: { cmd: false, args: ['grunt', 'simplemocha:all'] },
core: { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] },
nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] }
},
jshint: {
options: {
@@ -125,6 +131,7 @@ module.exports = function(grunt) {
src: [
// Ensure editor source files are concatenated in
// the right order
"packages/node_modules/@node-red/editor-client/src/js/polyfills.js",
"packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js",
"packages/node_modules/@node-red/editor-client/src/js/red.js",
"packages/node_modules/@node-red/editor-client/src/js/events.js",
@@ -151,6 +158,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/colorPicker.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
@@ -163,6 +171,8 @@ module.exports = function(grunt) {
"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",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
@@ -177,6 +187,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/group.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
@@ -189,8 +200,8 @@ module.exports = function(grunt) {
vendor: {
files: {
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.4.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.0.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"node_modules/marked/marked.min.js",
@@ -447,6 +458,7 @@ module.exports = function(grunt) {
'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/runtime/lib/hooks.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'
@@ -497,12 +509,10 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-chmod');
grunt.loadNpmTasks('grunt-jsonlint');
grunt.loadNpmTasks('grunt-mocha-istanbul');
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
grunt.loadNpmTasks('grunt-webdriver');
}
@@ -510,6 +520,26 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
grunt.loadNpmTasks('grunt-npm-command');
grunt.loadNpmTasks('grunt-mkdir');
grunt.loadNpmTasks('grunt-simple-nyc');
grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () {
const nodemon = require('nodemon');
this.async();
const options = this.options();
options.script = this.data.script;
let callback;
if (options.callback) {
callback = options.callback;
delete options.callback;
} else {
callback = function(nodemonApp) {
nodemonApp.on('log', function (event) {
console.log(event.colour);
});
};
}
callback(nodemon(options));
});
grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src;
@@ -591,11 +621,11 @@ module.exports = function(grunt) {
grunt.registerTask('default',
'Builds editor content then runs code style checks and unit tests on all components',
['build','verifyPackageDependencies','jshint:editor','mocha_istanbul:all']);
['build','verifyPackageDependencies','jshint:editor','nyc:all']);
grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code',
['build','mocha_istanbul:core']);
['build','nyc:core']);
grunt.registerTask('test-editor',
'Runs code style check on editor code',
@@ -613,12 +643,16 @@ module.exports = function(grunt) {
grunt.registerTask('test-nodes',
'Runs unit tests on core nodes',
['build','mocha_istanbul:nodes']);
['build','nyc:nodes']);
grunt.registerTask('build',
'Builds editor content',
['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']);
grunt.registerTask('build-dev',
'Developer mode: build dev version',
['clean:build','concat:build','concat:vendor','copy:build','sass:build','setDevEnv']);
grunt.registerTask('dev',
'Developer mode: run node-red, watch for source changes and build/restart',
['build','setDevEnv','concurrent:dev']);
@@ -634,7 +668,7 @@ module.exports = function(grunt) {
grunt.registerTask('coverage',
'Run Istanbul code test coverage task',
['build','mocha_istanbul:all']);
['build','nyc:all']);
grunt.registerTask('docs',
'Generates API documentation',

View File

@@ -1,4 +1,4 @@
Copyright JS Foundation and other contributors, http://js.foundation
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Apache License
Version 2.0, January 2004

View File

@@ -67,4 +67,4 @@ It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-t
## Copyright and license
Copyright JS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).
Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).

View File

@@ -1,13 +1,5 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.0 | :white_check_mark: |
| 0.20.x | :white_check_mark: |
## Reporting a Vulnerability
Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly.

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "1.0.5",
"version": "1.3.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -13,6 +13,8 @@
"start": "node packages/node_modules/node-red/red.js",
"test": "grunt",
"build": "grunt build",
"dev": "grunt dev",
"build-dev": "grunt build-dev",
"docs": "grunt docs"
},
"contributors": [
@@ -24,40 +26,44 @@
}
],
"dependencies": {
"ajv": "6.12.0",
"ajv": "6.12.6",
"async-mutex": "0.2.4",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"cheerio": "0.22.0",
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.4.0",
"cookie": "0.4.1",
"cookie-parser": "1.4.5",
"cors": "2.8.5",
"cron": "1.8.2",
"cron": "1.7.2",
"denque": "1.4.1",
"express": "4.17.1",
"express-session": "1.17.0",
"express-session": "1.17.1",
"fs-extra": "8.1.0",
"fs.notify": "0.0.4",
"hash-sum": "2.0.0",
"https-proxy-agent": "5.0.0",
"i18next": "15.1.2",
"iconv-lite": "0.5.1",
"iconv-lite": "0.6.2",
"is-utf8": "0.2.1",
"js-yaml": "3.13.1",
"js-yaml": "3.14.0",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.2",
"jsonata": "1.8.4",
"lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0",
"memorystore": "1.6.2",
"mime": "2.4.4",
"mqtt": "2.18.8",
"memorystore": "1.6.4",
"mime": "2.4.6",
"moment-timezone": "0.5.32",
"mqtt": "4.2.5",
"multer": "1.4.2",
"mustache": "4.0.1",
"node-red-node-rbe": "^0.2.6",
"node-red-admin": "^0.2.6",
"node-red-node-rbe": "^0.2.9",
"node-red-node-sentiment": "^0.1.6",
"node-red-node-tail": "^0.1.0",
"nopt": "4.0.3",
"nopt": "5.0.0",
"oauth2orize": "1.11.0",
"on-headers": "1.0.2",
"passport": "0.4.1",
@@ -66,7 +72,8 @@
"raw-body": "2.4.1",
"request": "2.88.0",
"semver": "6.3.0",
"uglify-js": "3.8.1",
"tar": "6.0.5",
"uglify-js": "3.11.6",
"when": "3.7.8",
"ws": "6.2.1",
"xml2js": "0.4.23"
@@ -75,40 +82,38 @@
"bcrypt": "3.0.8"
},
"devDependencies": {
"marked": "0.8.2",
"dompurify": "2.0.8",
"grunt": "~1.0.4",
"dompurify": "2.2.2",
"grunt": "1.3.0",
"grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.2",
"grunt-concurrent": "~2.3.1",
"grunt-concurrent": "3.0.0",
"grunt-contrib-clean": "~2.0.0",
"grunt-contrib-compress": "~1.5.0",
"grunt-contrib-compress": "1.6.0",
"grunt-contrib-concat": "~1.0.1",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-jshint": "~2.1.0",
"grunt-contrib-uglify": "~4.0.1",
"grunt-contrib-watch": "~1.1.0",
"grunt-jsdoc": "^2.2.1",
"grunt-jsdoc-to-markdown": "^4.0.0",
"grunt-jsonlint": "~2.0.0",
"grunt-jsdoc": "2.4.1",
"grunt-jsdoc-to-markdown": "5.0.0",
"grunt-jsonlint": "2.1.3",
"grunt-mkdir": "~1.0.0",
"grunt-mocha-istanbul": "5.0.2",
"grunt-nodemon": "~0.4.2",
"grunt-npm-command": "~0.1.2",
"grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1",
"http-proxy": "1.18.0",
"istanbul": "0.4.5",
"grunt-simple-nyc": "^3.0.1",
"http-proxy": "1.18.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "1.2.4",
"minami": "1.2.3",
"mocha": "^5.2.0",
"mosca": "^2.8.3",
"node-red-node-test-helper": "^0.2.3",
"node-sass": "^4.13.1",
"should": "^8.4.0",
"node-red-node-test-helper": "^0.2.5",
"node-sass": "^4.14.1",
"nodemon": "2.0.6",
"should": "13.2.3",
"sinon": "1.17.7",
"stoppable": "^1.1.0",
"supertest": "3.4.2"
"supertest": "5.0.0"
},
"engines": {
"node": ">=8"

View File

@@ -21,15 +21,17 @@ var flows = require("./flows");
var flow = require("./flow");
var context = require("./context");
var auth = require("../auth");
var info = require("./settings");
var apiUtil = require("../util");
module.exports = {
init: function(runtimeAPI) {
init: function(settings,runtimeAPI) {
flows.init(runtimeAPI);
flow.init(runtimeAPI);
nodes.init(runtimeAPI);
context.init(runtimeAPI);
info.init(settings,runtimeAPI);
var needsPermission = auth.needsPermission;
@@ -47,7 +49,14 @@ module.exports = {
// Nodes
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler);
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
if (!settings.editorTheme || !settings.editorTheme.palette || settings.editorTheme.palette.upload !== false) {
const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });
adminApp.post("/nodes",needsPermission("nodes.write"),upload.single("tarball"),nodes.post,apiUtil.errorHandler);
} else {
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
}
adminApp.get(/^\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler);
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler);
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler);
@@ -67,6 +76,8 @@ module.exports = {
// 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);
adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
return adminApp;
}
}

View File

@@ -44,8 +44,19 @@ module.exports = {
user: req.user,
module: req.body.module,
version: req.body.version,
url: req.body.url,
tarball: undefined,
req: apiUtils.getRequestLogObject(req)
}
if (!runtimeAPI.settings.editorTheme || !runtimeAPI.settings.editorTheme.palette || runtimeAPI.settings.editorTheme.palette.upload !== false) {
if (req.file) {
opts.tarball = {
name: req.file.originalname,
size: req.file.size,
buffer: req.file.buffer
}
}
}
runtimeAPI.nodes.addModule(opts).then(function(info) {
res.json(info);
}).catch(function(err) {

View File

@@ -0,0 +1,72 @@
/**
* 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.
**/
var apiUtils = require("../util");
var runtimeAPI;
var settings;
var theme = require("../editor/theme");
var clone = require("clone");
var i18n = require("@node-red/util").i18n
function extend(target, source) {
var keys = Object.keys(source);
var i = keys.length;
while(i--) {
var value = source[keys[i]]
var type = typeof value;
if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) {
target[keys[i]] = value;
} else if (value === null) {
if (target.hasOwnProperty(keys[i])) {
delete target[keys[i]];
}
} else {
// Object
if (target.hasOwnProperty(keys[i])) {
target[keys[i]] = extend(target[keys[i]],value);
} else {
target[keys[i]] = value;
}
}
}
return target;
}
module.exports = {
init: function(_settings,_runtimeAPI) {
runtimeAPI = _runtimeAPI;
settings = _settings;
},
runtimeSettings: function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
if (!settings.disableEditor) {
result.editorTheme = result.editorTheme||{};
var themeSettings = theme.settings();
if (themeSettings) {
// result.editorTheme may already exist with the palette
// disabled. Need to merge that into the receive settings
result.editorTheme = extend(clone(themeSettings),result.editorTheme);
}
result.editorTheme.languages = i18n.availableLanguages("editor");
}
res.json(result);
});
},
}

View File

@@ -36,6 +36,7 @@ var log = require("@node-red/util").log; // TODO: separate module
passport.use(strategies.bearerStrategy.BearerStrategy);
passport.use(strategies.clientPasswordStrategy.ClientPasswordStrategy);
passport.use(strategies.anonymousStrategy);
passport.use(strategies.tokensStrategy);
var server = oauth2orize.createServer();
@@ -60,7 +61,7 @@ function init(_settings,storage) {
function needsPermission(permission) {
return function(req,res,next) {
if (settings && settings.adminAuth) {
return passport.authenticate(['bearer','anon'],{ session: false })(req,res,function() {
return passport.authenticate(['bearer','tokens','anon'],{ session: false })(req,res,function() {
if (!req.user) {
return next();
}

View File

@@ -123,9 +123,57 @@ AnonymousStrategy.prototype.authenticate = function(req) {
});
}
function authenticateUserToken(req) {
return new Promise( (resolve,reject) => {
var token = null;
var tokenHeader = Users.tokenHeader();
if (Users.tokenHeader() === null) {
// No custom user token provided. Fail the request
reject();
return;
} else if (Users.tokenHeader() === 'authorization') {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
token = req.headers.authorization.split(' ')[1];
}
} else {
token = req.headers[Users.tokenHeader()];
}
if (token) {
Users.tokens(token).then(function(user) {
if (user) {
resolve(user);
} else {
reject();
}
});
} else {
reject();
}
});
}
function TokensStrategy() {
passport.Strategy.call(this);
this.name = 'tokens';
}
util.inherits(TokensStrategy, passport.Strategy);
TokensStrategy.prototype.authenticate = function(req) {
authenticateUserToken(req).then(user => {
this.success(user,{scope:user.permissions});
}).catch(err => {
this.fail(401);
});
}
module.exports = {
bearerStrategy: bearerStrategy,
clientPasswordStrategy: clientPasswordStrategy,
passwordTokenExchange: passwordTokenExchange,
anonymousStrategy: new AnonymousStrategy()
anonymousStrategy: new AnonymousStrategy(),
tokensStrategy: new TokensStrategy(),
authenticateUserToken: authenticateUserToken
}

View File

@@ -14,15 +14,7 @@
* limitations under the License.
**/
function generateToken(length) {
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
var token = [];
for (var i=0;i<length;i++) {
token.push(c[Math.floor(Math.random()*c.length)]);
}
return token.join("");
}
const crypto = require("crypto");
var storage;
var sessionExpiryTime
@@ -115,7 +107,7 @@ module.exports = {
},
create: function(user,client,scope) {
return loadSessions().then(function() {
var accessToken = generateToken(128);
var accessToken = crypto.randomBytes(128).toString('base64');
var accessTokenExpiresAt = Date.now() + (sessionExpiryTime*1000);

View File

@@ -59,7 +59,9 @@ function getDefaultUser() {
var api = {
get: get,
authenticate: authenticate,
default: getDefaultUser
default: getDefaultUser,
tokens: getDefaultUser,
tokenHeader: null
}
function init(config) {
@@ -105,6 +107,14 @@ function init(config) {
} else {
api.default = getDefaultUser;
}
if (config.tokens && typeof config.tokens === "function") {
api.tokens = config.tokens;
if (config.tokenHeader && typeof config.tokenHeader === "string") {
api.tokenHeader = config.tokenHeader.toLowerCase();
} else {
api.tokenHeader = "authorization";
}
}
}
function cleanUser(user) {
if (user && user.hasOwnProperty('password')) {
@@ -118,5 +128,7 @@ module.exports = {
init: init,
get: function(username) { return api.get(username).then(cleanUser)},
authenticate: function() { return api.authenticate.apply(null, arguments) },
default: function() { return api.default(); }
default: function() { return api.default(); },
tokens: function(token) { return api.tokens(token); },
tokenHeader: function() { return api.tokenHeader }
};

View File

@@ -16,11 +16,13 @@
var ws = require("ws");
var url = require("url");
const crypto = require("crypto");
var log = require("@node-red/util").log; // TODO: separate module
var Tokens;
var Users;
var Permissions;
var Strategies;
var server;
var settings;
@@ -31,8 +33,6 @@ var activeConnections = [];
var anonymousUser;
var retained = {};
var heartbeatTimer;
var lastSentTime;
@@ -44,6 +44,7 @@ function init(_server,_settings,_runtimeAPI) {
Tokens.onSessionExpiry(handleSessionExpiry);
Users = require("../auth/users");
Permissions = require("../auth/permissions");
Strategies = require("../auth/strategies");
}
function handleSessionExpiry(session) {
@@ -54,26 +55,19 @@ function handleSessionExpiry(session) {
}
})
}
function generateSession(length) {
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
var token = [];
for (var i=0;i<length;i++) {
token.push(c[Math.floor(Math.random()*c.length)]);
}
return token.join("");
}
function CommsConnection(ws) {
this.session = generateSession(32);
function CommsConnection(ws, user) {
this.session = crypto.randomBytes(32).toString('base64');
this.ws = ws;
this.stack = [];
this.user = null;
this.user = user;
this.lastSentTime = 0;
var self = this;
log.audit({event: "comms.open"});
log.trace("comms.open "+self.session);
var pendingAuth = (settings.adminAuth != null);
var preAuthed = !!user;
var pendingAuth = !this.user && (settings.adminAuth != null);
if (!pendingAuth) {
addActiveConnection(self);
@@ -130,8 +124,16 @@ function CommsConnection(ws) {
}
});
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,null,false);
Users.tokens(msg.auth).then(function(user) {
if (user) {
self.user = user;
log.audit({event: "comms.auth",user:self.user});
completeConnection(user.permissions,msg.auth,true);
} else {
log.audit({event: "comms.auth.fail"});
completeConnection(null,null,false);
}
});
}
});
} else {
@@ -191,8 +193,8 @@ function start() {
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('connection',function(ws, request, user) {
var commsConnection = new CommsConnection(ws, user);
});
wsServer.on('error', function(err) {
log.warn(log._("comms.error-server",{message:err.toString()}));
@@ -201,8 +203,26 @@ function start() {
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === commsPath) {
if (Users.tokenHeader() !== null && request.headers[Users.tokenHeader()]) {
// The user has provided custom token handling. For the websocket,
// the token could be provided in two ways:
// - as an http header (only possible with a reverse proxy setup)
// - passed over the connected websock in an auth packet
// If the header is present, verify the token. If not, use the auth
// packet over the connected socket
//
Strategies.authenticateUserToken(request).then(user => {
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request, user);
});
}).catch(err => {
log.audit({event: "comms.auth.fail"});
socket.destroy();
})
return
}
wsServer.handleUpgrade(request, socket, head, function done(ws) {
wsServer.emit('connection', ws, request);
wsServer.emit('connection', ws, request, null);
});
}
// Don't destroy the socket as other listeners may want to handle the

View File

@@ -103,7 +103,7 @@ module.exports = {
editorApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,apiUtil.errorHandler);
// Settings
editorApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
// Main /settings route is an admin route - see lib/admin/settings.js
// User Settings
editorApp.get("/settings/user",needsPermission("settings.read"),info.userSettings,apiUtil.errorHandler);
// User Settings

View File

@@ -137,6 +137,7 @@ module.exports = {
req.body.hasOwnProperty('description') ||
req.body.hasOwnProperty('dependencies')||
req.body.hasOwnProperty('summary') ||
req.body.hasOwnProperty('version') ||
req.body.hasOwnProperty('files') ||
req.body.hasOwnProperty('git')) {
runtimeAPI.projects.updateProject(opts).then(function() {

View File

@@ -16,56 +16,12 @@
var apiUtils = require("../util");
var runtimeAPI;
var sshkeys = require("./sshkeys");
var theme = require("./theme");
var clone = require("clone");
var i18n = require("@node-red/util").i18n
function extend(target, source) {
var keys = Object.keys(source);
var i = keys.length;
while(i--) {
var value = source[keys[i]]
var type = typeof value;
if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) {
target[keys[i]] = value;
} else if (value === null) {
if (target.hasOwnProperty(keys[i])) {
delete target[keys[i]];
}
} else {
// Object
if (target.hasOwnProperty(keys[i])) {
target[keys[i]] = extend(target[keys[i]],value);
} else {
target[keys[i]] = value;
}
}
}
return target;
}
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
sshkeys.init(runtimeAPI);
},
runtimeSettings: function(req,res) {
var opts = {
user: req.user
}
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
result.editorTheme = result.editorTheme||{};
var themeSettings = theme.settings();
if (themeSettings) {
// result.editorTheme may already exist with the palette
// disabled. Need to merge that into the receive settings
result.editorTheme = extend(clone(themeSettings),result.editorTheme);
}
result.editorTheme.languages = i18n.availableLanguages("editor");
res.json(result);
});
},
userSettings: function(req, res) {
var opts = {
user: req.user

View File

@@ -59,6 +59,12 @@ function init(settings,_server,storage,runtimeAPI) {
});
adminApp.use(corsHandler);
if (settings.httpAdminMiddleware) {
if (typeof settings.httpAdminMiddleware === "function") {
adminApp.use(settings.httpAdminMiddleware)
}
}
auth.init(settings,storage);
var maxApiRequestSize = settings.apiMaxLength || '5mb';
@@ -93,7 +99,7 @@ function init(settings,_server,storage,runtimeAPI) {
adminApp.use(corsHandler);
}
var adminApiApp = require("./admin").init(runtimeAPI);
var adminApiApp = require("./admin").init(settings, runtimeAPI);
adminApp.use(adminApiApp);
} else {
adminApp = null;

View File

@@ -43,10 +43,20 @@ module.exports = {
rejectHandler: function(req,res,err) {
//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({
if (!err.code) {
// by definition, an unexpected_error to log
log.error(err);
}
var response = {
code: err.code||"unexpected_error",
message: err.message||err.toString()
});
};
// Handle auth failures on a specific remote
// TODO: don't hardcode this here - allow users of rejectHandler to identify extra props to send
if (err.remote) {
response.remote = err.remote;
}
res.status(err.status||400).json(response);
},
getRequestLogObject: function(req) {
return {

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "1.0.5",
"version": "1.3.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,16 +16,17 @@
}
],
"dependencies": {
"@node-red/util": "1.0.5",
"@node-red/editor-client": "1.0.5",
"@node-red/util": "1.3.0-beta.1",
"@node-red/editor-client": "1.3.0-beta.1",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"clone": "2.1.2",
"cors": "2.8.5",
"express-session": "1.17.0",
"express-session": "1.17.1",
"express": "4.17.1",
"memorystore": "1.6.2",
"mime": "2.4.4",
"memorystore": "1.6.4",
"mime": "2.4.6",
"multer": "1.4.2",
"mustache": "4.0.1",
"oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1",

View File

@@ -14,7 +14,16 @@
"back": "Back",
"next": "Next",
"clone": "Clone project",
"cont": "Continue"
"cont": "Continue",
"style": "Style",
"line": "Outline",
"fill": "Fill",
"label": "Label",
"color": "Color",
"position": "Position",
"enable": "Enable",
"disable": "Disable",
"upload": "Upload"
},
"type": {
"string": "string",
@@ -28,6 +37,14 @@
"null": "null"
}
},
"event": {
"loadPalette": "Loading Palette",
"loadNodeCatalogs": "Loading Node catalogs",
"loadNodes": "Loading Nodes __count__",
"loadFlows": "Loading Flows",
"importFlows": "Adding Flows to workspace",
"importError": "<p>Error adding flows</p><p>__message__</p>"
},
"workspace": {
"defaultName": "Flow __number__",
"editFlow": "Edit flow: __name__",
@@ -91,7 +108,12 @@
"projects-new": "New",
"projects-open": "Open",
"projects-settings": "Project Settings",
"showNodeLabelDefault": "Show label of newly added nodes"
"showNodeLabelDefault": "Show label of newly added nodes",
"groups": "Groups",
"groupSelection": "Group selection",
"ungroupSelection": "Ungroup selection",
"groupMergeSelection": "Merge selection",
"groupRemoveSelection": "Remove from group"
}
},
"actions": {
@@ -171,10 +193,14 @@
"node_plural": "__count__ nodes",
"configNode": "__count__ configuration node",
"configNode_plural": "__count__ configuration nodes",
"group": "__count__ group",
"group_plural": "__count__ groups",
"flow": "__count__ flow",
"flow_plural": "__count__ flows",
"subflow": "__count__ subflow",
"subflow_plural": "__count__ subflows",
"replacedNodes": "__count__ node replaced",
"replacedNodes_plural": "__count__ nodes replaced",
"pasteNodes": "Paste flow json or",
"selectFile": "select a file to import",
"importNodes": "Import nodes",
@@ -182,11 +208,19 @@
"download": "Download",
"importUnrecognised": "Imported unrecognised type:",
"importUnrecognised_plural": "Imported unrecognised types:",
"importDuplicate": "Imported duplicate node:",
"importDuplicate_plural": "Imported duplicate nodes:",
"nodesExported": "Nodes exported to clipboard",
"nodesImported": "Imported:",
"nodeCopied": "__count__ node copied",
"nodeCopied_plural": "__count__ nodes copied",
"groupCopied": "__count__ group copied",
"groupCopied_plural": "__count__ groups copied",
"groupStyleCopied": "Group style copied",
"invalidFlow": "Invalid flow: __message__",
"recoveredNodes": "Recovered Nodes",
"recoveredNodesInfo": "The nodes on this flow were missing a valid flow id when they were imported. They have been added to this flow so you can either restore or delete them.",
"recoveredNodesNotification": "<p>Imported nodes without a valid flow id</p><p>They have been added to a new flow called '__flowName__'.</p>",
"export": {
"selected":"selected nodes",
"current":"current flow",
@@ -201,13 +235,19 @@
},
"import": {
"import": "Import to",
"importSelected": "Import selected",
"importCopy": "Import copy",
"viewNodes": "View nodes...",
"newFlow": "new flow",
"replace": "replace",
"errors": {
"notArray": "Input not a JSON Array",
"itemNotObject": "Input not a valid flow - item __index__ not a node object",
"missingId": "Input not a valid flow - item __index__ missing 'id' property",
"missingType": "Input not a valid flow - item __index__ missing 'type' property"
}
},
"conflictNotification1": "Some of the nodes you are importing already exist in your workspace.",
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them."
},
"copyMessagePath": "Path copied",
"copyMessageValue": "Value copied",
@@ -308,6 +348,13 @@
"multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection"
}
},
"group": {
"editGroup": "Edit group: __name__",
"errors": {
"cannotCreateDiffGroups": "Cannot create group using nodes from different groups",
"cannotAddSubflowPorts": "Cannot add subflow ports to a group"
}
},
"editor": {
"configEdit": "Edit",
"configAdd": "Add",
@@ -337,6 +384,7 @@
"locale": "Select UI Language",
"icon": "Icon",
"inputType": "Input type",
"selectType": "select types...",
"inputs" : {
"input": "input",
"select": "select",
@@ -351,7 +399,8 @@
"bool": "bool",
"json": "JSON",
"bin": "buffer",
"env": "env variable"
"env": "env variable",
"cred": "credential"
},
"menu": {
"input": "input",
@@ -498,6 +547,8 @@
"sortAZ": "a-z",
"sortRecent": "recent",
"more": "+ __count__ more",
"upload": "Upload module tgz file",
"refresh": "Refresh module list",
"errors": {
"catalogLoadFailed": "<p>Failed to load node catalogue.</p><p>Check the browser console for more information</p>",
"installFailed": "<p>Failed to install: __module__</p><p>__message__</p><p>Check the log for more information</p>",
@@ -533,11 +584,12 @@
},
"sidebar": {
"info": {
"name": "Node information",
"name": "Information",
"tabName": "Name",
"label": "info",
"node": "Node",
"type": "Type",
"group": "Group",
"module": "Module",
"id": "ID",
"status": "Status",
@@ -560,7 +612,29 @@
"nodeHelp": "Node Help",
"none":"None",
"arrayItems": "__count__ items",
"showTips":"You can open the tips from the settings panel"
"showTips":"You can open the tips from the settings panel",
"outline": "Outline",
"empty": "empty",
"globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action",
"find": "Find in workspace",
"search": {
"configNodes": "Configuration nodes",
"unusedConfigNodes": "Unused configuration nodes",
"invalidNodes": "Invalid nodes",
"uknownNodes": "Unknown nodes",
"unusedSubflows": "Unused subflows"
}
},
"help": {
"name": "Help",
"label": "help",
"search": "Search help",
"nodeHelp": "Node Help",
"showHelp": "Show help",
"showInOutline": "Show in outline",
"showTopics": "Show topics",
"noHelp": "No help topic selected"
},
"config": {
"name": "Configuration nodes",
@@ -613,7 +687,6 @@
"removeFromProject": "remove from project",
"addToProject": "add to project",
"files": "Files",
"package": "Package",
"flow": "Flow",
"credentials": "Credentials",
"package":"Package",
@@ -652,6 +725,12 @@
"committerTip": "Leave blank to use system default",
"userName": "Username",
"email": "Email",
"workflow": "Workflow",
"workfowTip": "Choose your preferred git workflow",
"workflowManual": "Manual",
"workflowManualTip": "All changes must be manually committed under the 'history' sidebar",
"workflowAuto": "Automatic",
"workflowAutoTip": "Changes are committed automatically with every deploy",
"sshKeys": "SSH Keys",
"sshKeysTip": "Allows you to create secure connections to remote git repositories.",
"add": "add key",
@@ -757,7 +836,8 @@
"bin": "buffer",
"date": "timestamp",
"jsonata": "expression",
"env": "env variable"
"env": "env variable",
"cred": "credential"
}
},
"editableList": {
@@ -804,9 +884,9 @@
"expandItems": "Expand items",
"collapseItems": "Collapse items",
"duplicate": "Duplicate",
"error": {
"invalidJSON": "Invalid JSON: "
}
"error": {
"invalidJSON": "Invalid JSON: "
}
},
"markdownEditor": {
"title": "Markdown editor",
@@ -978,7 +1058,7 @@
"retry": "Retry",
"update-failed": "Failed to update auth",
"unhandled": "Unhandled error response",
"host-key-verify-failed": "<p>Host key verification failed.</p><p>The repository host key could not be verified. Please update your <code>known_hosts</code> file and try again."
"host-key-verify-failed": "<p>Host key verification failed.</p><p>The repository host key could not be verified. Please update your <code>known_hosts</code> file and try again.</p>"
},
"create-branch-list": {
"invalid": "Invalid branch",
@@ -1009,6 +1089,7 @@
"en-US": "English",
"ja": "Japanese",
"ko": "Korean",
"ru": "Russian",
"zh-CN": "Chinese(Simplified)",
"zh-TW": "Chinese(Traditional)"
}

View File

@@ -266,5 +266,9 @@
"$type": {
"args": "value",
"desc": "Returns the type of `value` as a string. If `value` is undefined, this will return `undefined`"
},
"$moment": {
"args": "[str]",
"desc": "Gets a date object using the Moment library."
}
}

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

@@ -14,7 +14,16 @@
"back": "戻る",
"next": "進む",
"clone": "プロジェクトをクローン",
"cont": "続ける"
"cont": "続ける",
"style": "形式",
"line": "線",
"fill": "塗りつぶし",
"label": "ラベル",
"color": "色",
"position": "配置",
"enable": "有効",
"disable": "無効",
"upload": "アップロード"
},
"type": {
"string": "文字列",
@@ -28,6 +37,14 @@
"null": "null"
}
},
"event": {
"loadPalette": "パレットを読み込み中",
"loadNodeCatalogs": "ノードカタログを読み込み中",
"loadNodes": "ノードを読み込み中 __count__",
"loadFlows": "フローを読み込み中",
"importFlows": "ワークスペースにフローを追加中",
"importError": "<p>フロー追加エラー</p><p>__message__</p>"
},
"workspace": {
"defaultName": "フロー __number__",
"editFlow": "フローを編集: __name__",
@@ -67,7 +84,7 @@
"settings": "設定",
"userSettings": "ユーザ設定",
"nodes": "ノード",
"displayStatus": "ノードの状態を表示",
"displayStatus": "ノードのステータスを表示",
"displayConfig": "ノードの設定",
"import": "読み込み",
"export": "書き出し",
@@ -91,7 +108,12 @@
"projects-new": "新規",
"projects-open": "開く",
"projects-settings": "設定",
"showNodeLabelDefault": "追加したノードのラベルを表示"
"showNodeLabelDefault": "追加したノードのラベルを表示",
"groups": "グループ",
"groupSelection": "選択部分をグループ化",
"ungroupSelection": "選択部分をグループ解除",
"groupMergeSelection": "選択部分をマージ",
"groupRemoveSelection": "グループから削除"
}
},
"actions": {
@@ -171,22 +193,34 @@
"node_plural": "__count__ 個のノード",
"configNode": "__count__ 個の設定ノード",
"configNode_plural": "__count__ 個の設定ノード",
"group": "__count__ 個のグループ",
"group_plural": "__count__ 個のグループ",
"flow": "__count__ 個のフロー",
"flow_plural": "__count__ 個のフロー",
"subflow": "__count__ 個のサブフロー",
"subflow_plural": "__count__ 個のサブフロー",
"pasteNodes": "JSON形式のフローデータを貼り付けてください",
"selectFile": "読み込むファイルを選択してください",
"importNodes": "フローをクリップボートから読み込み",
"replacedNodes": "置換された __count__ 個のノード",
"replacedNodes_plural": "置換された __count__ 個のノード",
"pasteNodes": "JSON形式のフローデータを貼り付け",
"selectFile": "読み込むファイルを選択",
"importNodes": "フローをクリップボードから読み込み",
"exportNodes": "フローをクリップボードへ書き出し",
"download": "ダウンロード",
"importUnrecognised": "認識できない型が読み込まれました:",
"importUnrecognised_plural": "認識できない型が読み込まれました:",
"importDuplicate": "重複したノードを読み込みました:",
"importDuplicate_plural": "重複したノードを読み込みました:",
"nodesExported": "クリップボードへフローを書き出しました",
"nodesImported": "読み込みました:",
"nodeCopied": "__count__ 個のノードをコピーしました",
"nodeCopied_plural": "__count__ 個のノードをコピーしました",
"groupCopied": "__count__ 個のグループをコピーしました",
"groupCopied_plural": "__count__ 個のグループをコピーしました",
"groupStyleCopied": "グループの形式をコピーしました",
"invalidFlow": "不正なフロー: __message__",
"recoveredNodes": "復旧したノード",
"recoveredNodesInfo": "このフロー内のードは読み込み時に、有効なフローIDがありませんでした。これらフローIDは、フローに追加されているため、復元または削除できます。",
"recoveredNodesNotification": "<p>有効なフローIDを持たないードが読み込まれました</p><p>これらノードは '__flowName__' という新しいフローへ追加されました。</p>",
"export": {
"selected": "選択したフロー",
"current": "現在のタブ",
@@ -201,13 +235,19 @@
},
"import": {
"import": "読み込み先",
"importSelected": "選択したノードを読み込み",
"importCopy": "コピーを読み込み",
"viewNodes": "ノードを参照...",
"newFlow": "新規のタブ",
"replace": "置換",
"errors": {
"notArray": "JSON形式の配列ではありません",
"itemNotObject": "不正なフロー - __index__ 番目の要素はノードオブジェクトではありません",
"missingId": "不正なフロー - __index__ 番目の要素に'id'プロパティがありません",
"missingType": "不正なフロー - __index__ 番目の要素に'type'プロパティがありません"
}
},
"conflictNotification1": "読み込もうとしているノードのいくつかは、既にワークスペース内に存在しています。",
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。"
},
"copyMessagePath": "パスをコピーしました",
"copyMessageValue": "値をコピーしました",
@@ -308,6 +348,13 @@
"multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています"
}
},
"group": {
"editGroup": "__name__ グループを編集",
"errors": {
"cannotCreateDiffGroups": "異なるグループのノードを使用してグループを作成することはできません",
"cannotAddSubflowPorts": "グループにサブフローの端子を追加できません"
}
},
"editor": {
"configEdit": "編集",
"configAdd": "追加",
@@ -337,6 +384,7 @@
"locale": "UI言語の選択",
"icon": "記号",
"inputType": "入力形式",
"selectType": "形式選択...",
"inputs": {
"input": "入力",
"select": "メニュー",
@@ -351,7 +399,8 @@
"bool": "真偽",
"json": "JSON",
"bin": "バッファ",
"env": "環境変数"
"env": "環境変数",
"cred": "認証情報"
},
"menu": {
"input": "入力",
@@ -498,6 +547,8 @@
"sortAZ": "辞書順",
"sortRecent": "日付順",
"more": "+ さらに __count__ 個",
"upload": "モジュールのtgzファイルをアップロード",
"refresh": "モジュールリスト更新",
"errors": {
"catalogLoadFailed": "<p>ノードのカタログの読み込みに失敗しました。</p><p>詳細はブラウザのコンソールを確認してください。</p>",
"installFailed": "<p>追加処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
@@ -538,6 +589,7 @@
"label": "情報",
"node": "ノード",
"type": "型",
"group": "グループ",
"module": "モジュール",
"id": "ID",
"status": "状態",
@@ -560,7 +612,29 @@
"nodeHelp": "ノードのヘルプ",
"none": "なし",
"arrayItems": "__count__ 要素",
"showTips": "設定からヒントを表示できます"
"showTips": "設定からヒントを表示できます",
"outline": "アウトライン",
"empty": "空",
"globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索",
"search": {
"configNodes": "設定ノード",
"unusedConfigNodes": "未使用の設定ノード",
"invalidNodes": "不正なノード",
"uknownNodes": "未知のノード",
"unusedSubflows": "未使用のサブフロー"
}
},
"help": {
"name": "ヘルプ",
"label": "ヘルプ",
"search": "ヘルプを検索",
"nodeHelp": "ノードヘルプ",
"showHelp": "ヘルプを表示",
"showInOutline": "アウトラインに表示",
"showTopics": "トピックを表示",
"noHelp": "ヘルプのトピックが未選択"
},
"config": {
"name": "ノードの設定を表示",
@@ -613,9 +687,9 @@
"removeFromProject": "プロジェクトから削除",
"addToProject": "プロジェクトへ追加",
"files": "ファイル",
"package": "パッケージ",
"flow": "フロー",
"credentials": "認証情報",
"package": "パッケージ",
"packageCreate": "変更が保存された時にファイルが作成されます",
"fileNotExist": "ファイルが存在しません",
"selectFile": "ファイルを選択",
@@ -651,6 +725,12 @@
"committerTip": "システムのデフォルトを使用する場合、空白のままにしてください",
"userName": "ユーザ名",
"email": "メールアドレス",
"workflow": "ワークフロー",
"workfowTip": "望ましいgitワークフローを選択してください",
"workflowManual": "手動",
"workflowManualTip": "全ての変更は「履歴」サイドバー内で手動でコミットする必要があります",
"workflowAuto": "自動",
"workflowAutoTip": "変更はデプロイの度に自動的にコミットされます",
"sshKeys": "SSH キー",
"sshKeysTip": "gitリポジトリへのセキュアな接続を作成できます。",
"add": "キーを追加",
@@ -756,7 +836,8 @@
"bin": "バッファ",
"date": "日時",
"jsonata": "JSONata式",
"env": "環境変数"
"env": "環境変数",
"cred": "認証情報"
}
},
"editableList": {
@@ -976,7 +1057,8 @@
"passphrase": "パスフレーズ",
"retry": "リトライ",
"update-failed": "認証の更新に失敗しました",
"unhandled": "エラー応答が処理されませんでした"
"unhandled": "エラー応答が処理されませんでした",
"host-key-verify-failed": "<p>ホストキーの検証に失敗</p><p>リポジトリのホストキーを検証できませんでした。<code>known_hosts</code>ファイルを更新して、もう一度試してください。</p>"
},
"create-branch-list": {
"invalid": "不正なブランチ",

View File

@@ -266,5 +266,9 @@
"$type": {
"args": "value",
"desc": "`value` の型を文字列として返します。もし `value` が未定義の場合、 `undefined` が返されます。"
},
"$moment": {
"args": "[str]",
"desc": "Momentライブラリを使用して日付オブジェクトを取得します。"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"info": {
"tip0" : "Вы можете удалить выбранные узлы или провода с {{core:delete-selection}}",
"tip1" : "Ищите узлы с {{core:search}}",
"tip2" : "{{core:toggle-sidebar}} показывает/скрывает эту боковою панель",
"tip3" : "Вы можете управлять палитрой узлов с помощью {{core:manage-palette}}",
"tip4" : "Узлы конфигурации потока перечисляются на боковой панели. Доступ к списку можно получить из меню или с помощью {{core:show-config-tab}}",
"tip5" : "Эти советы можно включить/выключить через настройки",
"tip6" : "Перемещайте выбранные узлы клавишами [влево] [вверх] [вниз] и [вправо]. Удерживайте [Shift], чтобы увеличить шаг",
"tip7" : "Перетаскивание узла на провод соединит его с обеих сторон",
"tip8" : "Экспортируйте выбранные узлы или текущую вкладку с {{core:show-export-dialog}}",
"tip9" : "Импортируйте поток, перетаскивая его JSON в редактор или с помощью {{core:show-import-dialog}}",
"tip10" : "Нажмите [Shift], [кликните] по порту узла и перетаскивайте подключенные провода на другой узел",
"tip11" : "Открывайте вкладку Информация с {{core:show-info-tab}} или вкладку Отладка с {{core:show-debug-tab}}",
"tip12" : "Нажмите [ctrl] и [кликните] в рабочей области, чтобы открыть диалог быстрого добавления",
"tip13" : "Нажмите [ctrl] и [кликните] по порту узла, чтобы начать быстрое подключение",
"tip14" : "Нажмите [Shift] и [кликните] по узлу, чтобы выбрать все соединенные узлы",
"tip15" : "Нажмите [ctrl] и [кликните] по узлу, чтобы добавить или убрать его из текущего выбора",
"tip16" : "Переключайте вкладки потока с помощью {{core:show-previous-tab}} и {{core:show-next-tab}}",
"tip17" : "Вы можете подтвердить изменения в редакторе узла с {{core:confirm-edit-tray}} или отменить их с {{core:cancel-edit-tray}}",
"tip18" : "Нажатие {{core:edit-selected-node}} откроет редактор первого узла в текущем выборе"
}
}

View File

@@ -0,0 +1,274 @@
{
"$string": {
"args": "arg[, prettify]",
"desc": "Преобразует параметр `arg` в строку, используя следующие правила приведения:\n\n - Строки возвращаются как есть\n - Функции преобразуются в пустую строку\n - Числовая бесконечность и NaN выдают ошибку, поскольку они не могут быть представлены числом в JSON\n - Все остальные значения преобразуются в строку JSON с помощью функции `JSON.stringify`. Если значение `prettify` равно true, тогда будет сгенерирован \"отформатированный\" JSON. То есть каждое поле будет в отдельной строке, а строки будут иметь отступ в зависимости от глубины поля."
},
"$length": {
"args": "str",
"desc": "Возвращает количество символов в строке `str`. Выдается ошибка, если `str` не является строкой."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Возвращает строку, содержащую символы из первого параметра `str`, начиная с позиции `start` (отсчет с нуля). Если указан `length`, то подстрока будет содержать максимум `length` символов. Если `start` отрицателен, то это означает количество символов с конца `str`."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Возвращает подстроку перед первым вхождением последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Возвращает подстроку после первого вхождения последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
},
"$uppercase": {
"args": "str",
"desc": "Возвращает строку со всеми символами `str`, преобразованными в верхний регистр."
},
"$lowercase": {
"args": "str",
"desc": "Возвращает строку со всеми символами `str`, преобразованными в нижний регистр."
},
"$trim": {
"args": "str",
"desc": "Нормализует и обрезает все пробельные символы в строке `str`, выполняя следующие шаги:\n\n - Все символы табуляции, возврата каретки и перевода строки заменяются пробелами.\n- Последовательности пробелов сокращаются до одного пробела.\n- Пробелы в начале и конце `str` удаляются.\n\n Если `str` не указан (то есть эта функция вызывается без аргументов), тогда значение контекста используется в качестве значения `str`. Выдается ошибка, если `str` не является строкой."
},
"$contains": {
"args": "str, pattern",
"desc": "Возвращает `true`, если строка `str` соответствует шаблону `pattern`, в противном случае возвращает `false`. Если `str` не указан (то есть эта функция вызывается с одним аргументом), то значение контекста используется как значение `str`. Параметр `pattern` может быть либо строкой, либо регулярным выражением."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Разбивает строку `str` на массив подстрок. Выдает ошибку, если `str` не является строкой. Необязательный параметр `separator` (строка или регулярное выражение) указывает символы внутри строки `str`, относительно которых она должна быть разделена. Если `separator` не указан, то предполагается пустая строка, и `str` будет разбит на массив из отдельных символов. Выдает ошибку, если `separator` не является строкой. Необязательный параметр `limit` - это число, указывающее максимальное количество подстрок для включения в результирующий массив. Любые дополнительные подстроки отбрасываются. Если `limit` не указан, то весь `str` разделяется без ограничения размера результирующего массива. Выдает ошибку, если `limit` не является положительным числом."
},
"$join": {
"args": "array[, separator]",
"desc": "Объединяет массив подстрок в одну объединенную строку, в которой каждая подстрока отделена необязательным параметром `separator`. Выдает ошибку, если входной массив содержит элемент, который не является строкой. Если `separator` не указан, то предполагается, что это пустая строка, то есть нет `separator` между подстроками. Выдает ошибку, если `separator` не является строкой."
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "Применяет строку `str` к регулярному выражению `pattern` и возвращает массив объектов, каждый из которых содержит информацию о каждом совпадении внутри `str`."
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "Находит вхождения шаблона `pattern` в строке `str` и заменяет их на строку `replacement`.\n\nНеобязательный параметр `limit` - это максимальное количество замен."
},
"$now": {
"args":"",
"desc":"Создает отметку времени в формате, совместимом с ISO 8601, и возвращает ее как строку."
},
"$base64encode": {
"args":"string",
"desc":"Преобразует ASCII-строку в base-64 кодировку. Каждый символ в строке обрабатывается как байт двоичных данных. Для этого необходимо, чтобы все символы в строке находились в диапазоне от 0x00 до 0xFF, который включает все символы строк в URI-кодировке. Символы Юникода за пределами этого диапазона не поддерживаются."
},
"$base64decode": {
"args":"string",
"desc":"Преобразует байты в кодировке base-64 в строку, используя кодовую страницу Юникод UTF-8."
},
"$number": {
"args": "arg",
"desc": "Преобразует параметр `arg` в число с использованием следующих правил приведения:\n\n - Числа возвращаются как есть\n - Строки, которые содержат последовательность символов, представляющих допустимое в JSON число, преобразуются в это число\n - Все остальные значения вызывают ошибку."
},
"$abs": {
"args":"number",
"desc":"Возвращает абсолютное значение числа `number`."
},
"$floor": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое меньше или равно `number`."
},
"$ceil": {
"args":"number",
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое больше или равно `number`."
},
"$round": {
"args":"number [, precision]",
"desc":"Возвращает значение числа `number`, округленное до количества десятичных знаков, указанных необязательным параметром `precision`."
},
"$power": {
"args":"base, exponent",
"desc":"Возвращает значение числа `base`, возведенное в степень `exponent`."
},
"$sqrt": {
"args":"number",
"desc":"Возвращает квадратный корень из значения числа `number`."
},
"$random": {
"args":"",
"desc":"Возвращает псевдослучайное число, которе больше или равно нулю и меньше единицы."
},
"$millis": {
"args":"",
"desc":"Возвращает число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу) в виде числа. Все вызовы `$millis()` в пределах выполнения выражения будут возвращать одно и то же значение."
},
"$sum": {
"args": "array",
"desc": "Возвращает арифметическую сумму массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$max": {
"args": "array",
"desc": "Возвращает максимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$min": {
"args": "array",
"desc": "Возвращает минимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$average": {
"args": "array",
"desc": "Возвращает среднее значение массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
},
"$boolean": {
"args": "arg",
"desc": "Приводит аргумент к логическому значению, используя следующие правила: \n\n - Логические значения возвращаются как есть\n - пустая строка: `false`\n - непустая строка: `true`\n - число равное `0`: `false`\n - ненулевое число: `true`\n - `null` : `false`\n - пустой массив: `false`\n - массив, который содержит хотя бы один элемент, приводимый к `true`: `true`\n - массив, все элементы которого приводятся к `false`: `false`\n - пустой объект: `false`\n - непустой объект: `true`\n - функция: `false`"
},
"$not": {
"args": "arg",
"desc": "Возвращает логическое НЕ для аргумента. `arg` сначала приводится к логическому значению"
},
"$exists": {
"args": "arg",
"desc": "Возвращает логическое `true`, если выполнение выражения `arg` возвращает значение, или `false`, если выражение ничему не соответствует (например, путь к несуществующему полю)."
},
"$count": {
"args": "array",
"desc": "Возвращает количество элементов в массиве"
},
"$append": {
"args": "array, array",
"desc": "Присоединяет один массив к другому"
},
"$sort": {
"args":"array [, function]",
"desc":"Возвращает массив, содержащий все значения параметра `array`, но отсортированные по порядку.\n\nЕсли указан компаратор `function`, то это должна быть функция, которая принимает два параметра:\n\n`function(val1, val2)`\n\nЭту функцию вызывает алгоритм сортировки для сравнения двух значений: val1 и val2. Если значение val1 следует поместить после значения val2 в желаемом порядке сортировки, то функция должна возвращать логическое значение `true`, чтобы обозначить замену. В противном случае она должна вернуть `false`."
},
"$reverse": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но в обратном порядке."
},
"$shuffle": {
"args":"array",
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но перемешанный в случайном порядке."
},
"$zip": {
"args":"array, ...",
"desc":"Возвращает свернутый (сжатый) массив, содержащий сгруппированные массивы значений из аргументов `array1` … `arrayN` по индексам 0, 1, 2...."
},
"$keys": {
"args": "object",
"desc": "Возвращает массив, содержащий ключи объекта. Если аргумент является массивом объектов, то возвращаемый массив содержит недублированный список всех ключей из всех объектов."
},
"$lookup": {
"args": "object, key",
"desc": "Возвращает значение, связанное с ключом в объекте. Если первый аргумент является массивом объектов, то просходит поиск по всем объектам в массиве, и возвращаются значения, связанные со всеми вхождениями ключа."
},
"$spread": {
"args": "object",
"desc": "Разбивает объект, содержащий пары ключ / значение, на массив объектов, каждый из которых имеет одну пару ключ / значение из входного объекта. Если параметр является массивом объектов, то результирующий массив содержит объект для каждой пары ключ / значение из каждого объекта предоставленного массива."
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "Объединяет массив объектов в один объект, содержащий все пары ключ / значение каждого из объектов входного массива. Если какой-либо из входных объектов содержит один и тот же ключ, возвращаемый объект будет содержать значение последнего в массиве. Вызывает ошибку, если входной массив содержит элемент, который не является объектом."
},
"$sift": {
"args":"object, function",
"desc":"Возвращает объект, который содержит только пары ключ / значение из параметра `object`, которые удовлетворяют предикату `function`, переданному в качестве второго параметра.\n\n`function`, которая передается в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, key [, object]])`"
},
"$each": {
"args":"object, function",
"desc":"Возвращает массив, который содержит значения, возвращаемые функцией `function` при применении к каждой паре ключ/значение из объекта `object`."
},
"$map": {
"args":"array, function",
"desc":"Возвращает массив, содержащий результаты применения функции `function` к каждому значению массива `array`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$filter": {
"args":"array, function",
"desc":"Возвращает массив, содержащий только те значения из массива `array`, которые удовлетворяют предикату `function`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
},
"$reduce": {
"args":"array, function [, init]",
"desc":"Возвращает агрегированное значение, полученное в результате последовательного применения функции `function` к каждому значению в массиве в сочетании с результатом от предыдущего применения функции.\n\nФункция должна принимать два аргумента и вести себя как инфиксный оператор между каждым значением в массиве `array`. Сигнатура `function` должна иметь форму: `myfunc($accumulator, $value[, $index[, $array]])`\n\nНеобязательный параметр `init` используется в качестве начального значения в агрегации."
},
"$flowContext": {
"args": "string[, string]",
"desc": "Извлекает свойство контекста потока.\n\nЭто функция от Node-RED."
},
"$globalContext": {
"args": "string[, string]",
"desc": "Извлекает свойство глобального контекста.\n\nЭто функция от Node-RED."
},
"$pad": {
"args": "string, width [, char]",
"desc": "Возвращает копию строки `string` с дополнительным заполнением, если необходимо, чтобы общее количество символов как минимум соответствовало абсолютному значению параметра `width`.\n\nЕсли `width` является положительным числом, то строка дополняется справа; если отрицательным, то дополняется слева.\n\nНеобязательный аргумент `char` указывает символ(ы) для заполнения. Если не указано, по умолчанию используется пробел."
},
"$fromMillis": {
"args": "number",
"desc": "Преобразует число, представляющее миллисекунды с начала Unix-эпохи (1 января 1970 года по Гринвичу), в строку отметки времени в формате ISO 8601."
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "Преобразует число `number` в строку и форматирует ее в десятичное представление, как указано в строке `picture`.\n\nПоведение этой функции соответствует XPath/XQuery-функции fn:format-number, как определено в спецификация XPath F&O 3.1. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и fn:format-number.\n\nНеобязательный третий аргумент `options` используется для переопределения символов форматирования, специфичных для локали по умолчанию, таких как десятичный разделитель. Если аргумент указан, то это должен быть объект, содержащий пары имя/значение, указанные в разделе десятичного формата спецификации XPath F&O 3.1."
},
"$formatBase": {
"args": "number [, radix]",
"desc": "Преобразует число `number` в строку и форматирует ее в целое число, представленное в системе счисления, указанной аргументом `radix`. Если `radix` не указан, то по умолчанию используется десятичная. Значение 'radix` может быть от 2 до 36, в противном случае выдается ошибка."
},
"$toMillis": {
"args": "timestamp",
"desc": "Преобразует строку `timestamp` в формате ISO 8601 в число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу). Вызывает ошибку, если строка в неправильном формате."
},
"$env": {
"args": "arg",
"desc": "Возвращает значение переменной среды.\n\nЭто функция от Node-RED."
},
"$eval": {
"args": "expr [, context]",
"desc": "Анализирует и исполняет строку `expr`, которая содержит JSON или выражение JSONata, используя текущий контекст в качестве контекста для исполнения."
},
"$formatInteger": {
"args": "number, picture",
"desc": "Преобразует число `number` в строку и форматирует ее в целочисленное представление, как указано в строке `picture`. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и `fn:format-integer` из спецификации XPath F&O 3.1."
},
"$parseInteger": {
"args": "string, picture",
"desc": "Разбирает содержимое строки `string` в целое число (как число JSON), используя формат, указанный в строке `picture`. Строковый параметр `picture` имеет тот же формат, что и `$formatInteger`."
},
"$error": {
"args": "[str]",
"desc": "Вызывает ошибку с сообщением. Необязательная строка `str` заменяет сообщение по умолчанию $error() function evaluated`"
},
"$assert": {
"args": "arg, str",
"desc": "Если значение `arg` равно true, функция возвращает значение undefined. Если значение `arg` равно false, генерируется исключение с `str` в качестве сообщения об исключении."
},
"$single": {
"args": "array, function",
"desc": "Возвращает одно-единственное значение из массива `array`, которое удовлетворяет предикату `function` (то есть когда `function` возвращает логическое `true` при передаче значения). Выдает исключение, если число подходящих значений не одно.\n\nФункция должна соответствовать следующей сигнатуре: `function(value [, index [, array]])` где value - элемент массива, index - позиция этого значения, а весь массив передается в качестве третьего аргумента"
},
"$encodeUrl": {
"args": "str",
"desc": "Кодирует компонент Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrlComponent": {
"args": "str",
"desc": "Кодирует Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
},
"$decodeUrl": {
"args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrlComponent.\n\nПример: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
},
"$decodeUrlComponent": {
"args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
},
"$distinct": {
"args": "array",
"desc": "Возвращает массив содержащий все элементы из массива `array`, с удаленными дупликатами"
},
"$type": {
"args": "value",
"desc": "Возвращает тип значения `value` в виде строки. Если `value` не определено, то будет возвращено `undefined`"
},
"$moment": {
"args": "[str]",
"desc": "Получает date объект, используя библиотеку Moment."
}
}

View File

@@ -14,7 +14,16 @@
"back": "后退",
"next": "下一个",
"clone": "克隆项目",
"cont": "继续"
"cont": "继续",
"style": "风格",
"line": "大纲",
"fill": "填充",
"label": "标签",
"color": "颜色",
"position": "位置",
"enable": "启用",
"disable": "禁用",
"upload": "上传"
},
"type": {
"string": "字符串",
@@ -28,11 +37,18 @@
"null": "空"
}
},
"event": {
"loadPalette": "加载控制板",
"loadNodeCatalogs": "加载节点目录",
"loadNodes": "加载 __count__ 个节点",
"loadFlows": "加载流程",
"importFlows": "往工作区中加载流程"
},
"workspace": {
"defaultName": "流程__number__",
"defaultName": "流程 __number__",
"editFlow": "编辑流程: __name__",
"confirmDelete": "确认删除",
"delete": "你确定删除 '__label__'?",
"delete": "你确定删除 __label__ ?",
"dropFlowHere": "把流程放到这里",
"addFlow": "添加流程",
"listFlows": "流程一览",
@@ -91,7 +107,12 @@
"projects-new": "新建",
"projects-open": "打开",
"projects-settings": "项目设定",
"showNodeLabelDefault": "显示新添加的节点的标签"
"showNodeLabelDefault": "显示新添加的节点的标签",
"groups": "组",
"groupSelection": "选择组",
"ungroupSelection": "取消选择组",
"groupMergeSelection": "合并选择",
"groupRemoveSelection": "从组中移除"
}
},
"actions": {
@@ -101,7 +122,7 @@
"zoom-in": "放大"
},
"user": {
"loggedInAs": "作为__name__登陆",
"loggedInAs": "作为 __name__ 登陆",
"username": "账号",
"password": "密码",
"login": "登陆",
@@ -127,13 +148,13 @@
"missing_flow_file": "<p>找不到项目流程文件。</p><p>该项目未配置流程文件。</p>",
"missing_package_file": "<p>找不到项目包文件。</p><p>项目缺少package.json文件。</p>",
"project_empty": "<p>该项目为空。</p><p>是否要创建一组默认的项目文件?<br/>否则,您将必须在编辑器外部手动将文件添加到项目中。</p>",
"project_not_found": "<p>未找到项目'__project__'。</p>",
"project_not_found": "<p>未找到项目 __project__ 。</p>",
"git_merge_conflict": "<p>自动合并更改失败。</p><p>修复未合并的冲突,然后提交结果。</p>"
},
"error": "<strong>错误</strong>: __message__",
"errors": {
"lostConnection": "丢失与服务器的连接,重新连接...",
"lostConnectionReconnect": "丢失与服务器的连接__time__秒后重新连接",
"lostConnectionReconnect": "丢失与服务器的连接, __time__ 秒后重新连接",
"lostConnectionTry": "现在尝试",
"cannotAddSubflowToItself": "无法向其自身添加子流程",
"cannotAddCircularReference": "无法添加子流程 - 循环引用",
@@ -167,14 +188,18 @@
"clipboard": {
"clipboard": "剪贴板",
"nodes": "节点",
"node": "__count__节点",
"node_plural": "__count__节点",
"configNode": "__count__配置节点",
"configNode_plural": "__count__配置节点",
"flow": "__count__流程",
"flow_plural": "__count__流程",
"subflow": "__count__流程",
"subflow_plural": "__count__流程",
"node": "__count__节点",
"node_plural": "__count__节点",
"configNode": "__count__配置节点",
"configNode_plural": "__count__配置节点",
"group": "__count__ 个组",
"group_plural": "__count__ 个组",
"flow": "__count__流程",
"flow_plural": "__count__流程",
"subflow": "__count__ 个子流程",
"subflow_plural": "__count__ 子流程",
"replacedNodes": "__count__ 个节点被置换",
"replacedNodes_plural": "__count__ 个节点被置换",
"pasteNodes": "在这里粘贴节点",
"selectFile": "选择要导入的文件",
"importNodes": "导入节点",
@@ -184,9 +209,15 @@
"importUnrecognised_plural": "导入了无法识别的类型:",
"nodesExported": "节点导出到了剪贴板",
"nodesImported": "导入:",
"nodeCopied": "已复制__count__个节点",
"nodeCopied_plural": "已复制__count__个节点",
"nodeCopied": "已复制 __count__ 个节点",
"nodeCopied_plural": "已复制 __count__ 个节点",
"groupCopied": "复制 __count__ 个组",
"groupCopied_plural": "已复制 __count__ 个groups",
"groupStyleCopied": "已复制组风格",
"invalidFlow": "无效的流程: __message__",
"recoveredNodes": "复原的节点",
"recoveredNodesInfo": "导入节点时此流上的节点缺少有效的流ID。 它们已被添加到此流中,您可以复原或删除它们。",
"recoveredNodesNotification": "<p>导入的节点缺少有效的流ID</p><p>已将它们添加到名为 '__flowName__'的新流中。</p>",
"export": {
"selected": "已选择的节点",
"current": "现在的节点",
@@ -201,13 +232,19 @@
},
"import": {
"import": "导入到",
"importSelected": "导入所选项",
"importCopy": "导入副本",
"viewNodes": "查看节点",
"newFlow": "新流程",
"replace": "置换",
"errors": {
"notArray": "输入的不是JSON数组",
"itemNotObject": "输入的流无效 - 项目__index__不是节点对象",
"itemNotObject": "输入的流无效 - 项目 __index__ 不是节点对象",
"missingId": "输入的流无效-项 __index__ 缺少'id'属性",
"missingType": "输入的流程无效-项__index__缺少'类型'属性"
}
"missingType": "输入的流程无效-项 __index__ 缺少'类型'属性"
},
"conflictNotification1": "您要导入的某些节点已经存在于工作空间中。",
"conflictNotification2": "选择要导入的节点,并确认要替换现有的节点还是导入它们的副本"
},
"copyMessagePath": "已复制路径",
"copyMessageValue": "已复制数值",
@@ -250,7 +287,7 @@
"conflictChecking": "检查是否可以自动合并更改",
"conflictAutoMerge": "此更改不包括冲突,可以自动合并",
"conflictManualMerge": "这些更改包括了在部署之前必须解决的冲突。",
"plusNMore": "+ __count__更多"
"plusNMore": "+ __count__ 更多"
}
},
"eventLog": {
@@ -287,11 +324,11 @@
"newVersionError": "新版本不包含有效的JSON"
},
"subflow": {
"editSubflowInstance": "编辑子流实例__name__",
"editSubflow": "编辑流程模板: __name__",
"editSubflowInstance": "编辑子流实例: __name__",
"editSubflow": "编辑流程模板 __name__",
"edit": "编辑流程模板",
"subflowInstances": "这个子流程模板有__count__个实例",
"subflowInstances_plural": "这个子流程模板有__count__个实例",
"subflowInstances": "这个子流程模板有 __count__ 个实例",
"subflowInstances_plural": "这个子流程模板有 __count__ 个实例",
"editSubflowProperties": "编辑属性",
"input": "输入:",
"output": "输出:",
@@ -308,17 +345,24 @@
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
}
},
"group": {
"editGroup": "编辑组: __name__",
"errors": {
"cannotCreateDiffGroups": "无法使用来自不同组的节点创建组",
"cannotAddSubflowPorts": "无法将子流程的端口添加到组"
}
},
"editor": {
"configEdit": "编辑",
"configAdd": "添加",
"configUpdate": "更新",
"configDelete": "删除",
"nodesUse": "__count__个节点使用此配置",
"nodesUse_plural": "__count__个节点使用此配置",
"addNewConfig": "添加新的__type__配置",
"editNode": "编辑__type__节点",
"editConfig": "编辑__type__配置",
"addNewType": "添加新的__type__节点",
"nodesUse": "__count__ 个节点使用此配置",
"nodesUse_plural": "__count__ 个节点使用此配置",
"addNewConfig": "添加新的 __type__ 配置",
"editNode": "编辑 __type__ 节点",
"editConfig": "编辑 __type__ 配置",
"addNewType": "添加新的 __type__ 节点",
"nodeProperties": "节点属性",
"label": "标签",
"color": "颜色",
@@ -337,6 +381,7 @@
"locale": "选择界面语言",
"icon": "图标",
"inputType": "输入类型",
"selectType": "选择类型...",
"inputs": {
"input": "输入",
"select": "选择",
@@ -351,7 +396,8 @@
"bool": "布尔",
"json": "JSON",
"bin": "buffer",
"env": "环境变量"
"env": "环境变量",
"cred": "证书"
},
"menu": {
"input": "输入",
@@ -381,7 +427,7 @@
"scope": "范围",
"unassigned": "未分配",
"global": "全局",
"workspace": "工作",
"workspace": "工作",
"selectAll": "选择所有节点",
"selectAllConnected": "选择所有连接的节点",
"addRemoveNode": "从选择中添加/删除节点",
@@ -460,33 +506,33 @@
"times": {
"seconds": "秒前",
"minutes": "分前",
"minutesV": "__count__分前",
"hoursV": "__count__小时前",
"hoursV_plural": "__count__小时前",
"daysV": "__count__天前",
"daysV_plural": "__count__天前",
"weeksV": "__count__周前",
"weeksV_plural": "__count__周前",
"monthsV": "__count__月前",
"monthsV_plural": "__count__月前",
"yearsV": "__count__年前",
"yearsV_plural": "__count__年前",
"yearMonthsV": "__y__年, __count__月前",
"yearMonthsV_plural": "__y__年, __count__月前",
"yearsMonthsV": "__y__年, __count__月前",
"yearsMonthsV_plural": "__y__年, __count__月前"
"minutesV": "__count__ 分前",
"hoursV": "__count__ 小时前",
"hoursV_plural": "__count__ 小时前",
"daysV": "__count__ 天前",
"daysV_plural": "__count__ 天前",
"weeksV": "__count__ 周前",
"weeksV_plural": "__count__ 周前",
"monthsV": "__count__ 月前",
"monthsV_plural": "__count__ 月前",
"yearsV": "__count__ 年前",
"yearsV_plural": "__count__ 年前",
"yearMonthsV": "__y__ 年, __count__ 月前",
"yearMonthsV_plural": "__y__ 年, __count__ 月前",
"yearsMonthsV": "__y__ 年, __count__ 月前",
"yearsMonthsV_plural": "__y__ 年, __count__ 月前"
},
"nodeCount": "__label__个节点",
"nodeCount_plural": "__label__个节点",
"moduleCount": "__count__个可用模块",
"moduleCount_plural": "__count__个可用模块",
"nodeCount": "__label__ 个节点",
"nodeCount_plural": "__label__ 个节点",
"moduleCount": "__count__ 个可用模块",
"moduleCount_plural": "__count__ 个可用模块",
"inuse": "使用中",
"enableall": "全部启用",
"disableall": "全部禁用",
"enable": "启用",
"disable": "禁用",
"remove": "移除",
"update": "更新至__version__版本",
"update": "更新至 __version__ 版本",
"updated": "已更新",
"install": "安装",
"installed": "已安装",
@@ -498,7 +544,8 @@
"sort": "排序:",
"sortAZ": "a-z顺序",
"sortRecent": "日期顺序",
"more": "增加__count__个",
"more": "增加 __count__ 个",
"upload": "上传模块tgz文件",
"errors": {
"catalogLoadFailed": "无法加载节点目录。<br>查看浏览器控制台了解更多信息",
"installFailed": "无法安装: __module__<br>__message__<br>查看日志了解更多信息",
@@ -539,6 +586,7 @@
"label": "信息",
"node": "节点",
"type": "类型",
"group": "组",
"module": "模组",
"id": "ID",
"status": "状态",
@@ -560,8 +608,30 @@
"subflowDesc": "子流程描述",
"nodeHelp": "节点帮助",
"none": "无",
"arrayItems": "__count__个项目",
"showTips": "您可以从设置面板启用提示信息"
"arrayItems": "__count__ 个项目",
"showTips": "您可以从设置面板启用提示信息",
"outline": "大纲",
"empty": "空的",
"globalConfig": "全局配置节点",
"triggerAction": "触发动作",
"find": "在工作区中查找",
"search": {
"configNodes": "配置节点",
"unusedConfigNodes": "未使用的配置节点",
"invalidNodes": "无效的节点",
"uknownNodes": "未知的节点",
"unusedSubflows": "未使用的子流程"
}
},
"help": {
"name": "帮助",
"label": "帮助",
"search": "搜索帮助",
"nodeHelp": "节点帮助",
"showHelp": "显示帮助",
"showInOutline": "在大纲中显示",
"showTopics": "显示主题",
"noHelp": "未选择帮助主题"
},
"config": {
"name": "配置节点",
@@ -614,9 +684,9 @@
"removeFromProject": "从项目中删除",
"addToProject": "添加到项目",
"files": "文件",
"package": "包",
"flow": "流程",
"credentials": "证书",
"package": "包",
"packageCreate": "保存更改后将创建文件",
"fileNotExist": "文件不存在",
"selectFile": "选择文件",
@@ -628,7 +698,7 @@
"changeTheEncryptionKey": "更改加密密钥",
"currentKey": "当前密钥",
"newKey": "新密钥",
"credentialsAlert": "这将删除所有现有证",
"credentialsAlert": "这将删除所有现有证",
"versionControl": "版本控制",
"branches": "分支",
"noBranches": "没有分支",
@@ -652,6 +722,12 @@
"committerTip": "保留空白以使用系统默认值",
"userName": "用户名",
"email": "电子邮件",
"workflow": "工作流",
"workfowTip": "选择您偏好的工作流",
"workflowManual": "手动",
"workflowManualTip": "所有更改都必须在“历史记录”侧边栏中手动提交",
"workflowAuto": "自动",
"workflowAutoTip": "每次部署后都会自动提交更改",
"sshKeys": "SSH密钥",
"sshKeysTip": "允许您创建到远程git存储库的安全连接。",
"add": "添加密钥",
@@ -668,7 +744,7 @@
"copyPublicKey": "将公钥复制到剪贴板",
"delete": "删除密钥",
"gitConfig": "Git配置",
"deleteConfirm": "您确定要删除SSH密钥__name__吗这不能被撤消。"
"deleteConfirm": "您确定要删除SSH密钥 __name__ 吗?这不能被撤消。"
},
"versionControl": {
"unstagedChanges": "未暂存的变更",
@@ -722,24 +798,24 @@
"pullChanges": "拉取更改",
"history": "历史",
"projectHistory": "项目历史",
"daysAgo": "__count__天前",
"daysAgo_plural": "__count__天前",
"hoursAgo": "__count__小时前",
"hoursAgo_plural": "__count__小时前",
"minsAgo": "__count__分钟前",
"minsAgo_plural": "__count__分钟前",
"daysAgo": "__count__ 天前",
"daysAgo_plural": "__count__ 天前",
"hoursAgo": "__count__ 小时前",
"hoursAgo_plural": "__count__ 小时前",
"minsAgo": "__count__ 分钟前",
"minsAgo_plural": "__count__ 分钟前",
"secondsAgo": "秒前",
"notTracking": "您的本地分支当前未跟踪一个远程分支。",
"statusUnmergedChanged": "您的仓库中有未合并的更改。您需要解决冲突并提交结果。",
"repositoryUpToDate": "您的仓库是最新的。",
"commitsAhead": "您的存储库领先远程仓库__count__次提交。您现在可以推送这些提交。",
"commitsAhead_plural": "您的存储库领先远程仓库__count__次提交。您现在可以推送这些提交。",
"commitsBehind": "您的存储库落后远程仓库__count__次提交。您现在可以拉取这些提交。",
"commitsBehind_plural": "您的存储库落后远程仓库__count__次提交。您现在可以拉取这些提交。",
"commitsAheadAndBehind1": "您的存储库落后远程仓库__count__次提交",
"commitsAheadAndBehind1_plural": "您的存储库落后远程仓库__count__次提交",
"commitsAheadAndBehind2": "领先远程仓库__count__次提交。",
"commitsAheadAndBehind2_plural": "领先远程仓库__count__次提交。",
"commitsAhead": "您的存储库领先远程仓库 __count__ 次提交。您现在可以推送这些提交。",
"commitsAhead_plural": "您的存储库领先远程仓库 __count__ 次提交。您现在可以推送这些提交。",
"commitsBehind": "您的存储库落后远程仓库 __count__ 次提交。您现在可以拉取这些提交。",
"commitsBehind_plural": "您的存储库落后远程仓库 __count__ 次提交。您现在可以拉取这些提交。",
"commitsAheadAndBehind1": "您的存储库落后远程仓库 __count__ 次提交",
"commitsAheadAndBehind1_plural": "您的存储库落后远程仓库 __count__ 次提交",
"commitsAheadAndBehind2": "领先远程仓库 __count__ 次提交。",
"commitsAheadAndBehind2_plural": "领先远程仓库 __count__ 次提交。",
"commitsAheadAndBehind3": "您必须先拉取远程提交,然后才能进行推送。",
"commitsAheadAndBehind3_plural": "您必须先拉取远程提交,然后才能进行推送。",
"refreshCommitHistory": "刷新提交历史",
@@ -757,7 +833,8 @@
"bin": "二进制流",
"date": "时间戳",
"jsonata": "表达式",
"env": "环境变量"
"env": "环境变量",
"cred": "证书"
}
},
"editableList": {
@@ -977,7 +1054,8 @@
"passphrase": "密码短语",
"retry": "重试",
"update-failed": "无法更新身份验证",
"unhandled": "未处理的错误响应"
"unhandled": "未处理的错误响应",
"host-key-verify-failed": "<p>主机密钥验证失败。</p><p>无法验证存储库主机密钥。请更新您的<code>known_hosts</code>文件,然后重试。</p>"
},
"create-branch-list": {
"invalid": "无效的分支",

View File

@@ -1,15 +1,15 @@
{
"$string": {
"args": "arg",
"desc": "通过以下的类型转换规则将参数*arg*转换成字符串:\n\n - 字符串不转换。\n -函数转换成空的字符串。\n - JSON的值无法用数字表示所以用无限大或者NaN非数表示。\n - 用JSON.stringify函数将其他值转换成JSON字符串。"
"desc": "通过以下的类型转换规则将参数 *arg* 转换成字符串:\n\n - 字符串不转换。\n -函数转换成空的字符串。\n - JSON的值无法用数字表示所以用无限大或者NaN非数表示。\n - 用 `JSON.stringify` 函数将其他值转换成JSON字符串。"
},
"$length": {
"args": "str",
"desc": "输出字符串str的字数。如果str不是字符串,抛出错误。"
"desc": "输出字符串 `str` 的字数。如果 `str` 不是字符串,抛出错误。"
},
"$substring": {
"args": "str, start[, length]",
"desc": "输出`start`位置后的的首次出现的包括`str`的子字符串。 如果`length`被指定,那么的字符串中将只包括前`length`个文字。如果`start`是负数则输出从`str`末尾开始的`length`个文字"
"desc": "输出 `start` 位置后的的首次出现的包括 `str` 的子字符串。 如果 `length` 被指定,那么的字符串中将只包括前 `length` 个文字。如果 `start` 是负数则输出从 `str` 末尾开始的 `length` 个文字"
},
"$substringBefore": {
"args": "str, chars",
@@ -17,11 +17,11 @@
},
"$substringAfter": {
"args": "str, chars",
"desc": "输出str中首次出现的chars之后的子字符串,如果str中不包括chars则输出str。"
"desc": "输出 `str` 中首次出现的 `chars` 之后的子字符串,如果 `str` 中不包括 `chars` 则输出 `str` 。"
},
"$uppercase": {
"args": "str",
"desc": "`将’str中的所有字母变为大写后输出。"
"desc": "将 `str` 中的所有字母变为大写后输出。"
},
"$lowercase": {
"args": "str",
@@ -29,27 +29,27 @@
},
"$trim": {
"args": "str",
"desc": "将以下步骤应用于`str`来去除所有空白文字并实现标准化。\n\n 将全部tab制表符、回车键、换行字符用空白代替。\n- 将连续的空白文字变成一个空白文字。\n- 消除开头和末尾的空白文字。\n\n如果`str`没有被指定(即在无输入参数的情况下调用本函数),将上下文的值作为`str`来使用。 如果`str` 不是字符串则抛出错误。"
"desc": "将以下步骤应用于 `str` 来去除所有空白文字并实现标准化。\n\n 将全部tab制表符、回车键、换行字符用空白代替。\n- 将连续的空白文字变成一个空白文字。\n- 消除开头和末尾的空白文字。\n\n如果 `str` 没有被指定(即在无输入参数的情况下调用本函数),将上下文的值作为 `str` 来使用。 如果 `str` 不是字符串则抛出错误。"
},
"$contains": {
"args": "str, pattern",
"desc": "字符串`str` 和 `pattern`匹配的话输出`true`,不匹配的情况下输出 `false`。 不指定`str`的情况下(比如用一个参数调用本函数时)、将上下文的值作为`str`来使用。参数 `pattern`可以为字符串或正则表达。"
"desc": "字符串 `str` 和 `pattern` 匹配的话输出 `true` ,不匹配的情况下输出 `false` 。 不指定 `str` 的情况下(比如用一个参数调用本函数时)、将上下文的值作为 `str` 来使用。参数 `pattern` 可以为字符串或正则表达。"
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "将参数`str`分解成由子字符串组成的数组。 如果`str`不是字符串抛出错误。可以省略的参数 `separator`中指定字符串`str`的分隔符。分隔符可以是文字或正则表达式。在不指定`separator`的情况下、将分隔符看作空的字符串并把`str`拆分成由单个字母组成的数组。如果`separator`不是字符串则抛出错误。在可省略的参数`limit`中指定分割后的子字符串的最大个数。超出个数的子字符串将被舍弃。如果`limit`没有被指定,`str` 将不考虑子字符串的个数而将字符串完全分隔。如果`limit`是负数则抛出错误。"
"desc": "将参数 `str` 分解成由子字符串组成的数组。 如果 `str` 不是字符串抛出错误。可以省略的参数 `separator` 中指定字符串 `str` 的分隔符。分隔符可以是文字或正则表达式。在不指定 `separator` 的情况下、将分隔符看作空的字符串并把 `str` 拆分成由单个字母组成的数组。如果 `separator` 不是字符串则抛出错误。在可省略的参数 `limit` 中指定分割后的子字符串的最大个数。超出个数的子字符串将被舍弃。如果 `limit` 没有被指定,`str` 将不考虑子字符串的个数而将字符串完全分隔。如果 `limit` 是负数则抛出错误。"
},
"$join": {
"args": "array[, separator]",
"desc": "用可以省略的参数 `separator`来把多个字符串连接。如果`array`不是字符串则抛出错误。 如果没有指定`separator`,则用空字符串来连接字符(即字符串之间没有`separator`)。 如果`separator`不是字符则抛出错误。"
"desc": "用可以省略的参数 `separator` 来把多个字符串连接。如果 `array` 不是字符串则抛出错误。 如果没有指定 `separator` ,则用空字符串来连接字符(即字符串之间没有 `separator` )。 如果 `separator` 不是字符则抛出错误。"
},
"$match": {
"args": "str, pattern [, limit]",
"desc": "对字符串`str`使用正则表达式`pattern`并输出与`str`相匹配的部分信息。"
"desc": "对字符串 `str` 使用正则表达式 `pattern` 并输出与 `str` 相匹配的部分信息。"
},
"$replace": {
"args": "str, pattern, replacement [, limit]",
"desc": "在字符串`str`中搜索`pattern`并用`replacement`来替换。\n\n可选参数`limit`用来指定替换次数的上限。"
"desc": "在字符串 `str` 中搜索 `pattern` 并用 `replacement` 来替换。\n\n可选参数 `limit` 用来指定替换次数的上限。"
},
"$now": {
"args": "",
@@ -65,31 +65,31 @@
},
"$number": {
"args": "arg",
"desc": "用下述的规则将参数 `arg`转换为数值。:\n\n 数值不做转换。\n 将字符串中合法的JSON数値表示转换成数値。\n 其他形式的值则抛出错误。"
"desc": "用下述的规则将参数 `arg` 转换为数值。:\n\n 数值不做转换。\n 将字符串中合法的JSON数値表示转换成数値。\n 其他形式的值则抛出错误。"
},
"$abs": {
"args": "number",
"desc": "输出参数`number`的绝对值。"
"desc": "输出参数 `number` 的绝对值。"
},
"$floor": {
"args": "number",
"desc": "输出比`number`的值小的最大整数。"
"desc": "输出比 `number` 的值小的最大整数。"
},
"$ceil": {
"args": "number",
"desc": "输出比`number`的值大的最小整数。"
"desc": "输出比 `number` 的值大的最小整数。"
},
"$round": {
"args": "number [, precision]",
"desc": "输出四舍五入后的参数`number`。可省略的参数 `precision`指定四舍五入后小数点下的位数。"
"desc": "输出四舍五入后的参数 `number` 。可省略的参数 `precision` 指定四舍五入后小数点下的位数。"
},
"$power": {
"args": "base, exponent",
"desc": "输出底数`base``exponent`次幂。"
"desc": "输出底数 `base``exponent` 次幂。"
},
"$sqrt": {
"args": "number",
"desc": "输出参数 `number`的平方根。"
"desc": "输出参数 `number` 的平方根。"
},
"$random": {
"args": "",
@@ -97,35 +97,35 @@
},
"$millis": {
"args": "",
"desc": "返回从UNIX时间 (1970年1月1日 UTC/GMT的午夜开始到现在的毫秒数。在同一个表达式的测试中所有对`$millis()`的调用将会返回相同的值。"
"desc": "返回从UNIX时间 (1970年1月1日 UTC/GMT的午夜开始到现在的毫秒数。在同一个表达式的测试中所有对 `$millis()` 的调用将会返回相同的值。"
},
"$sum": {
"args": "array",
"desc": "输出数组`array`的总和。如果`array`不是数值则抛出错误。"
"desc": "输出数组 `array` 的总和。如果 `array` 不是数值则抛出错误。"
},
"$max": {
"args": "array",
"desc": "输出数组`array`的最大值。如果`array`不是数值则抛出错误。"
"desc": "输出数组 `array` 的最大值。如果 `array` 不是数值则抛出错误。"
},
"$min": {
"args": "array",
"desc": "输出数组`array`的最小值。如果`array`不是数值则抛出错误。。"
"desc": "输出数组 `array` 的最小值。如果 `array` 不是数值则抛出错误。。"
},
"$average": {
"args": "array",
"desc": "输出数组`array`的平均数。如果`array`不是数值则抛出错误。。"
"desc": "输出数组 `array` 的平均数。如果 `array` 不是数值则抛出错误。。"
},
"$boolean": {
"args": "arg",
"desc": "用下述规则将数据转换成布尔值。:\n\n - 不转换布尔值`Boolean`。\n 将空的字符串`string`转换为`false`\n 将不为空的字符串`string`转换为`true`\n 将为0的数字`number`转换成`false`\n 将不为0的数字`number`转换成`true`\n –将`null`转换成`false`\n –将空的数组`array`转换成`false`\n –如果数组`array`中含有可以转换成`true`的要素则转换成`true`\n –如果`array`中没有可转换成`true`的要素则转换成`false`\n 空的对象`object`转换成`false`\n 非空的对象`object`转换成`true`\n –将函数`function`转换成`false`"
"desc": "用下述规则将数据转换成布尔值。:\n\n - 不转换布尔值 `Boolean` 。\n 将空的字符串 `string` 转换为 `false` \n 将不为空的字符串 `string` 转换为 `true` \n 将为0的数字 `number` 转换成 `false` \n 将不为0的数字 `number` 转换成 `true` \n –将 `null` 转换成 `false` \n –将空的数组 `array` 转换成 `false` \n –如果数组 `array` 中含有可以转换成 `true` 的要素则转换成 `true` \n –如果 `array` 中没有可转换成 `true` 的要素则转换成 `false` \n 空的对象 `object` 转换成 `false` \n 非空的对象 `object` 转换成 `true` \n –将函数 `function` 转换成 `false` "
},
"$not": {
"args": "arg",
"desc": "输出做取反运算后的布尔值。首先将`arg`转换为布尔值。"
"desc": "输出做取反运算后的布尔值。首先将 `arg` 转换为布尔值。"
},
"$exists": {
"args": "arg",
"desc": "如果算式`arg`的值存在则输出`true`。如果算式的值不存在(比如指向不存在区域的引用)则输出`false`。"
"desc": "如果算式 `arg` 的值存在则输出 `true` 。如果算式的值不存在(比如指向不存在区域的引用)则输出 `false` 。"
},
"$count": {
"args": "array",
@@ -137,15 +137,15 @@
},
"$sort": {
"args": "array [, function]",
"desc": "输出排序后的数组`array`。\n\n如果使用了比较函数`function`,则下述两个参数需要被指定。\n\n`function(left, right)`\n\n该比较函数是为了比较left和right两个值而被排序算法调用的。如果用户希望left的值被置于right的值之后那么该函数必须输出布尔值`true`来表示位置交换。而在不需要位置交换时函数必须输出`false`。"
"desc": "输出排序后的数组 `array` 。\n\n如果使用了比较函数 `function` ,则下述两个参数需要被指定。\n\n `function(left, right)` \n\n该比较函数是为了比较left和right两个值而被排序算法调用的。如果用户希望left的值被置于right的值之后那么该函数必须输出布尔值 `true` 来表示位置交换。而在不需要位置交换时函数必须输出 `false` 。"
},
"$reverse": {
"args": "array",
"desc": "输出倒序后的数组`array`。"
"desc": "输出倒序后的数组 `array` 。"
},
"$shuffle": {
"args": "array",
"desc": "输出随机排序后的数组 `array`。"
"desc": "输出随机排序后的数组 `array` 。"
},
"$zip": {
"args": "array, ...",
@@ -157,35 +157,35 @@
},
"$lookup": {
"args": "object, key",
"desc": "输出对象中与参数`key`对应的值。如果第一个参数`object`是数组,那么数组中所有的对象都将被搜索并输出这些对象中与参数`key`对应的值。"
"desc": "输出对象中与参数 `key` 对应的值。如果第一个参数 `object` 是数组,那么数组中所有的对象都将被搜索并输出这些对象中与参数 `key` 对应的值。"
},
"$spread": {
"args": "object",
"desc": "将对象中的键值对分隔成每个要素中只含有一个键值对的数组。如果参数`object`是数组,那么返回值的数组中包含所有对象中的键值对。"
"desc": "将对象中的键值对分隔成每个要素中只含有一个键值对的数组。如果参数 `object` 是数组,那么返回值的数组中包含所有对象中的键值对。"
},
"$merge": {
"args": "array&lt;object&gt;",
"desc": "将输入数组`objects`中所有的键值对合并到一个`object`中并返回。如果输入数组的要素中含有重复的键,则返回的`object`中将只包含数组中最后出现要素的值。如果输入数组中包括对象以外的元素,则抛出错误。"
"desc": "将输入数组 `objects` 中所有的键值对合并到一个 `object` 中并返回。如果输入数组的要素中含有重复的键,则返回的 `object` 中将只包含数组中最后出现要素的值。如果输入数组中包括对象以外的元素,则抛出错误。"
},
"$sift": {
"args": "object, function",
"desc": "输出参数`object`中符合`function`的键值对。\n\n`function`必须含有下述参数。\n\n`function(value [, key [, object]])`"
"desc": "输出参数 `object` 中符合 `function` 的键值对。\n\n `function` 必须含有下述参数。\n\n `function(value [, key [, object]])` "
},
"$each": {
"args": "object, function",
"desc": "将函数`function`应用于`object`中的所有键值对并输出由所有返回值组成的数组。"
"desc": "将函数 `function` 应用于 `object` 中的所有键值对并输出由所有返回值组成的数组。"
},
"$map": {
"args": "array, function",
"desc": "将函数`function`应用于数组`array`中所有的值并输出由返回值组成的数组。\n\n`function`中必须含有下述参数。\n\n`function(value [, index [, array]])`"
"desc": "将函数 `function` 应用于数组 `array` 中所有的值并输出由返回值组成的数组。\n\n `function` 中必须含有下述参数。\n\n`function(value [, index [, array]])` "
},
"$filter": {
"args": "array, function",
"desc": "输出数组`array`中符合函数`function`条件的值组成的数组。\n\n`function`必须包括下述参数。\n\n`function(value [, index [, array]])`"
"desc": "输出数组 `array` 中符合函数 `function` 条件的值组成的数组。\n\n `function` 必须包括下述参数。\n\n `function(value [, index [, array]])`"
},
"$reduce": {
"args": "array, function [, init]",
"desc": "将`function`依次应用于数组中的各要素值。 其中,前一个要素值的计算结果将参与到下一次的函数运算中。。\n\n函数`function`接受两个参数并作为中缀表示法中的操作符。\n\n可省略的参数`init`将作为运算的初始值。"
"desc": "将 `function` 依次应用于数组中的各要素值。 其中,前一个要素值的计算结果将参与到下一次的函数运算中。。\n\n函数 `function` 接受两个参数并作为中缀表示法中的操作符。\n\n可省略的参数 `init` 将作为运算的初始值。"
},
"$flowContext": {
"args": "string",
@@ -197,7 +197,7 @@
},
"$pad": {
"args": "string, width [, char]",
"desc": "根据需要,向字符串`string`的副本中填充文字使该字符串的字数达到`width`的绝对值并返回填充文字后的字符串。\n\n如果`width`的值为正,则向字符串`string`的右侧填充文字,如果`width`为负,则向字符串`string`的左侧填充文字。\n\n可选参数`char`用来指定填充的文字。如果未指定该参数,则填充空白文字。"
"desc": "根据需要,向字符串 `string` 的副本中填充文字使该字符串的字数达到 `width` 的绝对值并返回填充文字后的字符串。\n\n如果 `width` 的值为正,则向字符串 `string` 的右侧填充文字,如果 `width` 为负,则向字符串 `string` 的左侧填充文字。\n\n可选参数 `char` 用来指定填充的文字。如果未指定该参数,则填充空白文字。"
},
"$fromMillis": {
"args": "number",
@@ -205,15 +205,15 @@
},
"$formatNumber": {
"args": "number, picture [, options]",
"desc": "将`number`转换成具有`picture`所指定的数值格式的字符串。\n\n此函数的功能与XPath F&O 3.1规格中定义的XPath/XQuery函数的fn:format-number功能相一致。参数`picture`用于指定数值的转换格式其语法与fn:format-number中的定义一致。\n\n可选的第三参数`options`用来覆盖默认的局部环境格式如小数点分隔符。如果指定该参数那么该参数必须是包含name/value对的对象并且name/value对必须符合XPath F&O 3.1规格中记述的数值格式。"
"desc": "将 `number` 转换成具有 `picture` 所指定的数值格式的字符串。\n\n此函数的功能与XPath F&O 3.1规格中定义的XPath/XQuery函数的fn:format-number功能相一致。参数 `picture` 用于指定数值的转换格式其语法与fn:format-number中的定义一致。\n\n可选的第三参数 `options` 用来覆盖默认的局部环境格式如小数点分隔符。如果指定该参数那么该参数必须是包含name/value对的对象并且name/value对必须符合XPath F&O 3.1规格中记述的数值格式。"
},
"$formatBase": {
"args": "number [, radix]",
"desc": "将`number`变换为以参数`radix`的值为基数形式的字符串。如果不指定`radix`的值则默认基数为10。指定的`radix`值必须在236之间否则抛出错误。"
"desc": "将 `number` 变换为以参数 `radix` 的值为基数形式的字符串。如果不指定 `radix` 的值则默认基数为10。指定的 `radix` 值必须在236之间否则抛出错误。"
},
"$toMillis": {
"args": "timestamp",
"desc": "将ISO 8601格式的字符串`timestamp`转换为从UNIX时间 (1970年1月1日 UTC/GMT的午夜开始到现在的毫秒数。如果该字符串的格式不正确则抛出错误。"
"desc": "将ISO 8601格式的字符串 `timestamp` 转换为从UNIX时间 (1970年1月1日 UTC/GMT的午夜开始到现在的毫秒数。如果该字符串的格式不正确则抛出错误。"
},
"$env": {
"args": "arg",
@@ -221,7 +221,7 @@
},
"$eval": {
"args": "expr [, context]",
"desc": "使用当前上下文来作为评估依据,分析并评估字符串`expr`其中包含文字JSON或JSONata表达式。"
"desc": "使用当前上下文来作为评估依据,分析并评估字符串 `expr` 其中包含文字JSON或JSONata表达式。"
},
"$formatInteger": {
"args": "number, picture",
@@ -233,19 +233,19 @@
},
"$error": {
"args": "[str]",
"desc": "引发错误并显示一条消息。 可选的`str`将替代$error()函数评估的默认消息。"
"desc": "引发错误并显示一条消息。 可选的 `str` 将替代$error()函数评估的默认消息。"
},
"$assert": {
"args": "arg, str",
"desc": "如果`arg`为真,则该函数返回。 如果arg为假则抛出带有str的异常作为异常消息。"
"desc": "如果 `arg` 为真,则该函数返回。 如果arg为假则抛出带有str的异常作为异常消息。"
},
"$single": {
"args": "array, function",
"desc": "返回满足参数function谓语的array参数中的唯一值 (比如传递值时函数返回布尔值“true”)。如果匹配值的数量不唯一时,则抛出异常。\n\n应在以下签名中提供函数`functionvalue [index [array []]]`其中value是数组的每个输入index是该值的位置整个数组作为第三个参数传递。"
"desc": "返回满足参数function谓语的array参数中的唯一值 (比如传递值时函数返回布尔值“true”)。如果匹配值的数量不唯一时,则抛出异常。\n\n应在以下签名中提供函数 `functionvalue [index [array []]]` 其中value是数组的每个输入index是该值的位置整个数组作为第三个参数传递。"
},
"$encodeUrl": {
"args": "str",
"desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL组件进行编码。\n\n示例`$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
"desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL组件进行编码。\n\n示例 `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
},
"$encodeUrlComponent": {
"args": "str",
@@ -261,10 +261,14 @@
},
"$distinct": {
"args": "array",
"desc": "返回一个数组,其中重复的值已从`数组`中删除"
"desc": "返回一个数组,其中重复的值已从 `数组` 中删除"
},
"$type": {
"args": "value",
"desc": "以字符串形式返回`值`的类型。 如果该`值`未定义,则将返回`未定义`"
"desc": "以字符串形式返回 `值` 的类型。 如果该 `值` 未定义,则将返回 `未定义` "
},
"$moment": {
"args": "[str]",
"desc": "使用Moment库获取日期对象。"
}
}

View File

@@ -14,7 +14,16 @@
"back": "返回",
"next": "下一步",
"clone": "複製專案",
"cont": "Continue"
"cont": "繼續",
"style": "風格",
"line": "大綱",
"fill": "填充",
"label": "標籤",
"color": "顏色",
"position": "位置",
"enable": "啟用",
"disable": "禁用",
"upload": "上傳"
},
"type": {
"string": "字符串",
@@ -28,6 +37,13 @@
"null": "空"
}
},
"event": {
"loadPalette": "加載控制板",
"loadNodeCatalogs": "加載節點目錄",
"loadNodes": "加載 __count__ 個節點",
"loadFlows": "加載流程",
"importFlows": "往工作區中加載流程"
},
"workspace": {
"defaultName": "流程__number__",
"editFlow": "編輯流程: __name__",
@@ -91,7 +107,12 @@
"projects-new": "新專案",
"projects-open": "開啟專案",
"projects-settings": "專案設定",
"showNodeLabelDefault": "顯示新添加節點的標籤"
"showNodeLabelDefault": "顯示新添加節點的標籤",
"groups": "組",
"groupSelection": "選擇組",
"ungroupSelection": "取消選擇組",
"groupMergeSelection": "合并選擇",
"groupRemoveSelection": "從組中移除"
}
},
"actions": {
@@ -101,7 +122,7 @@
"zoom-in": "放大"
},
"user": {
"loggedInAs": "作為__name__登入",
"loggedInAs": "作為 __name__ 登入",
"username": "帳號",
"password": "密碼",
"login": "登入",
@@ -133,7 +154,7 @@
"error": "<strong>Error</strong>: __message__",
"errors": {
"lostConnection": "丟失與伺服器的連接,重新連接...",
"lostConnectionReconnect": "丟失與伺服器的連接__time__秒後重新連接",
"lostConnectionReconnect": "丟失與伺服器的連接__time__ 秒後重新連接",
"lostConnectionTry": "現在嘗試",
"cannotAddSubflowToItself": "無法向其自身添加子流程",
"cannotAddCircularReference": "無法添加子流程 - 迴圈引用",
@@ -146,7 +167,7 @@
"loaded": "已加載項目'__project__'",
"updated": "已更新項目'__project__'",
"pull": "已重新加載項目'__project__'",
"revert": "項目__project__已還原",
"revert": "項目'__project__'已還原",
"merge-complete": "Git合併完成",
"setupCredentials": "設定證書",
"setupProjectFiles": "設置項目文件",
@@ -171,10 +192,14 @@
"node_plural": "__count__ 多個節點",
"configNode": "__count__ 節點組態",
"configNode_plural": "__count__ 多節點組態",
"group": "__count__ 個組",
"group_plural": "__count__ 個組",
"flow": "__count__ 流程",
"flow_plural": "__count__ 多流程",
"subflow": "__count__ 子流程",
"subflow_plural": "__count__ 多子流程",
"replacedNodes": "__count__ 個節點被置換",
"replacedNodes_plural": "__count__ 個節點被置換",
"pasteNodes": "在這裡粘貼節點",
"selectFile": "匯入所選檔案",
"importNodes": "匯入節點",
@@ -184,9 +209,15 @@
"importUnrecognised_plural": "匯入了無法識別的類型:",
"nodesExported": "節點匯出到了剪貼簿",
"nodesImported": "已匯入:",
"nodeCopied": "已複製__count__個節點",
"nodeCopied_plural": "已複製__count__個節點",
"nodeCopied": "已複製 __count__ 個節點",
"nodeCopied_plural": "已複製 __count__ 個節點",
"groupCopied": "複製 __count__ 個組",
"groupCopied_plural": "已複製 __count__ 個groups",
"groupStyleCopied": "已複製組風格",
"invalidFlow": "無效的流程: __message__",
"recoveredNodes": "復原的節點",
"recoveredNodesInfo": "導入節點時此流上的節點缺少有效的流ID。它們已被添加到此流中您可以復原或刪除它們。",
"recoveredNodesNotification": "<p>導入的節點缺少有效的流ID</p><p>已將它們添加到名為 '__flowName__'的新流中。</p>",
"export": {
"selected": "已選擇的節點",
"current": "現在的節點",
@@ -201,13 +232,19 @@
},
"import": {
"import": "匯入到",
"importSelected": "導入所選項",
"importCopy": "導入副本",
"viewNodes": "查看節點",
"newFlow": "新流程",
"replace": "置換",
"errors": {
"notArray": "輸入的不是JSON數組",
"itemNotObject": "輸入的流程無效-項目__index__不是節點對象",
"missingId": "輸入的流程無效-項__index__缺少“ id”屬性",
"missingType": "輸入的流程無效-項__index__缺少“類型”屬性"
}
"itemNotObject": "輸入的流程無效-項目 __index__ 不是節點對象",
"missingId": "輸入的流程無效-項 __index__ 缺少“ id”屬性",
"missingType": "輸入的流程無效-項 __index__ 缺少“類型”屬性"
},
"conflictNotification1": "您要導入的某些節點已經存在於工作空間中。",
"conflictNotification2": "選擇要導入的節點,並確認要替換現有的節點還是導入它們的副本"
},
"copyMessagePath": "已複製路徑",
"copyMessageValue": "已複製數值",
@@ -250,7 +287,7 @@
"conflictChecking": "檢查是否可以自動合併更改",
"conflictAutoMerge": "此更改不包括衝突,可以自動合併",
"conflictManualMerge": "這些更改包括了在部署之前必須解決的衝突。",
"plusNMore": "+更多的__count__"
"plusNMore": "+更多的 __count__"
}
},
"eventLog": {
@@ -258,8 +295,8 @@
"view": "查看日誌"
},
"diff": {
"unresolvedCount": "__count__個未解決的衝突",
"unresolvedCount_plural": "__count__個未解決的衝突",
"unresolvedCount": "__count__ 個未解決的衝突",
"unresolvedCount_plural": "__count__ 個未解決的衝突",
"globalNodes": "全局節點",
"flowProperties": "流程屬性",
"type": {
@@ -269,11 +306,11 @@
"deleted": "已刪除",
"flowDeleted": "已刪除流程",
"flowAdded": "已添加流程",
"movedTo": "移動至__id__",
"movedFrom": "從__id__移動"
"movedTo": "移動至 __id__",
"movedFrom": "從 __id__ 移動"
},
"nodeCount": "__count__個節點",
"nodeCount_plural": "__count__個節點",
"nodeCount": "__count__ 個節點",
"nodeCount_plural": "__count__ 個節點",
"local": "本地",
"remote": "遠端",
"reviewChanges": "查看變更",
@@ -287,11 +324,11 @@
"newVersionError": "新版本不包含有效的JSON"
},
"subflow": {
"editSubflowInstance": "編輯子流程實例__name__",
"editSubflow": "編輯流程範本: __name__",
"editSubflowInstance": "編輯子流程實例: __name__",
"editSubflow": "編輯流程範本 __name__",
"edit": "編輯流程範本",
"subflowInstances": "這個子流程範本有__count__個實例",
"subflowInstances_plural": "這個子流程範本有__count__個實例",
"subflowInstances": "這個子流程範本有 __count__ 個實例",
"subflowInstances_plural": "這個子流程範本有 __count__ 個實例",
"editSubflowProperties": "編輯屬性",
"input": "輸入:",
"output": "輸出:",
@@ -308,17 +345,24 @@
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
}
},
"group": {
"editGroup": "編輯組: __name__",
"errors": {
"cannotCreateDiffGroups": "無法使用來自不同組的節點創建組",
"cannotAddSubflowPorts": "無法將子流程的端口添加到組"
}
},
"editor": {
"configEdit": "編輯",
"configAdd": "添加",
"configUpdate": "更新",
"configDelete": "刪除",
"nodesUse": "__count__個節點使用此配置",
"nodesUse_plural": "__count__個節點使用此配置",
"addNewConfig": "添加新的__type__配置",
"editNode": "編輯__type__節點",
"editConfig": "編輯__type__配置",
"addNewType": "添加新的__type__節點",
"nodesUse": "__count__ 個節點使用此配置",
"nodesUse_plural": "__count__ 個節點使用此配置",
"addNewConfig": "添加新的 __type__ 配置",
"editNode": "編輯 __type__ 節點",
"editConfig": "編輯 __type__ 配置",
"addNewType": "添加新的 __type__ 節點",
"nodeProperties": "節點屬性",
"label": "Label",
"color": "顏色",
@@ -337,6 +381,7 @@
"locale": "選擇界面語言",
"icon": "圖標",
"inputType": "輸入類型",
"selectType": "選擇類型...",
"inputs": {
"input": "輸入",
"select": "選擇",
@@ -351,7 +396,8 @@
"bool": "布爾",
"json": "JSON",
"bin": "buffer",
"env": "環境變量"
"env": "環境變量",
"cred": "證書"
},
"menu": {
"input": "輸入",
@@ -405,13 +451,13 @@
"library": "庫",
"openLibrary": "打開庫...",
"saveToLibrary": "保存到庫...",
"typeLibrary": "__type__型別程式庫",
"unnamedType": "無名__type__",
"typeLibrary": "__type__ 型別程式庫",
"unnamedType": "無名 __type__",
"exportedToLibrary": "節點導出到庫",
"dialogSaveOverwrite": "一個叫做__libraryName____libraryType__已經存在您需要覆蓋麼",
"dialogSaveOverwrite": "一個叫做 __libraryName____libraryType__ 已經存在,您需要覆蓋麼?",
"invalidFilename": "無效的檔案名",
"savedNodes": "保存的節點",
"savedType": "已保存__type__",
"savedType": "已保存 __type__",
"saveFailed": "保存失敗: __message__",
"newFolder": "新文件夾",
"types": {
@@ -460,33 +506,33 @@
"times": {
"seconds": "秒前",
"minutes": "分前",
"minutesV": "__count__分前",
"hoursV": "__count__小時前",
"hoursV_plural": "__count__小時前",
"daysV": "__count__天前",
"daysV_plural": "__count__天前",
"weeksV": "__count__周前",
"weeksV_plural": "__count__周前",
"monthsV": "__count__月前",
"monthsV_plural": "__count__月前",
"yearsV": "__count__年前",
"yearsV_plural": "__count__年前",
"yearMonthsV": "__y__年, __count__月前",
"yearMonthsV_plural": "__y__年, __count__月前",
"yearsMonthsV": "__y__年, __count__月前",
"yearsMonthsV_plural": "__y__年, __count__月前"
"minutesV": "__count__ 分前",
"hoursV": "__count__ 小時前",
"hoursV_plural": "__count__ 小時前",
"daysV": "__count__ 天前",
"daysV_plural": "__count__ 天前",
"weeksV": "__count__ 周前",
"weeksV_plural": "__count__ 周前",
"monthsV": "__count__ 月前",
"monthsV_plural": "__count__ 月前",
"yearsV": "__count__ 年前",
"yearsV_plural": "__count__ 年前",
"yearMonthsV": "__y__ 年, __count__ 月前",
"yearMonthsV_plural": "__y__ 年, __count__ 月前",
"yearsMonthsV": "__y__ 年, __count__ 月前",
"yearsMonthsV_plural": "__y__ 年, __count__ 月前"
},
"nodeCount": "__label__個節點",
"nodeCount_plural": "__label__個節點",
"moduleCount": "__count__個可用模組",
"moduleCount_plural": "__count__個可用模組",
"nodeCount": "__label__ 個節點",
"nodeCount_plural": "__label__ 個節點",
"moduleCount": "__count__ 個可用模組",
"moduleCount_plural": "__count__ 個可用模組",
"inuse": "使用中",
"enableall": "全部啟用",
"disableall": "全部禁用",
"enable": "啟用",
"disable": "禁用",
"remove": "移除",
"update": "更新至__version__版本",
"update": "更新至 __version__ 版本",
"updated": "已更新",
"install": "安裝",
"installed": "已安裝",
@@ -498,7 +544,8 @@
"sort": "排序:",
"sortAZ": "a-z順序",
"sortRecent": "日期順序",
"more": "增加__count__個",
"more": "增加 __count__ 個",
"upload": "上傳模塊tgz文件",
"errors": {
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
@@ -539,6 +586,7 @@
"label": "信息",
"node": "節點",
"type": "類型",
"group": "組",
"module": "Module",
"id": "ID",
"status": "狀態",
@@ -560,8 +608,30 @@
"subflowDesc": "子流程描述",
"nodeHelp": "節點幫助",
"none": "無",
"arrayItems": "__count__個項目",
"showTips": "您可以從設置面板啟用提示資訊"
"arrayItems": "__count__ 個項目",
"showTips": "您可以從設置面板啟用提示資訊",
"outline": "大綱",
"empty": "空的",
"globalConfig": "全局配置節點",
"triggerAction": "觸發動作",
"find": "在工作區中查找",
"search": {
"configNodes": "配置節點",
"unusedConfigNodes": "未使用的配置節點",
"invalidNodes": "無效的節點",
"uknownNodes": "未知的節點",
"unusedSubflows": "未使用的子流程"
}
},
"help": {
"name": "幫助",
"label": "幫助",
"search": "搜索幫助",
"nodeHelp": "節點幫助",
"showHelp": "顯示幫助",
"showInOutline": "在大綱中顯示",
"showTopics": "顯示主題",
"noHelp": "未選擇幫助主題"
},
"config": {
"name": "配置節點",
@@ -614,9 +684,9 @@
"removeFromProject": "從項目中刪除",
"addToProject": "添加到項目",
"files": "文件",
"package": "包",
"flow": "流程",
"credentials": "證書",
"package": "包",
"packageCreate": "保存更改後將創建文件",
"fileNotExist": "文件不存在",
"selectFile": "選擇文件",
@@ -652,6 +722,12 @@
"committerTip": "保留空白以使用系統默認值",
"userName": "用戶名",
"email": "電子郵件",
"workflow": "工作流",
"workfowTip": "選擇您偏好的工作流",
"workflowManual": "手動",
"workflowManualTip": "所有更改都必須在“歷史記錄”側邊欄中手動提交",
"workflowAuto": "自動",
"workflowAutoTip": "每次部署後都會自動提交更改",
"sshKeys": "SSH密鑰",
"sshKeysTip": "允許您創建到遠程git存儲庫的安全連接。",
"add": "添加密鑰",
@@ -668,7 +744,7 @@
"copyPublicKey": "將公鑰複製到剪貼板",
"delete": "刪除密鑰",
"gitConfig": "Git配置",
"deleteConfirm": "您確定要刪除SSH密鑰__name__嗎 這不能被撤消。"
"deleteConfirm": "您確定要刪除SSH密鑰 __name__ 嗎? 這不能被撤消。"
},
"versionControl": {
"unstagedChanges": "未暫存的更改",
@@ -722,24 +798,24 @@
"pullChanges": "Pull變更",
"history": "歷史",
"projectHistory": "項目歷史",
"daysAgo": "__count__天前",
"daysAgo_plural": "__count__天前",
"hoursAgo": "__count__小時前",
"hoursAgo_plural": "__count__小時前",
"minsAgo": "__count__分鐘前",
"minsAgo_plural": "__count__分鐘前",
"daysAgo": "__count__ 天前",
"daysAgo_plural": "__count__ 天前",
"hoursAgo": "__count__ 小時前",
"hoursAgo_plural": "__count__ 小時前",
"minsAgo": "__count__ 分鐘前",
"minsAgo_plural": "__count__ 分鐘前",
"secondsAgo": "秒前",
"notTracking": "您的本地分支當前未跟蹤遠程分支。",
"statusUnmergedChanged": "您的存儲庫中有未合併的更改。您需要解決衝突並提交結果。",
"repositoryUpToDate": "您的存儲庫是最新的。",
"commitsAhead": "您的倉庫領先遠程倉庫__count__次提交。您現在可以push這些提交。",
"commitsAhead_plural": "您的倉庫領先遠程倉庫__count__次提交。您現在可以push這些提交。",
"commitsBehind": "您的倉庫落後遠程倉庫__count__次提交。您現在可以pull這些提交。",
"commitsBehind_plural": "您的倉庫落後遠程倉庫__count__次提交。您現在可以pull這些提交。",
"commitsAheadAndBehind1": "您的倉庫落後遠程倉庫__count__次提交",
"commitsAheadAndBehind1_plural": "您的倉庫落後遠程倉庫__count__次提交",
"commitsAheadAndBehind2": "領先遠程倉庫__count__次提交。",
"commitsAheadAndBehind2_plural": "領先遠程倉庫__count__次提交。",
"commitsAhead": "您的倉庫領先遠程倉庫 __count__ 次提交。您現在可以push這些提交。",
"commitsAhead_plural": "您的倉庫領先遠程倉庫 __count__ 次提交。您現在可以push這些提交。",
"commitsBehind": "您的倉庫落後遠程倉庫 __count__ 次提交。您現在可以pull這些提交。",
"commitsBehind_plural": "您的倉庫落後遠程倉庫 __count__ 次提交。您現在可以pull這些提交。",
"commitsAheadAndBehind1": "您的倉庫落後遠程倉庫 __count__ 次提交",
"commitsAheadAndBehind1_plural": "您的倉庫落後遠程倉庫 __count__ 次提交",
"commitsAheadAndBehind2": "領先遠程倉庫 __count__ 次提交。",
"commitsAheadAndBehind2_plural": "領先遠程倉庫 __count__ 次提交。",
"commitsAheadAndBehind3": "您必須先pull遠程提交然後再進行push。",
"commitsAheadAndBehind3_plural": "您必須先pull遠程提交然後再進行push。",
"refreshCommitHistory": "刷新提交歷史",
@@ -757,7 +833,8 @@
"bin": "二進位流",
"date": "時間戳記",
"jsonata": "expression",
"env": "env variable"
"env": "env variable",
"cred": "證書"
}
},
"editableList": {
@@ -977,7 +1054,8 @@
"passphrase": "密碼短語",
"retry": "重試",
"update-failed": "無法更新身份驗證",
"unhandled": "未處理的錯誤響應"
"unhandled": "未處理的錯誤響應",
"host-key-verify-failed": "<p>主機密鑰驗證失敗。</p><p>無法驗證存儲庫主機密鑰。請更新您的<code>known_hosts</code>文件,然後重試。</p>"
},
"create-branch-list": {
"invalid": "無效的分支",

View File

@@ -266,5 +266,9 @@
"$type": {
"args": "value",
"desc": "以字符串形式返回`值`的類型。 如果該`值`未定義,則將返回`未定義`"
},
"$moment": {
"args": "[str]",
"desc": "使用Moment庫獲取日期對象。"
}
}

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,8 @@
ace.define("ace/snippets/nrjavascript",[],function(e,t,n){"use strict";t.snippetText='# Prototype\nsnippet proto\n ${1:class_name}.prototype.${2:method_name} = function(${3:first_argument}) {\n ${4:// body...}\n };\n# Function\nsnippet fun\n function ${1?:function_name}(${2:argument}) {\n ${3:// body...}\n }\n# Anonymous Function\nregex /((=)\\s*|(:)\\s*|(\\()|\\b)/f/(\\))?/\nsnippet f\n function${M1?: ${1:functionName}}($2) {\n ${0:$TM_SELECTED_TEXT}\n }${M2?;}${M3?,}${M4?)}\n# Immediate function\ntrigger \\(?f\\(\nendTrigger \\)?\nsnippet f(\n (function(${1}) {\n ${0:${TM_SELECTED_TEXT:/* code */}}\n }(${1}));\n# if\nsnippet if\n if (${1:true}) {\n ${0}\n }\n# if ... else\nsnippet ife\n if (${1:true}) {\n ${2}\n } else {\n ${0}\n }\n# tertiary conditional\nsnippet ter\n ${1:/* condition */} ? ${2:a} : ${3:b}\n# switch\nsnippet switch\n switch (${1:expression}) {\n case \'${3:case}\':\n ${4:// code}\n break;\n ${5}\n default:\n ${2:// code}\n }\n# case\nsnippet case\n case \'${1:case}\':\n ${2:// code}\n break;\n ${3}\n\n# while (...) {...}\nsnippet wh\n while (${1:/* condition */}) {\n ${0:/* code */}\n }\n# try\nsnippet try\n try {\n ${0:/* code */}\n } catch (e) {}\n# do...while\nsnippet do\n do {\n ${2:/* code */}\n } while (${1:/* condition */});\n# Object Method\nsnippet :f\nregex /([,{[])|^\\s*/:f/\n ${1:method_name}: function(${2:attribute}) {\n ${0}\n }${3:,}\n# setTimeout function\nsnippet setTimeout\nregex /\\b/st|timeout|setTimeo?u?t?/\n setTimeout(function() {${3:$TM_SELECTED_TEXT}}, ${1:10});\n# console.log (Firebug)\nsnippet cl\n console.log(${1});\n# return\nsnippet ret\n return ${1:result}\n# for (property in object ) { ... }\nsnippet fori\n for (var ${1:prop} in ${2:Things}) {\n ${0:$2[$1]}\n }\n# hasOwnProperty\nsnippet has\n hasOwnProperty(${1})\n# docstring\nsnippet /**\n /**\n * ${1:description}\n *\n */\nsnippet @par\nregex /^\\s*\\*\\s*/@(para?m?)?/\n @param {${1:type}} ${2:name} ${3:description}\nsnippet @ret\n @return {${1:type}} ${2:description}\n# JSON.parse\nsnippet jsonp\n JSON.parse(${1:jstr});\n# JSON.stringify\nsnippet jsons\n JSON.stringify(${1:object});\n# self-defining function\nsnippet sdf\n var ${1:function_name} = function(${2:argument}) {\n ${3:// initial code ...}\n\n $1 = function($2) {\n ${4:// main code}\n };\n }\n# \nsnippet for-\n for (var ${1:i} = ${2:Things}.length; ${1:i}--; ) {\n ${0:${2:Things}[${1:i}];}\n }\n# for (...) {...}\nsnippet for\n for (var ${1:i} = 0; $1 < ${2:Things}.length; $1++) {\n ${3:$2[$1]}$0\n }\n# for (...) {...} (Improved Native For-Loop)\nsnippet forr\n for (var ${1:i} = ${2:Things}.length - 1; $1 >= 0; $1--) {\n ${3:$2[$1]}$0\n }\n# Node-RED Specific Funcs\nsnippet nodes\n node.send(${1:msg})\nsnippet clone\n RED.util.cloneMessage(${1:msg})\nsnippet nodel\n node.log($1)\nsnippet nodew\n node.warn($1)\nsnippet nodee\n node.error($1)\nsnippet noded\n node.debug($1)\nsnippet done\n node.done($1)\nsnippet flowg\n flow.get($1)\nsnippet flows\n flow.set($1, $2)\nsnippet globalg\n global.get($1)\nsnippet globals\n global.set($1, $2)\n',t.scope="nrjavascript"});
(function() {
; (function() {
ace.require(["ace/snippets/nrjavascript"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

File diff suppressed because one or more lines are too long

View File

@@ -76,13 +76,12 @@ oop.inherits(NRJavaScriptWorker, Mirror);
(function() {
this.setOptions = function(options) {
this.options = options || {
this.options = {
// undef: true,
// unused: true,
esnext: true,
moz: true,
esversion: 9,
devel: true,
browser: true,
browser: false,
node: true,
laxcomma: true,
laxbreak: true,
@@ -92,8 +91,17 @@ oop.inherits(NRJavaScriptWorker, Mirror);
maxerr: 100,
expr: true,
multistr: true,
globalstrict: true
strict: false,
sub: true,
asi: true
};
if (options) {
for (var opt in options) {
if (options.hasOwnProperty(opt)) {
this.options[opt] = options.opt;
}
}
}
this.doc.getValue() && this.deferredUpdate.schedule(100);
};
@@ -119,6 +127,8 @@ oop.inherits(NRJavaScriptWorker, Mirror);
if (!value)
return this.sender.emit("annotate", []);
var originalValue = value;
// [Node-RED] wrap the code in a function
value = "async function __nodered__(msg) {\n"+value+"\n}";
@@ -138,6 +148,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
continue;
var raw = error.raw;
var type = "warning";
var line = error.line - 2;
if (raw == "Missing semicolon.") {
var str = error.evidence.substr(error.character);
@@ -166,9 +177,62 @@ oop.inherits(NRJavaScriptWorker, Mirror);
type = "info";
}
if (raw === "Unmatched '{a}'." && line === -1) {
// This is an unmatched { error. It has incorrectly matched it
// against the { in the added line. Need to find the next valid {
// This code scans through the original code looking for the first '{'
// that is not in a comment or string.
// It will incorrectly find a '{' if it is inside a regex... but
// at least the error will be shown somwhere. There are only
// so many hours in the day to fix every tiny edge case of an
// edge case.
var inSingleComment = false;
var inMultiComment = false;
var inString = false;
var stringQ;
var lineNumber = 0;
for (var pos = 0;pos<originalValue.length;pos++) {
var c = originalValue[pos];
if (c === "\\") {
pos++;
} else if (inSingleComment) {
if (c === "\n") {
lineNumber++;
inSingleComment = false;
}
} else if (inMultiComment) {
if (c === "*" && originalValue[pos+1] === "/") {
pos++;
inMultiComment = false;
} else if (c === "\n") {
lineNumber++;
}
} else if (inString) {
if (c === stringQ) {
inString = false;
}
} else if (c === "'" || c === "\"") {
inString = true;
stringQ = c;
} else if (c === "/") {
if (originalValue[pos+1] === "/") {
inSingleComment = true;
} else if (originalValue[pos+1] === "*") {
inMultiComment = true;
}
} else if (c === "\n") {
lineNumber++;
} else if (c === "{") {
// found it!
break;
}
}
line = lineNumber;
}
errors.push({
// [Node-RED] offset the row for the added line
row: error.line-2,
row: Math.max(0,line),
column: error.character-1,
text: error.reason,
type: type,

View File

@@ -32,14 +32,19 @@
}
}
}
function emit(evt,arg) {
function emit() {
var evt = arguments[0]
var args = Array.prototype.slice.call(arguments,1);
if (RED.events.DEBUG) {
console.warn(evt,args);
}
if (handlers[evt]) {
for (var i=0;i<handlers[evt].length;i++) {
try {
handlers[evt][i](arg);
handlers[evt][i].apply(null, args);
} catch(err) {
console.log("RED.events.emit error: ["+evt+"] "+(err.toString()));
console.log(err);
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
console.warn(err);
}
}

View File

@@ -808,17 +808,7 @@ RED.nodes.fontAwesome = (function() {
"fa-youtube": "\uf167",
};
var iconList = [];
var isUsed = {};
Object.keys(iconMap).forEach(function(icon) {
var unicode = iconMap[icon];
// skip icons with a same unicode
if (isUsed[unicode] !== true) {
iconList.push(icon);
isUsed[unicode] = true;
}
});
isUsed = undefined;
var iconList = Object.keys(iconMap);
return {
getIconUnicode: function(name) {

View File

@@ -21,6 +21,7 @@ RED.history = (function() {
var i;
var len;
var node;
var group;
var subflow;
var modifiedTabs = {};
var inverseEv;
@@ -36,22 +37,38 @@ RED.history = (function() {
inverseEv.events.push(r);
}
} else if (ev.t == 'replace') {
inverseEv = {
t: 'replace',
config: RED.nodes.createCompleteNodeSet(),
changed: {},
rev: RED.nodes.version()
};
RED.nodes.clear();
var imported = RED.nodes.import(ev.config);
imported[0].forEach(function(n) {
if (ev.changed[n.id]) {
n.changed = true;
inverseEv.changed[n.id] = true;
}
})
if (ev.complete) {
// This is a replace of everything. We can short-cut
// the logic by clearing everyting first, then importing
// the ev.config.
// Used by RED.diff.mergeDiff
inverseEv = {
t: 'replace',
config: RED.nodes.createCompleteNodeSet(),
changed: {},
rev: RED.nodes.version()
};
RED.nodes.clear();
var imported = RED.nodes.import(ev.config);
imported.nodes.forEach(function(n) {
if (ev.changed[n.id]) {
n.changed = true;
inverseEv.changed[n.id] = true;
}
})
RED.nodes.version(ev.rev);
RED.nodes.version(ev.rev);
} else {
var importMap = {};
ev.config.forEach(function(n) {
importMap[n.id] = "replace";
})
var importedResult = RED.nodes.import(ev.config,{importMap: importMap})
inverseEv = {
t: 'replace',
config: importedResult.removedNodes
}
}
} else if (ev.t == 'add') {
inverseEv = {
t: "delete",
@@ -65,6 +82,14 @@ RED.history = (function() {
}
inverseEv.nodes.push(node);
RED.nodes.remove(ev.nodes[i]);
if (node.g) {
var group = RED.nodes.group(node.g);
var index = group.nodes.indexOf(node);
if (index !== -1) {
group.nodes.splice(index,1);
RED.group.markDirty(group);
}
}
}
}
if (ev.links) {
@@ -74,6 +99,17 @@ RED.history = (function() {
RED.nodes.removeLink(ev.links[i]);
}
}
if (ev.groups) {
inverseEv.groups = [];
for (i = ev.groups.length - 1;i>=0;i--) {
group = ev.groups[i];
modifiedTabs[group.z] = true;
// The order of groups is important
// - to invert the action, the order is reversed
inverseEv.groups.unshift(group);
RED.nodes.removeGroup(group);
}
}
if (ev.workspaces) {
inverseEv.workspaces = [];
for (i=0;i<ev.workspaces.length;i++) {
@@ -186,19 +222,46 @@ RED.history = (function() {
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
n.inputs = subflow.in.length;
n.outputs = subflow.out.length;
while (n.outputs > n.ports.length) {
n.ports.push(n.ports.length);
}
n.resize = true;
n.dirty = true;
});
}
if (ev.groups) {
inverseEv.groups = [];
var groupsToAdd = {};
ev.groups.forEach(function(g) { groupsToAdd[g.id] = g; });
for (i = ev.groups.length - 1;i>=0;i--) {
RED.nodes.addGroup(ev.groups[i])
modifiedTabs[ev.groups[i].z] = true;
// The order of groups is important
// - to invert the action, the order is reversed
inverseEv.groups.unshift(ev.groups[i]);
if (ev.groups[i].g) {
if (!groupsToAdd[ev.groups[i].g]) {
group = RED.nodes.group(ev.groups[i].g);
} else {
group = groupsToAdd[ev.groups[i].g];
}
if (group.nodes.indexOf(ev.groups[i]) === -1) {
group.nodes.push(ev.groups[i]);
}
RED.group.markDirty(ev.groups[i])
}
}
}
if (ev.nodes) {
inverseEv.nodes = [];
for (i=0;i<ev.nodes.length;i++) {
RED.nodes.add(ev.nodes[i]);
modifiedTabs[ev.nodes[i].z] = true;
inverseEv.nodes.push(ev.nodes[i].id);
if (ev.nodes[i].g) {
group = RED.nodes.group(ev.nodes[i].g);
if (group.nodes.indexOf(ev.nodes[i]) === -1) {
group.nodes.push(ev.nodes[i]);
}
RED.group.markDirty(group)
}
}
}
if (ev.links) {
@@ -227,9 +290,12 @@ RED.history = (function() {
}
node.dirty = true;
}
RED.events.emit("nodes:change",node);
}
}
}
if (subflow) {
RED.events.emit("subflows:change", subflow);
}
} else if (ev.t == "move") {
inverseEv = {
@@ -260,6 +326,13 @@ RED.history = (function() {
RED.nodes.addLink(ev.removedLinks[i]);
}
}
if (ev.addToGroup) {
RED.group.removeFromGroup(ev.addToGroup,ev.nodes.map(function(n) { return n.n }),false);
inverseEv.removeFromGroup = ev.addToGroup;
} else if (ev.removeFromGroup) {
RED.group.addToGroup(ev.removeFromGroup,ev.nodes.map(function(n) { return n.n }));
inverseEv.addToGroup = ev.removeFromGroup;
}
} else if (ev.t == "edit") {
inverseEv = {
t: "edit",
@@ -274,15 +347,28 @@ RED.history = (function() {
var currentConfigNode = RED.nodes.node(ev.node[i]);
if (currentConfigNode) {
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
RED.events.emit("nodes:change",currentConfigNode);
}
var newConfigNode = RED.nodes.node(ev.changes[i]);
if (newConfigNode) {
newConfigNode.users.push(ev.node);
RED.events.emit("nodes:change",newConfigNode);
}
}
ev.node[i] = ev.changes[i];
}
}
var eventType;
switch(ev.node.type) {
case 'tab': eventType = "flows"; break;
case 'group': eventType = "groups"; break;
case 'subflow': eventType = "subflows"; break;
default: eventType = "nodes"; break;
}
eventType += ":change";
RED.events.emit(eventType,ev.node);
if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) {
$("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled);
@@ -342,6 +428,7 @@ RED.history = (function() {
}
}
}
ev.node.__outputs = inverseEv.changes.outputs;
RED.editor.updateNodeProperties(ev.node,outputMap);
RED.editor.validateNode(ev.node);
}
@@ -370,7 +457,9 @@ RED.history = (function() {
if (ev.nodes) {
inverseEv.movedNodes = [];
var z = ev.activeWorkspace;
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
var fullNodeList = RED.nodes.filterNodes({z:ev.subflow.subflow.id});
fullNodeList = fullNodeList.concat(RED.nodes.groups(ev.subflow.subflow.id))
fullNodeList.forEach(function(n) {
n.x += ev.subflow.offsetX;
n.y += ev.subflow.offsetY;
n.dirty = true;
@@ -411,6 +500,9 @@ RED.history = (function() {
if (ev.subflow) {
RED.nodes.addSubflow(ev.subflow.subflow);
inverseEv.subflow = ev.subflow;
if (ev.subflow.subflow.g) {
RED.group.addToGroup(RED.nodes.group(ev.subflow.subflow.g),ev.subflow.subflow);
}
}
if (ev.subflows) {
inverseEv.nodes = [];
@@ -422,6 +514,9 @@ RED.history = (function() {
if (ev.movedNodes) {
ev.movedNodes.forEach(function(nid) {
nn = RED.nodes.node(nid);
if (!nn) {
nn = RED.nodes.group(nid);
}
nn.x -= ev.subflow.offsetX;
nn.y -= ev.subflow.offsetY;
nn.dirty = true;
@@ -435,7 +530,7 @@ RED.history = (function() {
RED.nodes.addLink(ev.links[i]);
}
}
if (ev.createdLinks) {
if (ev.createdLinks) {
inverseEv.removedLinks = [];
for (i=0;i<ev.createdLinks.length;i++) {
inverseEv.removedLinks.push(ev.createdLinks[i]);
@@ -450,6 +545,60 @@ RED.history = (function() {
if (ev.order) {
RED.workspaces.order(ev.order);
}
} else if (ev.t == "createGroup") {
inverseEv = {
t: "ungroup",
dirty: RED.nodes.dirty(),
groups: []
}
if (ev.groups) {
for (i=0;i<ev.groups.length;i++) {
inverseEv.groups.push(ev.groups[i]);
RED.group.ungroup(ev.groups[i]);
}
}
} else if (ev.t == "ungroup") {
inverseEv = {
t: "createGroup",
dirty: RED.nodes.dirty(),
groups: []
}
if (ev.groups) {
for (i=0;i<ev.groups.length;i++) {
inverseEv.groups.push(ev.groups[i]);
var nodes = ev.groups[i].nodes.slice();
ev.groups[i].nodes = [];
RED.nodes.addGroup(ev.groups[i]);
RED.group.addToGroup(ev.groups[i],nodes);
}
}
} else if (ev.t == "addToGroup") {
inverseEv = {
t: "removeFromGroup",
dirty: RED.nodes.dirty(),
group: ev.group,
nodes: ev.nodes,
reparent: ev.reparent
}
if (ev.nodes) {
RED.group.removeFromGroup(ev.group,ev.nodes,(ev.hasOwnProperty('reparent')&&ev.hasOwnProperty('reparent')!==undefined)?ev.reparent:true);
}
} else if (ev.t == "removeFromGroup") {
inverseEv = {
t: "addToGroup",
dirty: RED.nodes.dirty(),
group: ev.group,
nodes: ev.nodes,
reparent: ev.reparent
}
if (ev.nodes) {
RED.group.addToGroup(ev.group,ev.nodes);
}
}
if(ev.callback && typeof ev.callback === 'function') {
inverseEv.callback = ev.callback;
ev.callback(ev);
}
Object.keys(modifiedTabs).forEach(function(id) {
@@ -460,9 +609,8 @@ RED.history = (function() {
});
RED.nodes.dirty(ev.dirty);
RED.view.updateActive();
RED.view.select(null);
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
RED.subflow.refresh();
@@ -482,6 +630,9 @@ RED.history = (function() {
list: function() {
return undoHistory;
},
listRedo: function() {
return redoHistory;
},
depth: function() {
return undoHistory.length;
},

View File

@@ -10,6 +10,7 @@
"ctrl-escape": "core:cancel-edit-tray",
"ctrl-d": "core:deploy-flows",
"ctrl-g i": "core:show-info-tab",
"ctrl-g h": "core:show-help-tab",
"ctrl-g d": "core:show-debug-tab",
"ctrl-g c": "core:show-config-tab",
"ctrl-g x": "core:show-context-tab",
@@ -44,6 +45,14 @@
"ctrl-y": "core:redo",
"ctrl-a": "core:select-all-nodes",
"shift-?": "core:show-help",
"w": "core:scroll-view-up",
"d": "core:scroll-view-right",
"s": "core:scroll-view-down",
"a": "core:scroll-view-left",
"shift-w": "core:step-view-up",
"shift-d": "core:step-view-right",
"shift-s": "core:step-view-down",
"shift-a": "core:step-view-left",
"up": "core:move-selection-up",
"right": "core:move-selection-right",
"down": "core:move-selection-down",
@@ -53,6 +62,10 @@
"shift-down": "core:step-selection-down",
"shift-left": "core:step-selection-left",
"ctrl-shift-j": "core:show-previous-tab",
"ctrl-shift-k": "core:show-next-tab"
"ctrl-shift-k": "core:show-next-tab",
"ctrl-shift-g": "core:group-selection",
"ctrl-shift-u": "core:ungroup-selection",
"ctrl-shift-c": "core:copy-group-style",
"ctrl-shift-v": "core:paste-group-style"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
(function() {
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
// IE11 DOMTokenList.toggle does not support the two-argument variety
window.DOMTokenList.prototype.toggle = function(cl,bo) {
if (arguments.length === 1) {
bo = !this.contains(cl);
}
this[!!bo?"add":"remove"](cl);
}
// IE11 does not provide classList on SVGElements
if (! ("classList" in SVGElement.prototype)) {
Object.defineProperty(SVGElement.prototype, 'classList', Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'classList'));
}
// IE11 does not provide children on SVGElements
if (! ("children" in SVGElement.prototype)) {
Object.defineProperty(SVGElement.prototype, 'children', Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'children'));
}
Array.from = function() {
if (arguments.length > 1) {
throw new Error("Node-RED's IE11 Array.from polyfill doesn't support multiple arguments");
}
var arrayLike = arguments[0]
var result = [];
if (arrayLike.forEach) {
arrayLike.forEach(function(i) {
result.push(i);
})
} else {
for (var i=0;i<arrayLike.length;i++) {
result.push(arrayList[i]);
}
}
return result;
}
if (new Set([0]).size === 0) {
// IE does not support passing an iterable to Set constructor
var _Set = Set;
/*global Set:true */
Set = function Set(iterable) {
var set = new _Set();
if (iterable) {
iterable.forEach(set.add, set);
}
return set;
};
Set.prototype = _Set.prototype;
Set.prototype.constructor = Set;
}
}
})();

View File

@@ -75,6 +75,7 @@ var RED = (function() {
}
function loadNodeList() {
loader.reportProgress(RED._("event.loadPalette"), 20)
$.ajax({
headers: {
"Accept":"application/json"
@@ -83,6 +84,7 @@ var RED = (function() {
url: 'nodes',
success: function(data) {
RED.nodes.setNodeList(data);
loader.reportProgress(RED._("event.loadNodeCatalogs"), 25)
RED.i18n.loadNodeCatalogs(function() {
loadIconList(loadNodes);
});
@@ -107,6 +109,7 @@ var RED = (function() {
}
function loadNodes() {
loader.reportProgress(RED._("event.loadNodes",{count:""}), 30)
var lang = localStorage.getItem("editor-language")||i18n.detectLanguage();
$.ajax({
@@ -118,15 +121,19 @@ var RED = (function() {
url: 'nodes',
success: function(data) {
var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/);
var totalCount = configs.length;
var stepConfig = function() {
loader.reportProgress(RED._("event.loadNodes",{count:(totalCount-configs.length)+"/"+totalCount}), 30 + ((totalCount-configs.length)/totalCount)*40 )
if (configs.length === 0) {
$("#red-ui-editor").i18n();
$("#red-ui-palette > .red-ui-palette-spinner").hide();
$(".red-ui-palette-scroll").removeClass("hide");
$("#red-ui-palette-search").removeClass("hide");
loadFlows(function() {
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.refresh(function(activeProject) {
if (RED.settings.theme("projects.enabled",false)) {
RED.projects.refresh(function(activeProject) {
loadFlows(function() {
RED.sidebar.info.refresh()
if (!activeProject) {
// Projects enabled but no active project
@@ -140,12 +147,14 @@ var RED = (function() {
}
completeLoad();
});
} else {
});
} else {
loadFlows(function() {
// Projects disabled by the user
RED.sidebar.info.refresh()
completeLoad();
}
});
});
}
} else {
var config = configs.shift();
appendNodeConfig(config,stepConfig);
@@ -157,6 +166,7 @@ var RED = (function() {
}
function loadFlows(done) {
loader.reportProgress(RED._("event.loadFlows"),80 )
$.ajax({
headers: {
"Accept":"application/json",
@@ -167,11 +177,22 @@ var RED = (function() {
if (nodes) {
var currentHash = window.location.hash;
RED.nodes.version(nodes.rev);
RED.nodes.import(nodes.flows);
RED.nodes.dirty(false);
RED.view.redraw(true);
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6));
loader.reportProgress(RED._("event.importFlows"),90 )
try {
RED.nodes.import(nodes.flows);
RED.nodes.dirty(false);
RED.view.redraw(true);
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6));
}
} catch(err) {
RED.notify(
RED._("event.importError", {message: err.message}),
{
fixed: true,
type: 'error'
}
);
}
}
done();
@@ -193,6 +214,7 @@ var RED = (function() {
return;
}
if (notificationId === "project-update") {
loader.start("Loading project",0)
RED.nodes.clear();
RED.history.clear();
RED.view.redraw(true);
@@ -208,6 +230,7 @@ var RED = (function() {
"revert": RED._("notification.project.revert", {project: msg.project}),
"merge-complete": RED._("notification.project.merge-complete")
}[msg.action];
loader.end()
RED.notify("<p>"+message+"</p>");
RED.sidebar.info.refresh()
});
@@ -353,13 +376,13 @@ var RED = (function() {
var parts = topic.split("/");
var node = RED.nodes.node(parts[1]);
if (node) {
if (msg.hasOwnProperty("text") && /^[a-zA-Z]/.test(msg.text)) {
if (msg.hasOwnProperty("text") && msg.text !== null && /^[a-zA-Z]/.test(msg.text)) {
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
}
node.status = msg;
node.dirtyStatus = true;
node.dirty = true;
RED.view.redraw();
RED.view.redrawStatus(node);
}
});
RED.comms.subscribe("notification/node/#",function(topic,msg) {
@@ -423,6 +446,12 @@ var RED = (function() {
var id = topic.substring(9);
RED.eventLog.log(id,payload);
});
$(".red-ui-header-toolbar").show();
setTimeout(function() {
loader.end();
},100);
}
function showAbout() {
@@ -431,8 +460,7 @@ var RED = (function() {
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.info.set(aboutHeader+RED.utils.renderMarkdown(data));
RED.sidebar.info.show();
RED.sidebar.help.set(aboutHeader+RED.utils.renderMarkdown(data));
});
}
@@ -472,6 +500,14 @@ var RED = (function() {
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"},
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"},
]});
menuOptions.push({id:"menu-item-group",label:RED._("menu.label.groups"), options: [
{id:"menu-item-group-group",label:RED._("menu.label.groupSelection"),disabled:true,onselect:"core:group-selection"},
{id:"menu-item-group-ungroup",label:RED._("menu.label.ungroupSelection"),disabled:true,onselect:"core:ungroup-selection"},
null,
{id:"menu-item-group-merge",label:RED._("menu.label.groupMergeSelection"),disabled:true,onselect:"core:merge-selection-to-group"},
{id:"menu-item-group-remove",label:RED._("menu.label.groupRemoveSelection"),disabled:true,onselect:"core:remove-selection-from-group"}
]});
menuOptions.push(null);
if (RED.settings.theme('palette.editable') !== false) {
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
@@ -497,7 +533,6 @@ var RED = (function() {
}
function loadEditor() {
RED.workspaces.init();
RED.statusBar.init();
RED.view.init();
@@ -524,6 +559,7 @@ var RED = (function() {
}
RED.subflow.init();
RED.group.init();
RED.clipboard.init();
RED.search.init();
RED.actionList.init();
@@ -539,13 +575,14 @@ var RED = (function() {
RED.comms.connect();
$("#red-ui-main-container").show();
$(".red-ui-header-toolbar").show();
RED.actions.add("core:show-about", showAbout);
loadNodeList();
}
function buildEditor(options) {
var header = $('<div id="red-ui-header"></div>').appendTo(options.target);
var logo = $('<span class="red-ui-header-logo"></span>').appendTo(header);
@@ -560,6 +597,10 @@ var RED = (function() {
'</div>').appendTo(options.target);
$('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target);
$('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target);
loader.init().appendTo("#red-ui-main-container");
loader.start("...",0);
$.getJSON(options.apiRootUrl+"theme", function(theme) {
if (theme.header) {
if (theme.header.url) {
@@ -592,12 +633,39 @@ var RED = (function() {
options.target.addClass("red-ui-editor");
buildEditor(options);
RED.i18n.init(options, function() {
RED.settings.init(options, loadEditor);
})
}
var loader = {
init: function() {
var wrapper = $('<div id="red-ui-loading-progress"></div>').hide();
var container = $('<div>').appendTo(wrapper);
var label = $('<div>',{class:"red-ui-loading-bar-label"}).appendTo(container);
var bar = $('<div>',{class:"red-ui-loading-bar"}).appendTo(container);
var fill =$('<span>').appendTo(bar);
return wrapper;
},
start: function(text, prcnt) {
if (text) {
loader.reportProgress(text,prcnt)
}
$("#red-ui-loading-progress").show();
},
reportProgress: function(text, prcnt) {
$(".red-ui-loading-bar-label").text(text);
$(".red-ui-loading-bar span").width(prcnt+"%")
},
end: function() {
$("#red-ui-loading-progress").hide();
loader.reportProgress("",0);
}
}
return {
init: init
init: init,
loader: loader
}
})();

View File

@@ -21,7 +21,18 @@ RED.actions = (function() {
var result = [];
Object.keys(actions).forEach(function(action) {
var shortcut = RED.keyboard.getShortcut(action);
result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined,user:shortcut?shortcut.user:undefined})
var isUser = false;
if (shortcut) {
isUser = shortcut.user;
} else {
isUser = !!RED.keyboard.getUserShortcut(action);
}
result.push({
id:action,
scope:shortcut?shortcut.scope:undefined,
key:shortcut?shortcut.key:undefined,
user:isUser
})
})
return result;
}

View File

@@ -28,6 +28,8 @@ RED.clipboard = (function() {
var libraryBrowser;
var examplesBrowser;
var pendingImportConfig;
function setupDialogs() {
dialog = $('<div id="red-ui-clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("#red-ui-editor")
@@ -42,14 +44,14 @@ RED.clipboard = (function() {
"ui-widget-overlay": "red-ui-editor-dialog"
},
buttons: [
{
{ // red-ui-clipboard-dialog-cancel
id: "red-ui-clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
{ // red-ui-clipboard-dialog-download
id: "red-ui-clipboard-dialog-download",
class: "primary",
text: RED._("clipboard.download"),
@@ -64,7 +66,7 @@ RED.clipboard = (function() {
$( this ).dialog( "close" );
}
},
{
{ // red-ui-clipboard-dialog-export
id: "red-ui-clipboard-dialog-export",
class: "primary",
text: RED._("clipboard.export.copy"),
@@ -134,14 +136,14 @@ RED.clipboard = (function() {
}
}
},
{
{ // red-ui-clipboard-dialog-ok
id: "red-ui-clipboard-dialog-ok",
class: "primary",
text: RED._("common.label.import"),
click: function() {
var addNewFlow = ($("#red-ui-clipboard-dialog-import-opt > a.selected").attr('id') === 'red-ui-clipboard-dialog-import-opt-new');
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
RED.view.importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
} else {
var selectedPath;
if (activeTab === "red-ui-clipboard-dialog-import-tab-library") {
@@ -151,15 +153,51 @@ RED.clipboard = (function() {
}
if (selectedPath.path) {
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
RED.view.importNodes(data,addNewFlow);
importNodes(data,addNewFlow);
});
}
}
$( this ).dialog( "close" );
}
},
{ // red-ui-clipboard-dialog-import-conflict
id: "red-ui-clipboard-dialog-import-conflict",
class: "primary",
text: RED._("clipboard.import.importSelected"),
click: function() {
var importMap = {};
$('#red-ui-clipboard-dialog-import-conflicts-list input[type="checkbox"]').each(function() {
importMap[$(this).attr("data-node-id")] = this.checked?"import":"skip";
})
$('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').each(function() {
if (!$(this).attr("disabled")) {
importMap[$(this).attr("data-node-id")] = this.checked?"replace":"copy"
}
})
// skip - don't import
// import - import as-is
// copy - import with new id
// replace - import over the top of existing
pendingImportConfig.importOptions.importMap = importMap;
var newNodes = pendingImportConfig.importNodes.filter(function(n) {
if (!importMap[n.id] || importMap[n.z]) {
importMap[n.id] = importMap[n.z];
}
return importMap[n.id] !== "skip"
})
// console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
$( this ).dialog( "close" );
}
}
],
open: function( event, ui ) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
if (popover) {
popover.close(true);
currentPopoverError = null;
@@ -184,7 +222,7 @@ RED.clipboard = (function() {
'</div>'+
'<div id="red-ui-clipboard-dialog-export-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+
'<div class="form-row">'+
'<div class="form-row" style="height:calc(100% - 30px)">'+
'<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
'</div>'+
'<div class="form-row" style="text-align: right;">'+
@@ -216,7 +254,7 @@ RED.clipboard = (function() {
' <a class="red-ui-button" id="red-ui-clipboard-dialog-import-file-upload-btn"><i class="fa fa-upload"></i> <span data-i18n="clipboard.selectFile"></span></a>'+
'<input type="file" id="red-ui-clipboard-dialog-import-file-upload" accept=".json" style="display:none">'+
'</div>'+
'<div class="form-row">'+
'<div class="form-row" style="height:calc(100% - 47px)">'+
'<textarea id="red-ui-clipboard-dialog-import-text"></textarea>'+
'</div>'+
'</div>'+
@@ -232,6 +270,14 @@ RED.clipboard = (function() {
'</span>'+
'</div>';
importConflictsDialog =
'<div class="form-row">'+
'<div class="form-row"><p data-i18n="clipboard.import.conflictNotification1"></p><p data-i18n="clipboard.import.conflictNotification2"></p></div>'+
'<div class="red-ui-clipboard-dialog-import-conflicts-list-container">'+
'<div id="red-ui-clipboard-dialog-import-conflicts-list"></div>'+
'</div>'+
'</div>';
}
var validateExportFilenameTimeout
@@ -355,7 +401,7 @@ RED.clipboard = (function() {
}
}
function importNodes(mode) {
function showImportNodes(mode) {
if (disabled) {
return;
}
@@ -441,6 +487,8 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-download").hide();
$("#red-ui-clipboard-dialog-import-conflict").hide();
$("#red-ui-clipboard-dialog-ok").button("disable");
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
@@ -474,8 +522,16 @@ RED.clipboard = (function() {
},100)
}
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
dialog.dialog("option","title",RED._("clipboard.importNodes"))
.dialog("option","width",700)
.dialog("open");
popover = RED.popover.create({
target: $("#red-ui-clipboard-dialog-import-text"),
trigger: "manual",
@@ -484,7 +540,7 @@ RED.clipboard = (function() {
});
}
function exportNodes(mode) {
function showExportNodes(mode) {
if (disabled) {
return;
}
@@ -583,6 +639,7 @@ RED.clipboard = (function() {
nodes = [];
selection.forEach(function(n) {
nodes.push(n);
nodes = nodes.concat(RED.nodes.groups(n.id));
nodes = nodes.concat(RED.nodes.filterNodes({z:n.id}));
});
} else {
@@ -592,7 +649,8 @@ RED.clipboard = (function() {
nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'}));
} else if (type === 'red-ui-clipboard-dialog-export-rng-flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.filterNodes({z:activeWorkspace});
nodes = RED.nodes.groups(activeWorkspace);
nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
@@ -619,6 +677,8 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-ok").hide();
$("#red-ui-clipboard-dialog-cancel").hide();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-import-conflict").hide();
var selection = RED.workspaces.selection();
if (selection.length > 0) {
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
@@ -637,12 +697,23 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-export-fmt-mini").trigger("click");
}
tabs.activateTab("red-ui-clipboard-dialog-export-tab-"+mode);
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
dialog.dialog("option","title",RED._("clipboard.exportNodes"))
.dialog("option","width",700)
.dialog("open");
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").show();
$("#red-ui-clipboard-dialog-download").show();
$("#red-ui-clipboard-dialog-import-conflict").hide();
}
@@ -702,6 +773,12 @@ RED.clipboard = (function() {
// representation or null
return null;
}
if (value.type === 'bigint') {
return value.data.toString();
}
if (value.type === 'undefined') {
return undefined;
}
}
}
return value;
@@ -726,17 +803,315 @@ RED.clipboard = (function() {
}
return result;
}
function importNodes(nodesStr,addFlow) {
var newNodes = nodesStr;
if (typeof nodesStr === 'string') {
try {
nodesStr = nodesStr.trim();
if (nodesStr.length === 0) {
return;
}
newNodes = JSON.parse(nodesStr);
} catch(err) {
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
e.code = "NODE_RED";
throw e;
}
}
var importOptions = {generateIds: false, addFlow: addFlow};
try {
RED.view.importNodes(newNodes, importOptions);
} catch(error) {
// Thrown for import_conflict
confirmImport(error.importConfig, newNodes, importOptions);
}
}
function confirmImport(importConfig,importNodes,importOptions) {
var notification = RED.notify("<p>"+RED._("clipboard.import.conflictNotification1")+"</p>",{
type: "info",
fixed: true,
buttons: [
{text: RED._("common.label.cancel"), click: function() { notification.close(); }},
{text: RED._("clipboard.import.viewNodes"), click: function() {
notification.close();
showImportConflicts(importConfig,importNodes,importOptions);
}},
{text: RED._("clipboard.import.importCopy"), click: function() {
notification.close();
// generateIds=true to avoid conflicts
// and default to the 'old' behaviour around matching
// config nodes and subflows
importOptions.generateIds = true;
RED.view.importNodes(importNodes, importOptions);
}}
]
})
}
function showImportConflicts(importConfig,importNodes,importOptions) {
pendingImportConfig = {
importConfig: importConfig,
importNodes: importNodes,
importOptions: importOptions
}
var id,node;
var treeData = [];
var container;
var addedHeader = false;
for (id in importConfig.subflows) {
if (importConfig.subflows.hasOwnProperty(id)) {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.subflows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
node = importConfig.subflows[id];
var isConflicted = importConfig.conflicted[node.id];
var isSelected = !isConflicted;
var elements = getNodeElement(node, isConflicted, isSelected );
container = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
class: isSelected?"":"disabled",
deferBuild: true,
children: []
}
treeData.push(container);
if (importConfig.zMap[id]) {
importConfig.zMap[id].forEach(function(node) {
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
container.children.push({
id: node.id,
gutter: childElements.gutter.element,
element: childElements.element,
class: isSelected?"":"disabled"
})
});
}
}
}
addedHeader = false;
for (id in importConfig.tabs) {
if (importConfig.tabs.hasOwnProperty(id)) {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.flows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
node = importConfig.tabs[id];
var isConflicted = importConfig.conflicted[node.id];
var isSelected = true;
var elements = getNodeElement(node, isConflicted, isSelected);
container = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
icon: "red-ui-icons red-ui-icons-flow",
deferBuild: true,
class: isSelected?"":"disabled",
children: []
}
treeData.push(container);
if (importConfig.zMap[id]) {
importConfig.zMap[id].forEach(function(node) {
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
container.children.push({
id: node.id,
gutter: childElements.gutter.element,
element: childElements.element,
class: isSelected?"":"disabled"
})
// console.log(" ["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
});
}
}
}
addedHeader = false;
var extraNodes = [];
importConfig.all.forEach(function(node) {
if (node.type !== "tab" && node.type !== "subflow" && !importConfig.tabs[node.z] && !importConfig.subflows[node.z]) {
var isConflicted = importConfig.conflicted[node.id];
var isSelected = !isConflicted || !importConfig.configs[node.id];
var elements = getNodeElement(node, isConflicted, isSelected);
var item = {
id: node.id,
gutter: elements.gutter.element,
element: elements.element,
class: isSelected?"":"disabled"
}
if (importConfig.configs[node.id]) {
extraNodes.push(item);
} else {
if (!addedHeader) {
treeData.push({gutter:$('<span data-i18n="menu.label.nodes"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
}
treeData.push(item);
}
// console.log("["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
}
})
if (extraNodes.length > 0) {
treeData.push({gutter:$('<span data-i18n="menu.label.displayConfig"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
addedHeader = true;
treeData = treeData.concat(extraNodes);
}
dialogContainer.empty();
dialogContainer.append($(importConflictsDialog));
var nodeList = $("#red-ui-clipboard-dialog-import-conflicts-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: treeData
})
dialogContainer.i18n();
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 600) {
dialogHeight = 400 - (600 - winHeight);
}
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
$("#red-ui-clipboard-dialog-ok").hide();
$("#red-ui-clipboard-dialog-cancel").show();
$("#red-ui-clipboard-dialog-export").hide();
$("#red-ui-clipboard-dialog-download").hide();
$("#red-ui-clipboard-dialog-import-conflict").show();
dialog.dialog("option","title",RED._("clipboard.importNodes"))
.dialog("option","width",500)
.dialog( "open" );
}
function getNodeElement(n, isConflicted, isSelected, parent) {
var element;
if (n.type === "tab") {
element = getFlowLabel(n, isSelected);
} else {
element = getNodeLabel(n, isConflicted, isSelected);
}
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
controls.on("click", function(evt) { evt.stopPropagation(); });
if (isConflicted && !parent) {
var cb = $('<label><input '+(isSelected?'':'disabled ')+'type="checkbox" data-node-id="'+n.id+'"> <span data-i18n="clipboard.import.replace"></span></label>').appendTo(controls);
if (n.type === "tab" || (n.type !== "subflow" && n.hasOwnProperty("x") && n.hasOwnProperty("y"))) {
cb.hide();
}
}
return {
element: element,
gutter: getGutter(n, isSelected, parent)
}
}
function getGutter(n, isSelected, parent) {
var span = $("<label>",{class:"red-ui-clipboard-dialog-import-conflicts-gutter"});
var cb = $('<input data-node-id="'+n.id+'" type="checkbox" '+(isSelected?"checked":"")+'>').appendTo(span);
if (parent) {
cb.attr("disabled",true);
parent.addChild(cb);
}
span.on("click", function(evt) {
evt.stopPropagation();
})
cb.on("change", function(evt) {
var state = this.checked;
span.parent().toggleClass("disabled",!!!state);
span.parent().find('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').attr("disabled",!!!state);
childItems.forEach(function(c) {
c.attr("checked",state);
c.trigger("change");
});
})
var childItems = [];
var checkbox = {
addChild: function(c) {
childItems.push(c);
}
}
return {
cb: checkbox,
element: span
}
}
function getNodeLabelText(n) {
var label = n.name || n.type+": "+n.id;
if (n._def.label) {
try {
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
} catch(err) {
console.log("Definition error: "+n.type+".label",err);
}
}
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
return label;
}
function getFlowLabel(n) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
var label = (typeof n === "string")? n : n.label;
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
contentDiv.text(label);
// A conflicted flow should not be imported by default.
return div;
}
function getNodeLabel(n, isConflicted) {
n = JSON.parse(JSON.stringify(n));
n._def = RED.nodes.getType(n.type) || {};
if (n._def) {
n._ = n._def._;
}
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html(n.type)
}
return div;
}
return {
init: function() {
setupDialogs();
$('<input type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
RED.actions.add("core:show-export-dialog",exportNodes);
RED.actions.add("core:show-import-dialog",importNodes);
RED.actions.add("core:show-export-dialog",showExportNodes);
RED.actions.add("core:show-import-dialog",showImportNodes);
RED.actions.add("core:show-library-export-dialog",function() { exportNodes('library') });
RED.actions.add("core:show-library-import-dialog",function() { importNodes('library') });
RED.actions.add("core:show-library-export-dialog",function() { showExportNodes('library') });
RED.actions.add("core:show-library-import-dialog",function() { showImportNodes('library') });
RED.actions.add("core:show-examples-import-dialog",function() { showImportNodes('examples') });
RED.events.on("editor:open",function() { disabled = true; });
RED.events.on("editor:close",function() { disabled = false; });
@@ -770,7 +1145,7 @@ RED.clipboard = (function() {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
RED.view.importNodes(data);
importNodes(data);
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
var files = event.originalEvent.dataTransfer.files;
if (files.length === 1) {
@@ -778,7 +1153,7 @@ RED.clipboard = (function() {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
RED.view.importNodes(e.target.result);
importNodes(e.target.result);
};
})(file);
reader.readAsText(file);
@@ -789,8 +1164,8 @@ RED.clipboard = (function() {
});
},
import: importNodes,
export: exportNodes,
import: showImportNodes,
export: showExportNodes,
copyText: copyText
}
})();

View File

@@ -27,26 +27,26 @@
this.partialFlag = false;
this.stateValue = 0;
var initialState = this.element.prop('checked');
this.options = [
this.states = [
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-square-o"></i></span>').appendTo(this.uiElement),
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-check-square-o"></i></span>').appendTo(this.uiElement),
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-minus-square-o"></i></span>').appendTo(this.uiElement)
];
if (initialState) {
this.options[1].show();
this.states[1].show();
} else {
this.options[0].show();
this.states[0].show();
}
this.element.on("change", function() {
if (this.checked) {
that.options[0].hide();
that.options[1].show();
that.options[2].hide();
that.states[0].hide();
that.states[1].show();
that.states[2].hide();
} else {
that.options[1].hide();
that.options[0].show();
that.options[2].hide();
that.states[1].hide();
that.states[0].show();
that.states[2].hide();
}
var isChecked = this.checked;
that.children.forEach(function(child) {
@@ -106,17 +106,17 @@
var trueState = this.partialFlag||state;
this.element.prop('checked',trueState);
if (state === true) {
this.options[0].hide();
this.options[1].show();
this.options[2].hide();
this.states[0].hide();
this.states[1].show();
this.states[2].hide();
} else if (state === false) {
this.options[2].hide();
this.options[1].hide();
this.options[0].show();
this.states[2].hide();
this.states[1].hide();
this.states[0].show();
} else if (state === null) {
this.options[0].hide();
this.options[1].hide();
this.options[2].show();
this.states[0].hide();
this.states[1].hide();
this.states[2].show();
}
if (!suppressEvent) {
this.element.trigger('change',null);

View File

@@ -0,0 +1,209 @@
RED.colorPicker = (function() {
function create(options) {
var color = options.value;
var id = options.id;
var colorPalette = options.palette || [];
var width = options.cellWidth || 30;
var height = options.cellHeight || 30;
var margin = options.cellMargin || 2;
var perRow = options.cellPerRow || 6;
var container = $("<div>",{style:"display:inline-block"});
var colorHiddenInput = $("<input/>", { id: id, type: "hidden", value: color }).appendTo(container);
var opacityHiddenInput = $("<input/>", { id: id+"-opacity", type: "hidden", value: options.hasOwnProperty('opacity')?options.opacity:"1" }).appendTo(container);
var colorButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(container);
$('<i class="fa fa-caret-down"></i>').appendTo(colorButton);
var colorDispContainer = $('<div>',{class:"red-ui-search-result-node"}).appendTo(colorButton);
$('<div>',{class:"red-ui-color-picker-cell-none"}).appendTo(colorDispContainer);
var colorDisp = $('<div>',{class:"red-ui-color-picker-swatch"}).appendTo(colorDispContainer);
var refreshDisplay = function(color) {
if (color === "none") {
colorDisp.addClass('red-ui-color-picker-cell-none').css({
"background-color": "",
opacity: 1
});
colorDispContainer.css({
"border-color":""
})
} else {
var opacity = parseFloat(opacityHiddenInput.val())
colorDisp.removeClass('red-ui-color-picker-cell-none').css({
"background-color": color,
"opacity": opacity
});
var border = RED.utils.getDarkerColor(color);
if (border[0] === '#') {
border += Math.round(255*Math.floor(opacity*100)/100).toString(16);
} else {
border = "";
}
colorDispContainer.css({
"border-color": border
})
}
if (options.hasOwnProperty('opacity')) {
$(".red-ui-color-picker-opacity-slider-overlay").css({
"background-image": "linear-gradient(90deg, transparent 0%, "+color+" 100%)"
})
}
}
colorButton.on("click", function (e) {
var numColors = colorPalette.length;
var picker = $("<div/>", {
class: "red-ui-color-picker"
}).css({
width: ((width+margin+margin)*perRow)+"px",
height: Math.ceil(numColors/perRow)*(height+margin+margin)+"+px"
});
var count = 0;
var row = null;
row = $("<div/>").appendTo(picker);
var colorInput = $('<input>',{
type:"text",
value:colorHiddenInput.val()
}).appendTo(row);
var focusTarget = colorInput;
colorInput.on("change", function (e) {
var color = colorInput.val();
colorHiddenInput.val(color).trigger('change');
refreshDisplay(color);
});
// if (options.hasOwnProperty('opacity')) {
// var sliderContainer = $("<div>",{class:"red-ui-color-picker-opacity-slider"
// }
if (options.none) {
row = $("<div/>").appendTo(picker);
var button = $("<button/>", {
class:"red-ui-color-picker-cell red-ui-color-picker-cell-none"
}).css({
width: width+"px",
height: height+"px",
margin: margin+"px"
}).appendTo(row);
button.on("click", function (e) {
e.preventDefault();
colorInput.val("none");
colorInput.trigger("change");
});
}
colorPalette.forEach(function (col) {
if ((count % perRow) == 0) {
row = $("<div/>").appendTo(picker);
}
var button = $("<button/>", {
class:"red-ui-color-picker-cell"
}).css({
width: width+"px",
height: height+"px",
margin: margin+"px",
backgroundColor: col,
"border-color": RED.utils.getDarkerColor(col)
}).appendTo(row);
button.on("click", function (e) {
e.preventDefault();
// colorPanel.hide();
colorInput.val(col);
colorInput.trigger("change");
});
count++;
});
if (options.none || options.hasOwnProperty('opacity')) {
row = $("<div/>").appendTo(picker);
// if (options.none) {
// var button = $("<button/>", {
// class:"red-ui-color-picker-cell red-ui-color-picker-cell-none"
// }).css({
// width: width+"px",
// height: height+"px",
// margin: margin+"px"
// }).appendTo(row);
// button.on("click", function (e) {
// e.preventDefault();
// colorPanel.hide();
// selector.val("none");
// selector.trigger("change");
// });
// }
if (options.hasOwnProperty('opacity')) {
var sliderContainer = $("<div>",{class:"red-ui-color-picker-opacity-slider"}).appendTo(row);
sliderContainer.on("mousedown", function(evt) {
if (evt.target === sliderHandle[0]) {
return;
}
var v = evt.offsetX/sliderContainer.width();
sliderHandle.css({
left: ( v*(sliderContainer.width() - sliderHandle.outerWidth()))+"px"
});
v = Math.floor(100*v)
opacityHiddenInput.val(v/100)
opacityLabel.text(v+"%");
refreshDisplay(colorHiddenInput.val());
})
$("<div>",{class:"red-ui-color-picker-opacity-slider-overlay"}).appendTo(sliderContainer);
var sliderHandle = $("<div>",{class:"red-ui-color-picker-opacity-slider-handle red-ui-button red-ui-button-small"}).appendTo(sliderContainer).draggable({
containment: "parent",
axis: "x",
drag: function( event, ui ) {
var v = Math.max(0,ui.position.left/($(this).parent().width()-$(this).outerWidth()));
// Odd bug that if it is loaded with a non-0 value, the first time
// it is dragged it ranges -1 to 99. But every other time, its 0 to 100.
// The Math.max above makes the -1 disappear. The follow hack ensures
// it always maxes out at a 100, at the cost of not allowing 99% exactly.
v = Math.floor(100*v)
if ( v === 99 ) {
v = 100;
}
// console.log("uip",ui.position.left);
opacityHiddenInput.val(v/100)
opacityLabel.text(v+"%");
refreshDisplay(colorHiddenInput.val());
}
});
var opacityLabel = $('<small></small>').appendTo(row);
setTimeout(function() {
sliderHandle.css({
left: (parseFloat(opacityHiddenInput.val())*(sliderContainer.width() - sliderHandle.outerWidth()))+"px"
})
opacityLabel.text(Math.floor(opacityHiddenInput.val()*100)+"%");
},50);
}
}
var colorPanel = RED.popover.panel(picker);
setTimeout(function() {
refreshDisplay(colorHiddenInput.val())
},50);
colorPanel.show({
target: colorButton,
onclose: function() {
colorButton.focus();
}
})
if (focusTarget) {
focusTarget.focus();
}
});
setTimeout(function() {
refreshDisplay(colorHiddenInput.val())
},50);
return container;
}
return {
create: create
}
})();

View File

@@ -33,7 +33,7 @@
* methods:
* - addItem(itemData)
* - insertItemAt : function(data,index) - add an item at the specified index
* - removeItem(itemData)
* - removeItem(itemData, detach) - remove the item. Optionally detach to preserve any event handlers on the item's label
* - getItemAt(index)
* - indexOf(itemData)
* - width(width)
@@ -91,6 +91,9 @@
if (v!=="auto" && v!=="") {
that.topContainer.css(s,v);
that.uiContainer.css(s,"0");
if (s === "top" && that.options.header) {
that.uiContainer.css(s,"20px")
}
that.element.css(s,'auto');
}
})
@@ -183,7 +186,7 @@
if (this.options.resizeItem) {
var that = this;
this.element.children().each(function(i) {
that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i);
that.options.resizeItem($(this).children(".red-ui-editableList-item-content"),i);
});
}
},
@@ -223,7 +226,7 @@
var items = this.element.children();
var that = this;
items.sort(function(A,B) {
return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data'));
return that.activeSort($(A).children(".red-ui-editableList-item-content").data('data'),$(B).children(".red-ui-editableList-item-content").data('data'));
});
$.each(items,function(idx,li) {
that.element.append(li);
@@ -259,28 +262,6 @@
var that = this;
data = data || {};
var li = $('<li>');
var added = false;
if (this.activeSort) {
var items = this.items();
var skip = false;
items.each(function(i,el) {
if (added) { return }
var itemData = el.data('data');
if (that.activeSort(data,itemData) < 0) {
li.insertBefore(el.closest("li"));
added = true;
}
});
}
if (!added) {
if (index <= 0) {
li.prependTo(this.element);
} else if (index > that.element.children().length-1) {
li.appendTo(this.element);
} else {
li.insertBefore(this.element.children().eq(index));
}
}
var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
row.data('data',data);
if (this.options.sortable === true) {
@@ -303,9 +284,31 @@
});
});
}
var added = false;
if (this.activeSort) {
var items = this.items();
var skip = false;
items.each(function(i,el) {
if (added) { return }
var itemData = el.data('data');
if (that.activeSort(data,itemData) < 0) {
li.insertBefore(el.closest("li"));
added = true;
}
});
}
if (!added) {
if (index <= 0) {
li.prependTo(this.element);
} else if (index > that.element.children().length-1) {
li.appendTo(this.element);
} else {
li.insertBefore(this.element.children().eq(index));
}
}
if (this.options.addItem) {
var index = that.element.children().length-1;
setTimeout(function() {
// setTimeout(function() {
that.options.addItem(row,index,data);
if (that.activeFilter) {
try {
@@ -321,7 +324,7 @@
that.uiContainer.scrollTop(that.element.height());
},0);
}
},0);
// },0);
}
},
addItem: function(data) {
@@ -332,17 +335,21 @@
this.addItem(items[i]);
}
},
removeItem: function(data) {
removeItem: function(data,detach) {
var items = this.element.children().filter(function(f) {
return data === $(this).find(".red-ui-editableList-item-content").data('data');
return data === $(this).children(".red-ui-editableList-item-content").data('data');
});
items.remove();
if (detach) {
items.detach();
} else {
items.remove();
}
if (this.options.removeItem) {
this.options.removeItem(data);
}
},
items: function() {
return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); });
return this.element.children().map(function(i) { return $(this).children(".red-ui-editableList-item-content"); });
},
empty: function() {
this.element.empty();
@@ -365,14 +372,14 @@
},
show: function(item) {
var items = this.element.children().filter(function(f) {
return item === $(this).find(".red-ui-editableList-item-content").data('data');
return item === $(this).children(".red-ui-editableList-item-content").data('data');
});
if (items.length > 0) {
this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top)
}
},
getItem: function(li) {
var el = li.find(".red-ui-editableList-item-content");
var el = li.children(".red-ui-editableList-item-content");
if (el.length) {
return el.data('data');
} else {

View File

@@ -158,7 +158,7 @@ RED.menu = (function() {
activeMenu = null;
topMenu.hide();
});
$(".red-ui-menu").hide();
$(".red-ui-menu.red-ui-menu-dropdown").hide();
topMenu.show();
}
})

View File

@@ -29,6 +29,10 @@ RED.panels = (function() {
if (!vertical) {
container.addClass("red-ui-panels-horizontal");
}
$(children[0]).addClass("red-ui-panel");
$(children[1]).addClass("red-ui-panel");
var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]);
var startPosition;
var panelSizes = [];
@@ -52,11 +56,11 @@ RED.panels = (function() {
var newSizes = [panelSizes[0]+delta,panelSizes[1]-delta];
if (vertical) {
$(children[0]).height(newSizes[0]);
$(children[1]).height(newSizes[1]);
// $(children[1]).height(newSizes[1]);
ui.position.top -= delta;
} else {
$(children[0]).width(newSizes[0]);
$(children[1]).width(newSizes[1]);
// $(children[1]).width(newSizes[1]);
ui.position.left -= delta;
}
if (options.resize) {
@@ -71,6 +75,9 @@ RED.panels = (function() {
var panel = {
ratio: function(ratio) {
if (ratio === undefined) {
return panelRatio;
}
panelRatio = ratio;
modifiedSizes = true;
if (ratio === 0 || ratio === 1) {
@@ -99,10 +106,10 @@ RED.panels = (function() {
panelSizes = [topPanelSize,bottomPanelSize];
if (vertical) {
$(children[0]).outerHeight(panelSizes[0]);
$(children[1]).outerHeight(panelSizes[1]);
// $(children[1]).outerHeight(panelSizes[1]);
} else {
$(children[0]).outerWidth(panelSizes[0]);
$(children[1]).outerWidth(panelSizes[1]);
// $(children[1]).outerWidth(panelSizes[1]);
}
}
if (options.resize) {

View File

@@ -84,6 +84,7 @@ RED.popover = (function() {
var targetHeight = target.outerHeight();
var divHeight = div.height();
var divWidth = div.width();
var paddingRight = 10;
var viewportTop = $(window).scrollTop();
var viewportLeft = $(window).scrollLeft();
@@ -105,7 +106,7 @@ RED.popover = (function() {
d = "right";
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
} else if (left+divWidth > viewportRight) {
} else if (left+divWidth+paddingRight > viewportRight) {
d = "left";
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
@@ -136,6 +137,23 @@ RED.popover = (function() {
closePopup(true);
});
}
if (trigger === 'hover' && options.interactive) {
div.on('mouseenter', function(e) {
clearTimeout(timer);
active = true;
})
div.on('mouseleave', function(e) {
if (timer) {
clearTimeout(timer);
}
if (active) {
timer = setTimeout(function() {
active = false;
closePopup();
},delay.hide);
}
})
}
if (instant) {
div.show();
} else {
@@ -163,8 +181,10 @@ RED.popover = (function() {
if (trigger === 'hover') {
target.on('mouseenter',function(e) {
clearTimeout(timer);
active = true;
timer = setTimeout(openPopup,delay.show);
if (!active) {
active = true;
timer = setTimeout(openPopup,delay.show);
}
});
target.on('mouseleave disabled', function(e) {
if (timer) {
@@ -259,6 +279,85 @@ RED.popover = (function() {
delay: { show: 750, hide: 50 }
});
},
menu: function(options) {
var list = $('<ul class="red-ui-menu"></ul>');
if (options.style === 'compact') {
list.addClass("red-ui-menu-compact");
}
var menuOptions = options.options || [];
var first;
menuOptions.forEach(function(opt) {
var item = $('<li>').appendTo(list);
var link = $('<a href="#"></a>').text(opt.label).appendTo(item);
link.on("click", function(evt) {
evt.preventDefault();
if (opt.onselect) {
opt.onselect();
}
menu.hide();
})
if (!first) { first = link}
})
var container = RED.popover.panel(list);
var menu = {
show: function(opts) {
$(document).on("keydown.red-ui-menu", function(evt) {
var currentItem = list.find(":focus").parent();
if (evt.keyCode === 40) {
evt.preventDefault();
// DOWN
if (currentItem.length > 0) {
if (currentItem.index() === menuOptions.length-1) {
console.log("WARP TO TOP")
// Wrap to top of list
list.children().first().children().first().focus();
} else {
console.log("GO DOWN ONE")
currentItem.next().children().first().focus();
}
} else {
list.children().first().children().first().focus();
}
} else if (evt.keyCode === 38) {
evt.preventDefault();
// UP
if (currentItem.length > 0) {
if (currentItem.index() === 0) {
console.log("WARP TO BOTTOM")
// Wrap to bottom of list
list.children().last().children().first().focus();
} else {
console.log("GO UP ONE")
currentItem.prev().children().first().focus();
}
} else {
list.children().last().children().first().focus();
}
} else if (evt.keyCode === 27) {
// ESCAPE
evt.preventDefault();
menu.hide(true);
}
evt.stopPropagation();
})
opts.onclose = function() {
$(document).off("keydown.red-ui-menu");
if (options.onclose) {
options.onclose(true);
}
}
container.show(opts);
},
hide: function(cancelled) {
$(document).off("keydown.red-ui-menu");
container.hide(options.disposeOnClose);
if (options.onclose) {
options.onclose(cancelled);
}
}
}
return menu;
},
panel: function(content) {
var panel = $('<div class="red-ui-editor-dialog red-ui-popover-panel"></div>');
panel.css({ display: "none" });
@@ -266,18 +365,22 @@ RED.popover = (function() {
content.appendTo(panel);
var closeCallback;
function hide() {
function hide(dispose) {
$(document).off("mousedown.red-ui-popover-panel-close");
$(document).off("keydown.red-ui-popover-panel-close");
panel.hide();
panel.css({
height: "auto"
});
panel.remove();
if (dispose !== false) {
panel.remove();
}
}
function show(options) {
var closeCallback = options.onclose;
var target = options.target;
var align = options.align || "left";
var align = options.align || "right";
var offset = options.offset || [0,0];
var pos = target.offset();
var targetWidth = target.width();
@@ -285,7 +388,7 @@ RED.popover = (function() {
var panelHeight = panel.height();
var panelWidth = panel.width();
var top = (targetHeight+pos.top);
var top = (targetHeight+pos.top) + offset[1];
if (top+panelHeight > $(window).height()) {
top -= (top+panelHeight)-$(window).height() + 5;
}
@@ -293,25 +396,35 @@ RED.popover = (function() {
panelHeight.height(panelHeight+top)
top = 0;
}
if (align === "left") {
if (align === "right") {
panel.css({
top: top+"px",
left: (pos.left)+"px",
left: (pos.left+offset[0])+"px",
});
} else if(align === "right") {
} else if (align === "left") {
panel.css({
top: top+"px",
left: (pos.left-panelWidth)+"px",
left: (pos.left-panelWidth+offset[0])+"px",
});
}
panel.slideDown(100);
$(document).on("keydown.red-ui-popover-panel-close", function(event) {
if (event.keyCode === 27) {
// ESCAPE
if (closeCallback) {
closeCallback();
}
hide(options.dispose);
}
});
$(document).on("mousedown.red-ui-popover-panel-close", function(event) {
if(!$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) {
if (closeCallback) {
closeCallback();
}
hide();
hide(options.dispose);
}
// if ($(event.target).closest(target).length) {
// event.preventDefault();

View File

@@ -38,12 +38,17 @@
this.element.addClass("red-ui-searchBox-input");
this.uiContainer = this.element.wrap("<div>").parent();
this.uiContainer.addClass("red-ui-searchBox-container");
if (this.options.style === "compact") {
this.uiContainer.addClass("red-ui-searchBox-compact");
}
if (this.element.parents("form").length === 0) {
var form = this.element.wrap("<form>").parent();
form.addClass("red-ui-searchBox-form");
}
$('<i class="fa fa-search"></i>').prependTo(this.uiContainer);
this.clearButton = $('<a href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer);
this.clearButton = $('<a class="red-ui-searchBox-clear" href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer);
this.clearButton.on("click",function(e) {
e.preventDefault();
that.element.val("");
@@ -51,6 +56,62 @@
that.element.trigger("focus");
});
if (this.options.options) {
this.uiContainer.addClass("red-ui-searchBox-has-options");
this.optsButton = $('<a class="red-ui-searchBox-opts" href="#"><i class="fa fa-caret-down"></i></a>').appendTo(this.uiContainer);
var menuShown = false;
this.optsMenu = RED.popover.menu({
style: this.options.style,
options: this.options.options.map(function(opt) {
return {
label: opt.label,
onselect: function() {
that.element.val(opt.value+" ");
that._change(opt.value,true);
}
}
}),
onclose: function(cancelled) {
menuShown = false;
that.element.trigger("focus");
},
disposeOnClose: false
});
var showMenu = function() {
menuShown = true;
that.optsMenu.show({
target: that.optsButton,
align: "left",
offset: [that.optsButton.width()-2,-1],
dispose: false
})
}
this.optsButton.on("click",function(e) {
e.preventDefault();
if (!menuShown) {
showMenu();
} else {
// TODO: This doesn't quite work because the panel's own
// mousedown handler triggers a close before this click
// handler fires.
that.optsMenu.hide(true);
}
});
this.optsButton.on("keydown",function(e) {
if (!menuShown && e.keyCode === 40) {
//DOWN
showMenu();
}
});
this.element.on("keydown",function(e) {
if (!menuShown && e.keyCode === 40) {
//DOWN
showMenu();
}
});
}
this.resultCount = $('<span>',{class:"red-ui-searchBox-resultCount hide"}).appendTo(this.uiContainer);
this.element.val("");
@@ -58,6 +119,9 @@
if (evt.keyCode === 27) {
that.element.val("");
}
if (evt.keyCode === 13) {
evt.preventDefault();
}
})
this.element.on("keyup",function(evt) {
that._change($(this).val());

View File

@@ -29,7 +29,7 @@ RED.tabs = (function() {
var currentTabWidth;
var currentActiveTabWidth = 0;
var collapsibleMenu;
var preferredOrder = options.order;
var ul = options.element || $("#"+options.id);
var wrapper = ul.wrap( "<div>" ).parent();
var scrollContainer = ul.wrap( "<div>" ).parent();
@@ -132,11 +132,11 @@ RED.tabs = (function() {
activateTab(id);
}
};
if (tabs[id].pinned) {
pinnedOptions.push(opt);
} else {
// if (tabs[id].pinned) {
// pinnedOptions.push(opt);
// } else {
options.push(opt);
}
// }
});
options = pinnedOptions.concat(options);
collapsibleMenu = RED.menu.init({options: options});
@@ -153,7 +153,7 @@ RED.tabs = (function() {
if (collapsibleMenu.is(":visible")) {
$(document).off("click.red-ui-tabmenu");
} else {
$(".red-ui-menu").hide();
$(".red-ui-menu.red-ui-menu-dropdown").hide();
$(document).on("click.red-ui-tabmenu", function(evt) {
$(document).off("click.red-ui-tabmenu");
collapsibleMenu.hide();
@@ -363,23 +363,39 @@ RED.tabs = (function() {
var tabWidth;
if (options.collapsible) {
var availableCount = collapsedButtonsRow.children().length;
var visibleCount = collapsedButtonsRow.children(":visible").length;
tabWidth = width - collapsedButtonsRow.width()-10;
if (tabWidth < 198) {
var delta = 198 - tabWidth;
var maxTabWidth = 198;
var minTabWidth = 80;
if (tabWidth <= minTabWidth || (tabWidth < maxTabWidth && visibleCount > 5)) {
// The tab is too small. Hide the next button to make room
// Start at the end of the button row, -1 for the menu button
var b = collapsedButtonsRow.find("a:last").prev();
var index = collapsedButtonsRow.children().length - 2;
// Work backwards to find the first visible button
while (b.is(":not(:visible)")) {
b = b.prev();
index--;
}
if (!b.hasClass("red-ui-tab-link-button-pinned")) {
// If it isn't a pinned button, hide it to get the room
if (tabWidth <= minTabWidth || visibleCount>6) {//}!b.hasClass("red-ui-tab-link-button-pinned")) {
b.hide();
}
tabWidth = width - collapsedButtonsRow.width()-10;
tabWidth = Math.max(minTabWidth,width - collapsedButtonsRow.width()-10);
} else {
var space = width - 198 - collapsedButtonsRow.width();
if (visibleCount !== availableCount) {
if (visibleCount < 6) {
tabWidth = minTabWidth;
} else {
tabWidth = maxTabWidth;
}
}
var space = width - tabWidth - collapsedButtonsRow.width();
if (space > 40) {
collapsedButtonsRow.find("a:not(:visible):first").show();
tabWidth = width - collapsedButtonsRow.width()-10;
}
tabWidth = width - collapsedButtonsRow.width()-10;
}
tabs.css({width:tabWidth});
@@ -469,7 +485,7 @@ RED.tabs = (function() {
}
}
return {
var tabAPI = {
addTab: function(tab,targetIndex) {
if (options.onselect) {
var selection = ul.find("li.red-ui-tab.selected");
@@ -531,11 +547,93 @@ RED.tabs = (function() {
evt.preventDefault();
activateTab(tab.id);
});
pinnedLink.data("tabId",tab.id)
if (tab.pinned) {
pinnedLink.addClass("red-ui-tab-link-button-pinned");
pinnedTabsCount++;
}
RED.popover.tooltip($(pinnedLink), tab.name, tab.action);
if (options.onreorder) {
var pinnedLinkIndex;
var pinnedLinks = [];
var startPinnedIndex;
pinnedLink.draggable({
distance: 10,
axis:"x",
containment: ".red-ui-tab-link-buttons",
start: function(event,ui) {
dragActive = true;
$(".red-ui-tab-link-buttons").width($(".red-ui-tab-link-buttons").width());
if (dblClickArmed) { dblClickArmed = false; return false }
collapsedButtonsRow.children().each(function(i) {
pinnedLinks[i] = {
el:$(this),
text: $(this).text(),
left: $(this).position().left,
width: $(this).width(),
menu: $(this).hasClass("red-ui-tab-link-button-menu")
};
if ($(this).is(pinnedLink)) {
pinnedLinkIndex = i;
startPinnedIndex = i;
}
});
collapsedButtonsRow.children().each(function(i) {
if (i!==pinnedLinkIndex) {
$(this).css({
position: 'absolute',
left: pinnedLinks[i].left+"px",
width: pinnedLinks[i].width+2,
transition: "left 0.3s"
});
}
})
if (!pinnedLink.hasClass('active')) {
pinnedLink.css({'zIndex':1});
}
},
drag: function(event,ui) {
ui.position.left += pinnedLinks[pinnedLinkIndex].left;
var tabCenter = ui.position.left + pinnedLinks[pinnedLinkIndex].width/2;
for (var i=0;i<pinnedLinks.length;i++) {
if (i === pinnedLinkIndex || pinnedLinks[i].menu || pinnedLinks[i].el.is(":not(:visible)")) {
continue;
}
if (tabCenter > pinnedLinks[i].left && tabCenter < pinnedLinks[i].left+pinnedLinks[i].width) {
if (i < pinnedLinkIndex) {
pinnedLinks[i].left += pinnedLinks[pinnedLinkIndex].width+8;
pinnedLinks[pinnedLinkIndex].el.detach().insertBefore(pinnedLinks[i].el);
} else {
pinnedLinks[i].left -= pinnedLinks[pinnedLinkIndex].width+8;
pinnedLinks[pinnedLinkIndex].el.detach().insertAfter(pinnedLinks[i].el);
}
pinnedLinks[i].el.css({left:pinnedLinks[i].left+"px"});
pinnedLinks.splice(i, 0, pinnedLinks.splice(pinnedLinkIndex, 1)[0]);
pinnedLinkIndex = i;
break;
}
}
},
stop: function(event,ui) {
dragActive = false;
collapsedButtonsRow.children().css({position:"relative",left:"",transition:""});
$(".red-ui-tab-link-buttons").width('auto');
pinnedLink.css({zIndex:""});
updateTabWidths();
if (startPinnedIndex !== pinnedLinkIndex) {
if (collapsibleMenu) {
collapsibleMenu.remove();
collapsibleMenu = null;
}
var newOrder = $.makeArray(collapsedButtonsRow.children().map(function() { return $(this).data('tabId');}));
tabAPI.order(newOrder);
options.onreorder(newOrder);
}
}
});
}
}
link.on("mouseup",onTabClick);
@@ -565,7 +663,7 @@ RED.tabs = (function() {
if (ul.find("li.red-ui-tab").length == 1) {
activateTab(link);
}
if (options.onreorder) {
if (options.onreorder && !options.collapsible) {
var originalTabOrder;
var tabDragIndex;
var tabElements = [];
@@ -652,6 +750,9 @@ RED.tabs = (function() {
collapsibleMenu.remove();
collapsibleMenu = null;
}
if (preferredOrder) {
tabAPI.order(preferredOrder);
}
},
removeTab: removeTab,
activateTab: activateTab,
@@ -673,10 +774,8 @@ RED.tabs = (function() {
},
selection: getSelection,
order: function(order) {
preferredOrder = order;
var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));
if (existingTabOrder.length !== order.length) {
return
}
var i;
var match = true;
for (i=0;i<order.length;i++) {
@@ -692,12 +791,41 @@ RED.tabs = (function() {
var existingTabs = ul.children().detach().each(function() {
existingTabMap[$(this).data("tabId")] = $(this);
});
var pinnedButtons = {};
if (options.collapsible) {
collapsedButtonsRow.children().detach().each(function() {
var id = $(this).data("tabId");
if (!id) {
id = "__menu__"
}
pinnedButtons[id] = $(this);
});
}
for (i=0;i<order.length;i++) {
existingTabMap[order[i]].appendTo(ul);
if (existingTabMap[order[i]]) {
existingTabMap[order[i]].appendTo(ul);
if (options.collapsible) {
pinnedButtons[order[i]].appendTo(collapsedButtonsRow);
}
delete existingTabMap[order[i]];
}
}
// Add any tabs that aren't known in the order
for (i in existingTabMap) {
if (existingTabMap.hasOwnProperty(i)) {
existingTabMap[i].appendTo(ul);
if (options.collapsible) {
pinnedButtons[i].appendTo(collapsedButtonsRow);
}
}
}
if (options.collapsible) {
pinnedButtons["__menu__"].appendTo(collapsedButtonsRow);
updateTabWidths();
}
}
}
return tabAPI;
}
return {

View File

@@ -37,8 +37,8 @@
invertState = this.options.invertState;
}
var baseClass = this.options.baseClass || "red-ui-button";
var enabledIcon = this.options.enabledIcon || "fa-check-square-o";
var disabledIcon = this.options.disabledIcon || "fa-square-o";
var enabledIcon = this.options.hasOwnProperty('enabledIcon')?this.options.enabledIcon : "fa-check-square-o";
var disabledIcon = this.options.hasOwnProperty('disabledIcon')?this.options.disabledIcon : "fa-square-o";
var enabledLabel = this.options.hasOwnProperty('enabledLabel') ? this.options.enabledLabel : RED._("editor:workspace.enabled");
var disabledLabel = this.options.hasOwnProperty('disabledLabel') ? this.options.disabledLabel : RED._("editor:workspace.disabled");
@@ -46,25 +46,41 @@
this.element.on("focus", function() {
that.button.focus();
});
this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"><i class="fa"></i> <span></span></button>');
this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"></button>');
if (enabledLabel || disabledLabel) {
this.buttonLabel = $("<span>").appendTo(this.button);
}
if (this.options.class) {
this.button.addClass(this.options.class)
}
this.element.after(this.button);
this.buttonIcon = this.button.find("i");
this.buttonLabel = this.button.find("span");
if (enabledIcon && disabledIcon) {
this.buttonIcon = $('<i class="fa"></i>').prependTo(this.button);
}
// Quick hack to find the maximum width of the button
this.button.addClass("selected");
this.buttonIcon.addClass(enabledIcon);
this.buttonLabel.text(enabledLabel);
if (this.buttonIcon) {
this.buttonIcon.addClass(enabledIcon);
}
if (this.buttonLabel) {
this.buttonLabel.text(enabledLabel);
}
var width = this.button.width();
this.button.removeClass("selected");
this.buttonIcon.removeClass(enabledIcon);
that.buttonIcon.addClass(disabledIcon);
that.buttonLabel.text(disabledLabel);
if (this.buttonIcon) {
this.buttonIcon.removeClass(enabledIcon);
that.buttonIcon.addClass(disabledIcon);
}
if (this.buttonLabel) {
that.buttonLabel.text(disabledLabel);
}
width = Math.max(width,this.button.width());
this.buttonIcon.removeClass(disabledIcon);
if (this.buttonIcon) {
this.buttonIcon.removeClass(disabledIcon);
}
// Fix the width of the button so it doesn't jump around when toggled
if (width > 0) {
@@ -73,7 +89,7 @@
this.button.on("click",function(e) {
e.stopPropagation();
if (that.buttonIcon.hasClass(disabledIcon)) {
if (!that.state) {
that.element.prop("checked",!invertState);
} else {
that.element.prop("checked",invertState);
@@ -84,14 +100,24 @@
this.element.on("change", function(e) {
if ($(this).prop("checked") !== invertState) {
that.button.addClass("selected");
that.buttonIcon.addClass(enabledIcon);
that.buttonIcon.removeClass(disabledIcon);
that.buttonLabel.text(enabledLabel);
that.state = true;
if (that.buttonIcon) {
that.buttonIcon.addClass(enabledIcon);
that.buttonIcon.removeClass(disabledIcon);
}
if (that.buttonLabel) {
that.buttonLabel.text(enabledLabel);
}
} else {
that.button.removeClass("selected");
that.buttonIcon.addClass(disabledIcon);
that.buttonIcon.removeClass(enabledIcon);
that.buttonLabel.text(disabledLabel);
that.state = false;
if (that.buttonIcon) {
that.buttonIcon.addClass(disabledIcon);
that.buttonIcon.removeClass(enabledIcon);
}
if (that.buttonLabel) {
that.buttonLabel.text(disabledLabel);
}
}
})
this.element.trigger("change");

View File

@@ -27,7 +27,8 @@
*
* methods:
* - data(items) - clears existing items and replaces with new data
*
* - clearSelection - clears the selected items
* - filter(filterFunc) - filters the tree using the provided function
* events:
* - treelistselect : function(event, item) {}
* - treelistconfirm : function(event,item) {}
@@ -39,7 +40,8 @@
* label: 'Local', // label for the item
* sublabel: 'Local', // a sub-label for the item
* icon: 'fa fa-rocket', // (optional) icon for the item
* selected: true/false, // (optional) if present, display checkbox accordingly
* checkbox: true/false, // (optional) if present, display checkbox accordingly
* selected: true/false, // (optional) whether the item is selected or not
* children: [] | function(done,item) // (optional) an array of child items, or a function
* // that will call the `done` callback with an array
* // of child items
@@ -59,11 +61,11 @@
* properties and functions:
*
* item.parent - set to the parent item
* item.depth - the depth in the tree (0 == root)
* item.treeList.container
* item.treeList.label - the label element for the item
* item.treeList.depth - the depth in the tree (0 == root)
* item.treeList.parentList - the editableList instance this item is in
* item.treeList.remove() - removes the item from the tree
* item.treeList.remove(detach) - removes the item from the tree. Optionally detach to preserve any event handlers on the item's label
* item.treeList.makeLeaf(detachChildElements) - turns an element with children into a leaf node,
* removing the UI decoration etc.
* detachChildElements - any children with custom
@@ -78,8 +80,8 @@
* Optionally selects the item after adding.
* item.treeList.expand(done) - expands the parent item to show children. Optional 'done' callback.
* item.treeList.collapse() - collapse the parent item to hide children.
*
*
* item.treeList.sortChildren(sortFunction) - does a one-time sort of the children using sortFunction
* item.treeList.replaceElement(element) - replace the custom element for the item
*
*
*/
@@ -100,6 +102,8 @@
var target;
switch(evt.keyCode) {
case 13: // ENTER
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
@@ -112,6 +116,8 @@
break;
case 37: // LEFT
evt.preventDefault();
evt.stopPropagation();
if (selected.children&& selected.treeList.container.hasClass("expanded")) {
selected.treeList.collapse()
} else if (selected.parent) {
@@ -119,6 +125,8 @@
}
break;
case 38: // UP
evt.preventDefault();
evt.stopPropagation();
target = that._getPreviousSibling(selected);
if (target) {
target = that._getLastDescendant(target);
@@ -128,6 +136,8 @@
}
break;
case 39: // RIGHT
evt.preventDefault();
evt.stopPropagation();
if (selected.children) {
if (!selected.treeList.container.hasClass("expanded")) {
selected.treeList.expand()
@@ -135,6 +145,8 @@
}
break
case 40: //DOWN
evt.preventDefault();
evt.stopPropagation();
if (selected.children && Array.isArray(selected.children) && selected.children.length > 0 && selected.treeList.container.hasClass("expanded")) {
target = selected.children[0];
} else {
@@ -151,10 +163,11 @@
}
});
this._data = [];
this._items = {};
this._selected = new Set();
this._topList = $('<ol class="red-ui-treeList-list">').css({
position:'absolute',
top: 0,
top:0,
left:0,
right:0,
bottom:0
@@ -168,6 +181,9 @@
that._addSubtree(that._topList,container,item,0);
}
};
if (this.options.header) {
topListOptions.header = this.options.header;
}
if (this.options.rootSortable !== false && !!this.options.sortable) {
topListOptions.sortable = this.options.sortable;
topListOptions.connectWith = '.red-ui-treeList-sortable';
@@ -215,7 +231,7 @@
return candidates[index+1];
}
},
_addChildren: function(container,parent,children,depth) {
_addChildren: function(container,parent,children,depth,onCompleteChildren) {
var that = this;
var subtree = $('<ol class="red-ui-treeList-list">').appendTo(container).editableList({
connectWith: ".red-ui-treeList-sortable",
@@ -244,15 +260,38 @@
that._trigger("changeparent",null,evt);
});
that._trigger("sort",null,parent);
}
},
filter: parent.treeList.childFilter
});
if (!!that.options.sortable) {
subtree.addClass('red-ui-treeList-sortable');
}
for (var i=0;i<children.length;i++) {
children[i].parent = parent;
subtree.editableList('addItem',children[i])
var sliceSize = 30;
var index = 0;
var addSlice = function() {
var start = index;
for (var i=0;i<sliceSize;i++) {
index = start+i;
if (index === children.length) {
setTimeout(function() {
if (onCompleteChildren) {
onCompleteChildren();
}
},10);
return;
}
children[index].parent = parent;
subtree.editableList('addItem',children[index])
}
index++;
if (index < children.length) {
setTimeout(function() {
addSlice();
},10);
}
}
addSlice();
subtree.hide()
return subtree;
},
_fixDepths: function(parent,child) {
@@ -289,23 +328,184 @@
}
return reparentedEvent;
},
_addSubtree: function(parentList, container, item, depth) {
_initItem: function(item,depth) {
if (item.treeList) {
return;
}
var that = this;
this._items[item.id] = item;
item.treeList = {};
item.treeList.depth = depth;
item.treeList.container = container;
item.treeList.parentList = parentList;
item.treeList.remove = function() {
parentList.editableList('removeItem',item);
item.depth = depth;
item.treeList.remove = function(detach) {
if (item.treeList.parentList) {
item.treeList.parentList.editableList('removeItem',item,detach);
}
if (item.parent) {
var index = item.parent.children.indexOf(item);
item.parent.children.splice(index,1)
that._trigger("sort",null,item.parent);
}
that._selected.delete(item);
delete item.treeList;
delete that._items[item.id];
}
item.treeList.insertChildAt = function(newItem,position,select) {
newItem.parent = item;
item.children.splice(position,0,newItem);
var processChildren = function(parent,i) {
that._initItem(i,parent.depth+1)
i.parent = parent;
if (i.children && typeof i.children !== 'function') {
i.children.forEach(function(item) {
processChildren(i, item, parent.depth+2)
});
}
}
processChildren(item,newItem);
if (!item.deferBuild && item.treeList.childList) {
item.treeList.childList.editableList('insertItemAt',newItem,position)
if (select) {
setTimeout(function() {
that.select(newItem)
},100);
}
that._trigger("sort",null,item);
if (that.activeFilter) {
that.filter(that.activeFilter);
}
}
}
item.treeList.addChild = function(newItem,select) {
item.treeList.insertChildAt(newItem,item.children.length,select);
}
item.treeList.expand = function(done) {
if (!item.children) {
if (done) { done(false) }
return;
}
if (!item.treeList.container) {
item.expanded = true;
if (done) { done(false) }
return;
}
var container = item.treeList.container;
if (container.hasClass("expanded")) {
if (done) { done(false) }
return;
}
if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) {
container.addClass('built');
var childrenAdded = false;
var spinner;
var startTime = 0;
var started = Date.now();
var completeBuild = function(children) {
childrenAdded = true;
item.treeList.childList = that._addChildren(container,item,children,depth, function() {
if (done) { done(true) }
that._trigger("childrenloaded",null,item)
});
var delta = Date.now() - startTime;
if (delta < 400) {
setTimeout(function() {
item.treeList.childList.slideDown('fast');
if (spinner) {
spinner.remove();
}
},400-delta);
} else {
item.treeList.childList.slideDown('fast');
if (spinner) {
spinner.remove();
}
}
item.expanded = true;
}
if (typeof item.children === 'function') {
item.children(completeBuild,item);
} else {
delete item.deferBuild;
completeBuild(item.children);
}
if (!childrenAdded) {
startTime = Date.now();
spinner = $('<div class="red-ui-treeList-spinner">').css({
"background-position": (35+depth*20)+'px 50%'
}).appendTo(container);
}
} else {
if (that._loadingData || item.children.length > 20) {
item.treeList.childList.show();
} else {
item.treeList.childList.slideDown('fast');
}
item.expanded = true;
if (done) { done(!that._loadingData) }
}
container.addClass("expanded");
}
item.treeList.collapse = function() {
if (!item.children) {
return;
}
item.expanded = false;
if (item.treeList.container) {
if (item.children.length < 20) {
item.treeList.childList.slideUp('fast');
} else {
item.treeList.childList.hide();
}
item.treeList.container.removeClass("expanded");
}
}
item.treeList.sortChildren = function(sortFunc) {
if (!item.children) {
return;
}
item.children.sort(sortFunc);
if (item.treeList.childList) {
// Do a one-off sort of the list, which means calling sort twice:
// 1. first with the desired sort function
item.treeList.childList.editableList('sort',sortFunc);
// 2. and then with null to remove it
item.treeList.childList.editableList('sort',null);
}
}
item.treeList.replaceElement = function (element) {
if (item.element) {
if (item.treeList.container) {
$(item.element).remove();
$(element).appendTo(item.treeList.label);
var labelPaddingWidth = (item.gutter?item.gutter.width()+2:0)+(item.depth*20);
$(element).css({
width: "calc(100% - "+(labelPaddingWidth+20+(item.icon?20:0))+"px)"
})
}
item.element = element;
}
}
var label = $("<div>",{class:"red-ui-treeList-label"}).appendTo(container);
if (item.children && typeof item.children !== "function") {
item.children.forEach(function(i) {
that._initItem(i,depth+1);
})
}
},
_addSubtree: function(parentList, container, item, depth) {
var that = this;
this._initItem(item,depth);
// item.treeList = {};
// item.treeList.depth = depth;
item.treeList.container = container;
item.treeList.parentList = parentList;
var label = $("<div>",{class:"red-ui-treeList-label"});
label.appendTo(container);
item.treeList.label = label;
if (item.class) {
label.addClass(item.class);
@@ -357,6 +557,7 @@
treeListIcon.off("click.red-ui-treeList-expand");
delete item.children;
container.removeClass("expanded");
delete item.expanded;
}
item.treeList.makeParent = function(children) {
if (treeListIcon.children().length) {
@@ -385,104 +586,28 @@
})
if (!item.children) {
item.children = children||[];
item.treeList.childList = that._addChildren(container,item,item.children,depth).hide();
item.treeList.childList = that._addChildren(container,item,item.children,depth);
}
}
item.treeList.insertChildAt = function(newItem,position,select) {
newItem.parent = item;
item.children.splice(position,0,newItem);
if (!item.deferBuild) {
item.treeList.childList.editableList('insertItemAt',newItem,position)
if (select) {
setTimeout(function() {
that.select(newItem)
},100);
}
that._trigger("sort",null,item);
}
}
item.treeList.addChild = function(newItem,select) {
item.treeList.insertChildAt(newItem,item.children.length,select);
}
item.treeList.expand = function(done) {
if (!item.children) {
return;
}
if (container.hasClass("expanded")) {
if (done) { done() }
return;
}
if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) {
container.addClass('built');
var childrenAdded = false;
var spinner;
var startTime = 0;
var completeBuild = function(children) {
childrenAdded = true;
item.treeList.childList = that._addChildren(container,item,children,depth).hide();
var delta = Date.now() - startTime;
if (delta < 400) {
setTimeout(function() {
item.treeList.childList.slideDown('fast');
if (spinner) {
spinner.remove();
}
},400-delta);
} else {
item.treeList.childList.slideDown('fast');
if (spinner) {
spinner.remove();
}
}
if (done) { done() }
that._trigger("childrenloaded",null,item)
}
if (typeof item.children === 'function') {
item.children(completeBuild,item);
} else {
delete item.deferBuild;
completeBuild(item.children);
}
if (!childrenAdded) {
startTime = Date.now();
spinner = $('<div class="red-ui-treeList-spinner">').css({
"background-position": (35+depth*20)+'px 50%'
}).appendTo(container);
}
} else {
if (that._loadingData) {
item.treeList.childList.show();
} else {
item.treeList.childList.slideDown('fast');
}
if (done) { done() }
}
container.addClass("expanded");
}
item.treeList.collapse = function() {
if (!item.children) {
return;
}
item.treeList.childList.slideUp('fast');
container.removeClass("expanded");
}
var treeListIcon = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
if (item.children) {
item.treeList.makeParent();
}
if (item.hasOwnProperty('selected')) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
if (item.checkbox) {
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>');
var cb = $('<input class="red-ui-treeList-checkbox" type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
label.toggleClass("selected",item.selected);
cb.on('click', function(e) {
e.stopPropagation();
});
cb.on('change', function(e) {
item.selected = this.checked;
if (item.selected) {
that._selected.add(item);
} else {
that._selected.delete(item);
}
label.toggleClass("selected",this.checked);
that._trigger("select",e,item);
})
@@ -497,10 +622,15 @@
cb.trigger("click");
}
}
selectWrapper.appendTo(label)
} else {
label.on("click", function(e) {
that._topList.find(".selected").removeClass("selected");
if (!that.options.multi) {
that.clearSelection();
}
label.addClass("selected");
that._selected.add(item);
that._trigger("select",e,item)
})
label.on("dblclick", function(e) {
@@ -508,9 +638,30 @@
that._trigger("confirm",e,item);
}
})
item.treeList.select = function(v) {
if (!that.options.multi) {
that.clearSelection();
}
label.toggleClass("selected",v);
if (v) {
that._selected.add(item);
that._trigger("select",null,item)
} else {
that._selected.delete(item);
}
that.reveal(item);
}
}
label.toggleClass("selected",!!item.selected);
if (item.selected) {
that._selected.add(item);
}
if (item.icon) {
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
if (typeof item.icon === "string") {
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
} else {
$('<span class="red-ui-treeList-icon">').appendTo(label).append(item.icon);
}
}
if (item.hasOwnProperty('label') || item.hasOwnProperty('sublabel')) {
if (item.hasOwnProperty('label')) {
@@ -528,12 +679,13 @@
}
if (item.children) {
if (Array.isArray(item.children) && !item.deferBuild) {
item.treeList.childList = that._addChildren(container,item,item.children,depth).hide();
item.treeList.childList = that._addChildren(container,item,item.children,depth);
}
if (item.expanded) {
item.treeList.expand();
}
}
// label.appendTo(container);
},
empty: function() {
this._topList.editableList('empty');
@@ -542,6 +694,7 @@
var that = this;
if (items !== undefined) {
this._data = items;
this._items = {};
this._topList.editableList('empty');
this._loadingData = true;
for (var i=0; i<items.length;i++) {
@@ -556,33 +709,149 @@
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]);
show: function(item, done) {
if (typeof item === "string") {
item = this._items[item]
}
if (!item) {
return;
}
var that = this;
var stack = [];
var i = item;
while(i) {
stack.unshift(i);
i = i.parent;
}
var isOpening = false;
var handleStack = function(opening) {
isOpening = isOpening ||opening
var item = stack.shift();
if (stack.length === 0) {
setTimeout(function() {
that.reveal(item);
if (done) { done(); }
},isOpening?200:0);
} else {
item.treeList.expand(handleStack)
}
}
handleStack();
},
select: function(item) {
this._topList.find(".selected").removeClass("selected");
item.treeList.label.addClass("selected");
this._trigger("select",null,item)
reveal: function(item) {
if (typeof item === "string") {
item = this._items[item]
}
if (!item) {
return;
}
var listOffset = this._topList.offset().top;
var itemOffset = item.treeList.label.offset().top;
var scrollTop = this._topList.parent().scrollTop();
itemOffset -= listOffset+scrollTop;
var treeHeight = this._topList.parent().height();
var itemHeight = item.treeList.label.outerHeight();
if (itemOffset < itemHeight/2) {
this._topList.parent().scrollTop(scrollTop+itemOffset-itemHeight/2-itemHeight)
} else if (itemOffset+itemHeight > treeHeight) {
this._topList.parent().scrollTop(scrollTop+((itemOffset+2.5*itemHeight)-treeHeight));
}
},
select: function(item, triggerEvent, deselectExisting) {
var that = this;
if (!this.options.multi && deselectExisting !== false) {
this.clearSelection();
}
if (Array.isArray(item)) {
item.forEach(function(i) {
that.select(i,triggerEvent,false);
})
return;
}
if (typeof item === "string") {
item = this._items[item]
}
if (!item) {
return;
}
// this.show(item.id);
item.selected = true;
this._selected.add(item);
if (item.treeList.label) {
item.treeList.label.addClass("selected");
}
if (triggerEvent !== false) {
this._trigger("select",null,item)
}
},
clearSelection: function() {
this._selected.forEach(function(item) {
item.selected = false;
if (item.treeList.label) {
item.treeList.label.removeClass("selected")
}
});
this._selected.clear();
},
selected: function() {
var s = this._topList.find(".selected");
var selected = [];
this._selected.forEach(function(item) {
selected.push(item);
})
if (this.options.multi) {
var res = [];
s.each(function() {
res.push($(this).parent().data('data'));
})
return res;
return selected;
}
if (s.length) {
return s.parent().data('data');
if (selected.length) {
return selected[0]
} else {
// TODO: This may be a bug.. it causes the call to return itself
// not undefined.
return undefined;
}
},
filter: function(filterFunc) {
this.activeFilter = filterFunc;
var totalCount = 0;
var filter = function(item) {
var matchCount = 0;
if (filterFunc && filterFunc(item)) {
matchCount++;
totalCount++;
}
var childCount = 0;
if (item.children && typeof item.children !== "function") {
if (item.treeList.childList) {
childCount = item.treeList.childList.editableList('filter', filter);
} else {
item.treeList.childFilter = filter;
if (filterFunc) {
item.children.forEach(function(i) {
if (filter(i)) {
childCount++;
}
})
}
}
matchCount += childCount;
if (filterFunc && childCount > 0) {
setTimeout(function() {
item.treeList.expand();
},10);
}
}
if (!filterFunc) {
totalCount++;
return true
}
return matchCount > 0
}
this._topList.editableList('filter', filter);
return totalCount;
},
get: function(id) {
return this._items[id] || null;
}
});

View File

@@ -164,6 +164,88 @@
}
})
}
},
cred:{
value:"cred",
label:"credential",
icon:"fa fa-lock",
inputType: "password",
valueLabel: function(container,value) {
var that = this;
container.css("pointer-events","none");
container.css("flex-grow",0);
this.elementDiv.hide();
var buttons = $('<div>').css({
position: "absolute",
right:"6px",
top: "6px",
"pointer-events":"all"
}).appendTo(container);
var eyeButton = $('<button type="button" class="red-ui-button red-ui-button-small"></button>').css({
width:"20px"
}).appendTo(buttons).on("click", function(evt) {
evt.preventDefault();
var cursorPosition = that.input[0].selectionStart;
var currentType = that.input.attr("type");
if (currentType === "text") {
that.input.attr("type","password");
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
setTimeout(function() {
that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50);
} else {
that.input.attr("type","text");
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
setTimeout(function() {
that.input.focus();
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
},50);
}
}).hide();
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
if (value === "__PWRD__") {
var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
padding:"6px 6px",
borderRadius:"4px"
}).addClass("red-ui-typedInput-value-label-inactive").appendTo(container);
var editButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-pencil"></i></button>').appendTo(buttons).on("click", function(evt) {
evt.preventDefault();
innerContainer.hide();
container.css("background","none");
container.css("pointer-events","none");
that.input.val("");
that.element.val("");
that.elementDiv.show();
editButton.hide();
cancelButton.show();
eyeButton.show();
setTimeout(function() {
that.input.focus();
},50);
});
var cancelButton = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-times"></i></button>').css("margin-left","3px").appendTo(buttons).on("click", function(evt) {
evt.preventDefault();
innerContainer.show();
container.css("background","");
that.input.val("__PWRD__");
that.element.val("__PWRD__");
that.elementDiv.hide();
editButton.show();
cancelButton.hide();
eyeButton.hide();
that.input.attr("type","password");
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
}).hide();
} else {
container.css("background","none");
container.css("pointer-events","none");
this.elementDiv.show();
eyeButton.show();
}
}
}
};
var nlsd = false;
@@ -206,7 +288,7 @@
this.input.css('width','100%');
this.uiSelect.width(m[1]);
this.uiWidth = null;
} else {
} else if (this.uiWidth !== 0){
this.uiSelect.width(this.uiWidth);
}
["Right","Left"].forEach(function(d) {
@@ -220,11 +302,17 @@
that.input.attr(d,m);
});
this.defaultInputType = this.input.attr('type');
this.uiSelect.addClass("red-ui-typedInput-container");
this.element.attr('type','hidden');
this.options.types = this.options.types||Object.keys(allOptions);
if (!this.options.types && this.options.type) {
this.options.types = [this.options.type]
} else {
this.options.types = this.options.types||Object.keys(allOptions);
}
this.selectTrigger = $('<button class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect);
$('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
@@ -369,6 +457,9 @@
if (opt.label) {
op.text(opt.label);
}
if (opt.title) {
op.prop('title', opt.title)
}
if (opt.icon) {
if (opt.icon.indexOf("<") === 0) {
$(opt.icon).prependTo(op);
@@ -635,7 +726,7 @@
$('<img>',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
}
else {
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel);
$('<i>',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel);
}
}
if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) {
@@ -778,16 +869,24 @@
if (this.optionSelectTrigger) {
this.optionSelectTrigger.hide();
}
if (opt.inputType) {
this.input.attr('type',opt.inputType)
} else {
this.input.attr('type',this.defaultInputType)
}
if (opt.hasValue === false) {
this.oldValue = this.input.val();
this.input.val("");
this.elementDiv.hide();
this.valueLabelContainer.hide();
} else if (opt.valueLabel) {
// Reset any CSS the custom label may have set
this.valueLabelContainer.css("pointer-events","");
this.valueLabelContainer.css("flex-grow",1);
this.valueLabelContainer.show();
this.valueLabelContainer.empty();
opt.valueLabel.call(this,this.valueLabelContainer,this.input.val());
this.elementDiv.hide();
opt.valueLabel.call(this,this.valueLabelContainer,this.input.val());
} else {
if (this.oldValue !== undefined) {
this.input.val(this.oldValue);
@@ -825,7 +924,7 @@
panel.show({
target:that.optionExpandButton,
onclose:content.onclose,
align: "right"
align: "left"
});
}
})

View File

@@ -108,7 +108,7 @@ RED.deploy = (function() {
RED.events.on('nodes:change',function(state) {
RED.events.on('workspace:dirty',function(state) {
if (state.dirty) {
window.onbeforeunload = function() {
return RED._("deploy.confirm.undeployedChanges");

View File

@@ -1411,7 +1411,7 @@ RED.diff = (function() {
// Restore the original flow so subsequent merge resolutions can properly
// identify new-vs-old
RED.nodes.originalFlow(originalFlow);
imported[0].forEach(function(n) {
imported.nodes.forEach(function(n) {
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
n.changed = true;
}

View File

@@ -204,32 +204,28 @@ RED.editor = (function() {
node.dirty = true;
node.dirtyStatus = true;
var removedLinks = [];
if (node.ports) {
if (outputMap) {
RED.nodes.eachLink(function(l) {
if (l.source === node && outputMap.hasOwnProperty(l.sourcePort)) {
if (outputMap) {
RED.nodes.eachLink(function(l) {
if (l.source === node) {
if (outputMap.hasOwnProperty(l.sourcePort)) {
if (outputMap[l.sourcePort] === "-1") {
removedLinks.push(l);
} else {
l.sourcePort = outputMap[l.sourcePort];
}
}
});
}
if (node.outputs < node.ports.length) {
while (node.outputs < node.ports.length) {
node.ports.pop();
}
});
}
if (node.hasOwnProperty("__outputs")) {
if (node.outputs < node.__outputs) {
RED.nodes.eachLink(function(l) {
if (l.source === node && l.sourcePort >= node.outputs && removedLinks.indexOf(l) === -1) {
removedLinks.push(l);
}
});
} else if (node.outputs > node.ports.length) {
while (node.outputs > node.ports.length) {
node.ports.push(node.ports.length);
}
}
delete node.__outputs;
}
node.inputs = Math.min(1,Math.max(0,parseInt(node.inputs)));
if (isNaN(node.inputs)) {
@@ -490,8 +486,7 @@ RED.editor = (function() {
done();
}
}
if (definition.credentials) {
if (definition.credentials || /^subflow:/.test(definition.type)) {
if (node.credentials) {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
completePrepare();
@@ -499,7 +494,9 @@ RED.editor = (function() {
$.getJSON(getCredentialsURL(node.type, node.id), function (data) {
node.credentials = data;
node.credentials._ = $.extend(true,{},data);
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
if (!/^subflow:/.test(definition.type)) {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
}
completePrepare();
});
}
@@ -513,7 +510,9 @@ RED.editor = (function() {
for (var i=editStack.length-1;i<editStack.length;i++) {
var node = editStack[i];
label = node.type;
if (node.type === '_expression') {
if (node.type === 'group') {
label = RED._("group.editGroup",{name:RED.utils.sanitize(node.name||node.id)});
} else if (node.type === '_expression') {
label = RED._("expressionEditor.title");
} else if (node.type === '_js') {
label = RED._("jsEditor.title");
@@ -576,8 +575,11 @@ RED.editor = (function() {
$(this).attr("data-i18n",keys.join(";"));
});
if (type === "subflow-template" || type === "subflow") {
RED.subflow.buildEditForm(dialogForm,type,node);
if (type === "subflow-template") {
// This is the 'edit properties' dialog for a subflow template
// TODO: this needs to happen later in the dialog open sequence
// so that credentials can be loaded prior to building the form
RED.subflow.buildEditForm(type,node);
}
// Add dummy fields to prevent 'Enter' submitting the form in some
@@ -586,6 +588,7 @@ RED.editor = (function() {
// - the elements need to have id's that imply password/username
$('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-password" type="password"/></span>').prependTo(dialogForm);
$('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-username" type="text"/></span>').prependTo(dialogForm);
$('<span style="position: absolute; top: -2000px;"><input id="red-ui-trap-user" type="text"/></span>').prependTo(dialogForm);
dialogForm.on("submit", function(e) { e.preventDefault();});
dialogForm.find('input').attr("autocomplete","off");
return dialogForm;
@@ -784,6 +787,11 @@ RED.editor = (function() {
nodeDiv.css({
'backgroundColor': backgroundColor
});
var borderColor = RED.utils.getDarkerColor(backgroundColor);
if (borderColor !== backgroundColor) {
nodeDiv.css('border-color',borderColor)
}
}
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
@@ -819,99 +827,6 @@ RED.editor = (function() {
searchInput.trigger("focus");
}
function createColorPicker(colorRow, color) {
var colorButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(colorRow);
$('<i class="fa fa-caret-down"></i>').appendTo(colorButton);
var colorDisp = $('<div>',{class:"red-ui-search-result-node"}).appendTo(colorButton);
var selector = $("<input/>", {
id: "red-ui-editor-node-color",
type: "text",
value: color
}).css({
marginLeft: "10px",
width: "150px",
}).appendTo(colorRow);
selector.on("change", function (e) {
var color = selector.val();
$(".red-ui-editor-node-appearance-button .red-ui-search-result-node").css({
"background-color": color
});
});
selector.trigger("change");
colorButton.on("click", function (e) {
var recommendedColors = [
"#DDAA99",
"#3FADB5", "#87A980", "#A6BBCF",
"#AAAA66", "#C0C0C0", "#C0DEED",
"#C7E9C0", "#D7D7A0", "#D8BFD8",
"#DAC4B4", "#DEB887", "#DEBD5C",
"#E2D96E", "#E6E0F8", "#E7E7AE",
"#E9967A", "#F3B567", "#FDD0A2",
"#FDF0C2", "#FFAAAA", "#FFCC66",
"#FFF0F0", "#FFFFFF"
].map(function(c) {
var r = parseInt(c.substring(1, 3), 16) / 255;
var g = parseInt(c.substring(3, 5), 16) / 255;
var b = parseInt(c.substring(5, 7), 16) / 255;
return {
hex: c,
r: r,
g: g,
b: b,
l: 0.3 * r + 0.59 * g + 0.11 * b
}
});
// Sort by luminosity.
recommendedColors.sort(function (a, b) {
return a.l - b.l;
});
var numColors = recommendedColors.length;
var width = 30;
var height = 30;
var margin = 2;
var perRow = 6;
var picker = $("<div/>", {
class: "red-ui-color-picker"
}).css({
width: ((width+margin+margin)*perRow)+"px",
height: Math.ceil(numColors/perRow)*(height+margin+margin)+"+px"
});
var count = 0;
var row = null;
recommendedColors.forEach(function (col) {
if ((count % perRow) == 0) {
row = $("<div/>").appendTo(picker);
}
var button = $("<button/>", {
}).css({
width: width+"px",
height: height+"px",
margin: margin+"px",
backgroundColor: col.hex,
"border-style": "solid",
"border-width": "1px",
"border-color": col.luma<0.92?col.hex:'#ccc'
}).appendTo(row);
button.on("click", function (e) {
e.preventDefault();
colorPanel.hide();
selector.val(col.hex);
selector.trigger("change");
});
count++;
});
var colorPanel = RED.popover.panel(picker);
colorPanel.show({
target: colorButton
})
});
}
function buildAppearanceForm(container,node) {
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
@@ -964,13 +879,13 @@ RED.editor = (function() {
var userCount = 0;
var subflowType = "subflow:"+node.id;
RED.nodes.eachNode(function(n) {
if (n.type === subflowType) {
userCount++;
}
});
// RED.nodes.eachNode(function(n) {
// if (n.type === subflowType) {
// userCount++;
// }
// });
$("#red-ui-editor-subflow-user-count")
.text(RED._("subflow.subflowInstances", {count:userCount})).show();
.text(RED._("subflow.subflowInstances", {count:node.instances.length})).show();
}
$('<div class="form-row">'+
@@ -997,7 +912,35 @@ RED.editor = (function() {
class: "form-row"
}).appendTo(dialogForm);
$("<label/>").text(RED._("editor.color")).appendTo(colorRow);
createColorPicker(colorRow, color);
var recommendedColors = [
"#DDAA99",
"#3FADB5", "#87A980", "#A6BBCF",
"#AAAA66", "#C0C0C0", "#C0DEED",
"#C7E9C0", "#D7D7A0", "#D8BFD8",
"#DAC4B4", "#DEB887", "#DEBD5C",
"#E2D96E", "#E6E0F8", "#E7E7AE",
"#E9967A", "#F3B567", "#FDD0A2",
"#FDF0C2", "#FFAAAA", "#FFCC66",
"#FFF0F0", "#FFFFFF"
]
RED.colorPicker.create({
id: "red-ui-editor-node-color",
value: color,
palette: recommendedColors,
sortPalette: function (a, b) {return a.l - b.l;}
}).appendTo(colorRow);
$("#red-ui-editor-node-color").on('change', function(ev) {
// Horribly out of scope...
var colour = $(this).val();
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
})
}
@@ -1012,6 +955,11 @@ RED.editor = (function() {
var colour = RED.utils.getNodeColor(node.type, node._def);
var icon_url = RED.utils.getNodeIcon(node._def,node);
nodeDiv.css('backgroundColor',colour);
var borderColor = RED.utils.getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
@@ -1132,15 +1080,17 @@ RED.editor = (function() {
if (node.info) {
nodeInfoEditor.getSession().setValue(node.info, -1);
}
node.infoEditor = nodeInfoEditor;
return nodeInfoEditor;
}
function showEditDialog(node) {
function showEditDialog(node, defaultTab) {
var editing_node = node;
var isDefaultIcon;
var defaultIcon;
var nodeInfoEditor;
var finishedBuilding = false;
var skipInfoRefreshOnClose = false;
editStack.push(node);
RED.view.state(RED.state.EDITING);
@@ -1333,10 +1283,12 @@ RED.editor = (function() {
if (configNode) {
var users = configNode.users;
users.splice(users.indexOf(editing_node),1);
RED.events.emit("nodes:change",configNode);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(editing_node);
RED.events.emit("nodes:change",configNode);
}
}
changes[d] = editing_node[d];
@@ -1471,6 +1423,19 @@ RED.editor = (function() {
if (type === "subflow") {
var old_env = editing_node.env;
var new_env = RED.subflow.exportSubflowInstanceEnv(editing_node);
if (new_env && new_env.length > 0) {
new_env.forEach(function(prop) {
if (prop.type === "cred") {
editing_node.credentials = editing_node.credentials || {_:{}};
editing_node.credentials[prop.name] = prop.value;
editing_node.credentials['has_'+prop.name] = (prop.value !== "");
if (prop.value !== '__PWRD__') {
changed = true;
}
delete prop.value;
}
});
}
if (!isSameObj(old_env, new_env)) {
editing_node.env = new_env;
changes.env = editing_node.env;
@@ -1520,6 +1485,7 @@ RED.editor = (function() {
editing_node.dirty = true;
validateNode(editing_node);
RED.events.emit("editor:save",editing_node);
RED.events.emit("nodes:change",editing_node);
RED.tray.close();
}
}
@@ -1537,6 +1503,10 @@ RED.editor = (function() {
}
},
open: function(tray, done) {
if (editing_node.hasOwnProperty('outputs')) {
editing_node.__outputs = editing_node.outputs;
}
var trayFooter = tray.find(".red-ui-tray-footer");
var trayBody = tray.find('.red-ui-tray-body');
trayBody.parent().css('overflow','hidden');
@@ -1567,9 +1537,6 @@ RED.editor = (function() {
collapsible: true,
menu: false
});
if (editing_node) {
RED.sidebar.info.refresh(editing_node);
}
var ns;
if (node._def.set.module === "node-red") {
ns = "node-red";
@@ -1599,12 +1566,13 @@ RED.editor = (function() {
id: "editor-subflow-envProperties",
label: RED._("editor-tab.envProperties"),
name: RED._("editor-tab.envProperties"),
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
content: $('<div>', {id:"editor-subflow-envProperties-content",class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-list"
};
RED.subflow.buildPropertiesForm(subflowPropertiesTab.content,node);
editorTabs.addTab(subflowPropertiesTab);
// This tab is populated by the oneditprepare function of this
// subflow. That ensures it is done *after* any credentials
// have been loaded for the instance.
}
if (!node._def.defaults || !node._def.defaults.hasOwnProperty('info')) {
@@ -1638,6 +1606,9 @@ RED.editor = (function() {
prepareEditDialog(node,node._def,"node-input", function() {
trayBody.i18n();
finishedBuilding = true;
if (defaultTab) {
editorTabs.activateTab(defaultTab);
}
done();
});
},
@@ -1645,7 +1616,7 @@ RED.editor = (function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
if (editing_node) {
if (editing_node && !skipInfoRefreshOnClose) {
RED.sidebar.info.refresh(editing_node);
}
RED.workspaces.refresh();
@@ -1659,6 +1630,7 @@ RED.editor = (function() {
show: function() {
if (editing_node) {
RED.sidebar.info.refresh(editing_node);
RED.sidebar.help.show(editing_node.type, false);
}
}
}
@@ -1673,6 +1645,7 @@ RED.editor = (function() {
text: RED._("subflow.edit"),
click: function() {
RED.workspaces.show(id);
skipInfoRefreshOnClose = true;
$("#node-dialog-ok").trigger("click");
}
});
@@ -1864,6 +1837,7 @@ RED.editor = (function() {
show: function() {
if (editing_config_node) {
RED.sidebar.info.refresh(editing_config_node);
RED.sidebar.help.show(type, false);
}
}
}
@@ -1941,10 +1915,12 @@ RED.editor = (function() {
if (configNode) {
var users = configNode.users;
users.splice(users.indexOf(editing_config_node),1);
RED.events.emit("nodes:change",configNode);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(editing_config_node);
RED.events.emit("nodes:change",configNode);
}
}
editing_config_node[d] = newValue;
@@ -2039,6 +2015,7 @@ RED.editor = (function() {
RED.view.redraw(true);
if (!configAdding) {
RED.events.emit("editor:save",editing_config_node);
RED.events.emit("nodes:change",editing_config_node);
}
RED.tray.close(function() {
updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix);
@@ -2252,12 +2229,26 @@ RED.editor = (function() {
var old_env = editing_node.env;
var new_env = RED.subflow.exportSubflowTemplateEnv($("#node-input-env-container").editableList("items"));
if (new_env && new_env.length > 0) {
new_env.forEach(function(prop) {
if (prop.type === "cred") {
editing_node.credentials = editing_node.credentials || {_:{}};
editing_node.credentials[prop.name] = prop.value;
editing_node.credentials['has_'+prop.name] = (prop.value !== "");
if (prop.value !== '__PWRD__') {
changed = true;
}
delete prop.value;
}
});
}
if (!isSameObj(old_env, new_env)) {
editing_node.env = new_env;
changes.env = editing_node.env;
changed = true;
}
RED.palette.refresh();
if (changed) {
var wasChanged = editing_node.changed;
@@ -2277,6 +2268,7 @@ RED.editor = (function() {
validateNode(n);
}
});
RED.events.emit("subflows:change",editing_node);
RED.nodes.dirty(true);
var historyEvent = {
t:'edit',
@@ -2311,7 +2303,7 @@ RED.editor = (function() {
$("#node-input-env-container").editableList('height',height-95);
}
},
open: function(tray) {
open: function(tray, done) {
var trayFooter = tray.find(".red-ui-tray-footer");
var trayFooterLeft = $("<div/>", {
class: "red-ui-tray-footer-left"
@@ -2362,7 +2354,6 @@ RED.editor = (function() {
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog"
};
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
editorTabs.addTab(nodePropertiesTab);
var descriptionTab = {
@@ -2391,11 +2382,19 @@ RED.editor = (function() {
buildAppearanceForm(appearanceTab.content,editing_node);
editorTabs.addTab(appearanceTab);
$("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name"));
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
trayBody.i18n();
finishedBuilding = true;
$.getJSON(getCredentialsURL("subflow", subflow.id), function (data) {
subflow.credentials = data;
subflow.credentials._ = $.extend(true,{},data);
$("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name"));
finishedBuilding = true;
done();
});
},
close: function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
@@ -2414,6 +2413,253 @@ RED.editor = (function() {
RED.tray.show(trayOptions);
}
function showEditGroupDialog(group) {
var editing_node = group;
editStack.push(group);
RED.view.state(RED.state.EDITING);
var nodeInfoEditor;
var finishedBuilding = false;
var trayOptions = {
title: getEditStackTitle(),
buttons: [
{
id: "node-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
RED.tray.close();
}
},
{
id: "node-dialog-ok",
class: "primary",
text: RED._("common.label.done"),
click: function() {
var changes = {};
var changed = false;
var wasDirty = RED.nodes.dirty();
var d;
var outputMap;
if (editing_node._def.oneditsave) {
var oldValues = {};
for (d in editing_node._def.defaults) {
if (editing_node._def.defaults.hasOwnProperty(d)) {
if (typeof editing_node[d] === "string" || typeof editing_node[d] === "number") {
oldValues[d] = editing_node[d];
} else {
oldValues[d] = $.extend(true,{},{v:editing_node[d]}).v;
}
}
}
try {
var rc = editing_node._def.oneditsave.call(editing_node);
if (rc === true) {
changed = true;
}
} catch(err) {
console.log("oneditsave",editing_node.id,editing_node.type,err.toString());
}
for (d in editing_node._def.defaults) {
if (editing_node._def.defaults.hasOwnProperty(d)) {
if (oldValues[d] === null || typeof oldValues[d] === "string" || typeof oldValues[d] === "number") {
if (oldValues[d] !== editing_node[d]) {
changes[d] = oldValues[d];
changed = true;
}
} else if (d !== "nodes") {
if (JSON.stringify(oldValues[d]) !== JSON.stringify(editing_node[d])) {
changes[d] = oldValues[d];
changed = true;
}
}
}
}
}
var newValue;
if (editing_node._def.defaults) {
for (d in editing_node._def.defaults) {
if (editing_node._def.defaults.hasOwnProperty(d)) {
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 {
newValue = input.val();
}
if (newValue != null) {
if (editing_node._def.defaults[d].type) {
if (newValue == "_ADD_") {
newValue = "";
}
}
if (editing_node[d] != newValue) {
if (editing_node._def.defaults[d].type) {
// Change to a related config node
var configNode = RED.nodes.node(editing_node[d]);
if (configNode) {
var users = configNode.users;
users.splice(users.indexOf(editing_node),1);
RED.events.emit("nodes:change",configNode);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(editing_node);
RED.events.emit("nodes:change",configNode);
}
}
changes[d] = editing_node[d];
editing_node[d] = newValue;
changed = true;
}
}
}
}
}
var oldInfo = editing_node.info;
if (nodeInfoEditor) {
var newInfo = nodeInfoEditor.getValue();
if (!!oldInfo) {
// Has existing info property
if (newInfo.trim() === "") {
// New value is blank - remove the property
changed = true;
changes.info = oldInfo;
delete editing_node.info;
} else if (newInfo !== oldInfo) {
// New value is different
changed = true;
changes.info = oldInfo;
editing_node.info = newInfo;
}
} else {
// No existing info
if (newInfo.trim() !== "") {
// New value is not blank
changed = true;
changes.info = undefined;
editing_node.info = newInfo;
}
}
}
if (changed) {
var wasChanged = editing_node.changed;
editing_node.changed = true;
RED.nodes.dirty(true);
var historyEvent = {
t:'edit',
node:editing_node,
changes:changes,
dirty:wasDirty,
changed:wasChanged
};
RED.history.push(historyEvent);
RED.events.emit("groups:change",editing_node);
}
editing_node.dirty = true;
RED.tray.close();
RED.view.redraw(true);
}
}
],
resize: function(size) {
editTrayWidthCache['group'] = size.width;
$(".red-ui-tray-content").height(size.height - 50);
// var form = $(".red-ui-tray-content form").height(dimensions.height - 50 - 40);
// if (editing_node && editing_node._def.oneditresize) {
// try {
// editing_node._def.oneditresize.call(editing_node,{width:form.width(),height:form.height()});
// } catch(err) {
// console.log("oneditresize",editing_node.id,editing_node.type,err.toString());
// }
// }
},
open: function(tray, done) {
var trayFooter = tray.find(".red-ui-tray-footer");
var trayFooterLeft = $("<div/>", {
class: "red-ui-tray-footer-left"
}).appendTo(trayFooter)
var trayBody = tray.find('.red-ui-tray-body');
trayBody.parent().css('overflow','hidden');
var editorTabEl = $('<ul></ul>').appendTo(trayBody);
var editorContent = $('<div></div>').appendTo(trayBody);
var editorTabs = RED.tabs.create({
element:editorTabEl,
onchange:function(tab) {
editorContent.children().hide();
if (tab.onchange) {
tab.onchange.call(tab);
}
tab.content.show();
if (finishedBuilding) {
RED.tray.resize();
}
},
collapsible: true,
menu: false
});
var nodePropertiesTab = {
id: "editor-tab-properties",
label: RED._("editor-tab.properties"),
name: RED._("editor-tab.properties"),
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cog"
};
buildEditForm(nodePropertiesTab.content,"dialog-form","group","node-red",group);
editorTabs.addTab(nodePropertiesTab);
var descriptionTab = {
id: "editor-tab-description",
label: RED._("editor-tab.description"),
name: RED._("editor-tab.description"),
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-file-text-o",
onchange: function() {
nodeInfoEditor.focus();
}
};
editorTabs.addTab(descriptionTab);
nodeInfoEditor = buildDescriptionForm(descriptionTab.content,editing_node);
prepareEditDialog(group,group._def,"node-input", function() {
trayBody.i18n();
finishedBuilding = true;
done();
});
},
close: function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
RED.sidebar.info.refresh(editing_node);
nodeInfoEditor.destroy();
nodeInfoEditor = null;
editStack.pop();
editing_node = null;
},
show: function() {
}
}
if (editTrayWidthCache.hasOwnProperty('group')) {
trayOptions.width = editTrayWidthCache['group'];
}
RED.tray.show(trayOptions);
}
function showTypeEditor(type, options) {
if (customEditTypes.hasOwnProperty(type)) {
if (editStack.length > 0) {
@@ -2478,7 +2724,7 @@ RED.editor = (function() {
if (options.globals) {
setTimeout(function() {
if (!!session.$worker) {
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
session.$worker.send("setOptions", [{globals: options.globals, maxerr:1000}]);
}
},100);
}
@@ -2537,11 +2783,18 @@ RED.editor = (function() {
edit: showEditDialog,
editConfig: showEditConfigNodeDialog,
editSubflow: showEditSubflowDialog,
editGroup: showEditGroupDialog,
editJavaScript: function(options) { showTypeEditor("_js",options) },
editExpression: function(options) { showTypeEditor("_expression", options) },
editJSON: function(options) { showTypeEditor("_json", options) },
editMarkdown: function(options) { showTypeEditor("_markdown", options) },
editText: function(options) { showTypeEditor("_text", options) },
editText: function(options) {
if (options.mode == "markdown") {
showTypeEditor("_markdown", options)
} else {
showTypeEditor("_text", options)
}
},
editBuffer: function(options) { showTypeEditor("_buffer", options) },
buildEditForm: buildEditForm,
validateNode: validateNode,

View File

@@ -15,7 +15,7 @@
**/
(function() {
var template = '<script type="text/x-red" data-template-name="_buffer"><div id="red-ui-editor-type-buffer-panels"><div id="red-ui-editor-type-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button class="red-ui-editor-type-buffer-type red-ui-button red-ui-button-small"><i class="fa fa-exclamation-circle"></i> <span id="red-ui-editor-type-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="red-ui-editor-type-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></button></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="red-ui-editor-type-buffer-str"></div></div></div><div id="red-ui-editor-type-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="red-ui-editor-type-buffer-bin"></div></div></div></div></script>';
var template = '<script type="text/x-red" data-template-name="_buffer"><div id="red-ui-editor-type-buffer-panels"><div id="red-ui-editor-type-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button class="red-ui-editor-type-buffer-type red-ui-button red-ui-button-small"><i class="fa fa-exclamation-circle"></i> <span id="red-ui-editor-type-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="red-ui-editor-type-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></button></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="red-ui-editor-type-buffer-str"></div></div></div><div id="red-ui-editor-type-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px; margin-bottom:0;"><div class="node-text-editor" id="red-ui-editor-type-buffer-bin"></div></div></div></div></script>';
function stringToUTF8Array(str) {
var data = [];
@@ -187,8 +187,7 @@
$(".red-ui-editor-type-buffer-type").on("click", function(e) {
e.preventDefault();
RED.sidebar.info.set(RED._("bufferEditor.modeDesc"));
RED.sidebar.info.show();
RED.sidebar.help.set(RED._("bufferEditor.modeDesc"));
})

View File

@@ -237,8 +237,7 @@
var changeTimer;
$(".red-ui-editor-type-expression-legacy").on("click", function(e) {
e.preventDefault();
RED.sidebar.info.set(RED._("expressionEditor.compatModeDesc"));
RED.sidebar.info.show();
RED.sidebar.help.set(RED._("expressionEditor.compatModeDesc"));
})
var testExpression = function() {
var value = testDataEditor.getValue();
@@ -318,9 +317,9 @@
var p2 = $("#red-ui-editor-type-expression-panel-info > .form-row > div:first-child");
p2Height -= p2.outerHeight(true) + 20;
$(".red-ui-editor-type-expression-tab-content").height(p2Height);
$("#red-ui-editor-type-expression-test-data").css("height",(p2Height-5)+"px");
$("#red-ui-editor-type-expression-test-data").css("height",(p2Height-25)+"px");
testDataEditor.resize();
$("#red-ui-editor-type-expression-test-result").css("height",(p2Height-5)+"px");
$("#red-ui-editor-type-expression-test-result").css("height",(p2Height-25)+"px");
testResultEditor.resize();
}
});

View File

@@ -87,6 +87,9 @@
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
dialogForm.i18n();
setTimeout(function() {
expressionEditor.focus();
},300);
},
close: function() {
expressionEditor.destroy();

View File

@@ -0,0 +1,702 @@
/**
* 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.group = (function() {
var _groupEditTemplate = '<script type="text/x-red" data-template-name="group">'+
'<div class="form-row">'+
'<label for="node-input-name" data-i18n="[append]editor:common.label.name"><i class="fa fa-tag"></i> </label>'+
'<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">'+
'</div>'+
// '<div class="node-input-group-style-tools"><span class="button-group"><button class="red-ui-button red-ui-button-small">Use default style</button><button class="red-ui-button red-ui-button-small">Set as default style</button></span></div>'+
'<div class="form-row" id="node-input-row-style-stroke">'+
'<label data-i18n="editor:common.label.style"></label>'+
'<label style="width: 70px;margin-right:10px" for="node-input-style-stroke" data-i18n="editor:common.label.line"></label>'+
'</div>'+
'<div class="form-row" style="padding-left: 100px;" id="node-input-row-style-fill">'+
'<label style="width: 70px;margin-right: 10px " for="node-input-style-fill" data-i18n="editor:common.label.fill"></label>'+
'</div>'+
'<div class="form-row">'+
'<label for="node-input-style-label" data-i18n="editor:common.label.label"></label>'+
'<input type="checkbox" id="node-input-style-label"/>'+
'</div>'+
'<div class="form-row" id="node-input-row-style-label-options">'+
'<div style="margin-left: 100px; display: inline-block">'+
'<div class="form-row">'+
'<span style="display: inline-block; min-width: 140px" id="node-input-row-style-label-color">'+
'<label style="width: 70px;margin-right: 10px" for="node-input-style-fill" data-i18n="editor:common.label.color"></label>'+
'</span>'+
'</div>'+
'<div class="form-row">'+
'<span style="display: inline-block; min-width: 140px;" id="node-input-row-style-label-position">'+
'<label style="width: 70px;margin-right: 10px " for="node-input-style-label-position" data-i18n="editor:common.label.position"></label>'+
'</span>'+
'</div>'+
'</div>'+
'</div>'+
'</script>';
var colorPalette = [
"#ff0000",
"#ffC000",
"#ffff00",
"#92d04f",
"#0070c0",
"#001f60",
"#6f2fa0",
"#000000",
"#777777"
]
var colorSteps = 3;
var colorCount = colorPalette.length;
for (var i=0,len=colorPalette.length*colorSteps;i<len;i++) {
var ci = i%colorCount;
var j = Math.floor(i/colorCount)+1;
var c = colorPalette[ci];
var r = parseInt(c.substring(1, 3), 16);
var g = parseInt(c.substring(3, 5), 16);
var b = parseInt(c.substring(5, 7), 16);
var dr = (255-r)/(colorSteps+((ci===colorCount-1) ?0:1));
var dg = (255-g)/(colorSteps+((ci===colorCount-1) ?0:1));
var db = (255-b)/(colorSteps+((ci===colorCount-1) ?0:1));
r = Math.min(255,Math.floor(r+j*dr));
g = Math.min(255,Math.floor(g+j*dg));
b = Math.min(255,Math.floor(b+j*db));
var s = ((r<<16) + (g<<8) + b).toString(16);
colorPalette.push('#'+'000000'.slice(0, 6-s.length)+s);
}
var defaultGroupStyle = {
label: true,
"label-position": "nw"
};
var groupDef = {
defaults:{
name:{value:""},
style:{value:{label:true}},
nodes:{value:[]}
},
category: "config",
oneditprepare: function() {
var style = this.style || {};
RED.colorPicker.create({
id:"node-input-style-stroke",
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
cellHeight: 16,
cellMargin: 3,
none: true,
opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0)
}).appendTo("#node-input-row-style-stroke");
RED.colorPicker.create({
id:"node-input-style-fill",
value: style.fill || defaultGroupStyle.fill ||"none",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
cellHeight: 16,
cellMargin: 3,
none: true,
opacity: style.hasOwnProperty('fill-opacity')?style['fill-opacity']:(defaultGroupStyle.hasOwnProperty('fill-opacity')?defaultGroupStyle['fill-opacity']:1.0)
}).appendTo("#node-input-row-style-fill");
createLayoutPicker({
id:"node-input-style-label-position",
value:style["label-position"] || "nw"
}).appendTo("#node-input-row-style-label-position");
RED.colorPicker.create({
id:"node-input-style-color",
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
cellHeight: 16,
cellMargin: 3
}).appendTo("#node-input-row-style-label-color");
$("#node-input-style-label").toggleButton({
enabledLabel: RED._("editor.show"),
disabledLabel: RED._("editor.show"),
})
$("#node-input-style-label").on("change", function(evt) {
$("#node-input-row-style-label-options").toggle($(this).prop("checked"));
})
$("#node-input-style-label").prop("checked", this.style.label)
$("#node-input-style-label").trigger("change");
},
oneditresize: function(size) {
},
oneditsave: function() {
this.style.stroke = $("#node-input-style-stroke").val();
this.style.fill = $("#node-input-style-fill").val();
this.style["stroke-opacity"] = $("#node-input-style-stroke-opacity").val();
this.style["fill-opacity"] = $("#node-input-style-fill-opacity").val();
this.style.label = $("#node-input-style-label").prop("checked");
if (this.style.label) {
this.style["label-position"] = $("#node-input-style-label-position").val();
this.style.color = $("#node-input-style-color").val();
} else {
delete this.style["label-position"];
delete this.style.color;
}
var node = this;
['stroke','fill','stroke-opacity','fill-opacity','color','label-position'].forEach(function(prop) {
if (node.style[prop] === defaultGroupStyle[prop]) {
delete node.style[prop]
}
})
this.resize = true;
},
set:{
module: "node-red"
}
}
function init() {
RED.events.on("view:selection-changed",function(selection) {
var activateGroup = !!selection.nodes;
var activateUngroup = false;
var activateMerge = false;
var activateRemove = false;
if (activateGroup) {
selection.nodes.forEach(function (n) {
if (n.type === "group") {
activateUngroup = true;
}
if (!!n.g) {
activateRemove = true;
}
});
if (activateUngroup) {
activateMerge = (selection.nodes.length > 1);
}
}
RED.menu.setDisabled("menu-item-group-group", !activateGroup);
RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
});
RED.actions.add("core:group-selection", function() { groupSelection() })
RED.actions.add("core:ungroup-selection", function() { ungroupSelection() })
RED.actions.add("core:merge-selection-to-group", function() { mergeSelection() })
RED.actions.add("core:remove-selection-from-group", function() { removeSelection() })
RED.actions.add("core:copy-group-style", function() { copyGroupStyle() });
RED.actions.add("core:paste-group-style", function() { pasteGroupStyle() });
$(_groupEditTemplate).appendTo("#red-ui-editor-node-configs");
var groupStyleDiv = $("<div>",{
class:"red-ui-flow-group-body",
style: "position: absolute; top: -1000px;"
}).appendTo(document.body);
var groupStyle = getComputedStyle(groupStyleDiv[0]);
defaultGroupStyle = {
stroke: convertColorToHex(groupStyle.stroke),
"stroke-opacity": groupStyle.strokeOpacity,
fill: convertColorToHex(groupStyle.fill),
"fill-opacity": groupStyle.fillOpacity,
label: true,
"label-position": "nw"
}
groupStyleDiv.remove();
groupStyleDiv = $("<div>",{
class:"red-ui-flow-group-label",
style: "position: absolute; top: -1000px;"
}).appendTo(document.body);
groupStyle = getComputedStyle(groupStyleDiv[0]);
defaultGroupStyle.color = convertColorToHex(groupStyle.fill);
groupStyleDiv.remove();
}
function convertColorToHex(c) {
var m = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(c);
if (m) {
var s = ((parseInt(m[1])<<16) + (parseInt(m[2])<<8) + parseInt(m[3])).toString(16)
return '#'+'000000'.slice(0, 6-s.length)+s;
}
return c;
}
var groupStyleClipboard;
function copyGroupStyle() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') {
groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style));
RED.notify(RED._("clipboard.groupStyleCopied"),{id:"clipboard"})
}
}
function pasteGroupStyle() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
if (groupStyleClipboard) {
var selection = RED.view.selection();
if (selection.nodes) {
var historyEvent = {
t:'multi',
events:[],
dirty: RED.nodes.dirty()
}
selection.nodes.forEach(function(n) {
if (n.type === 'group') {
historyEvent.events.push({
t: "edit",
node: n,
changes: {
style: JSON.parse(JSON.stringify(n.style))
},
dirty: RED.nodes.dirty()
});
n.style = JSON.parse(JSON.stringify(groupStyleClipboard));
n.dirty = true;
}
})
if (historyEvent.events.length > 0) {
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.view.redraw();
}
}
}
}
function groupSelection() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
var group = createGroup(selection.nodes);
if (group) {
var historyEvent = {
t:"createGroup",
groups: [ group ],
dirty: RED.nodes.dirty()
}
RED.history.push(historyEvent);
RED.view.select({nodes:[group]});
RED.nodes.dirty(true);
}
}
}
function ungroupSelection() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
var newSelection = [];
groups = selection.nodes.filter(function(n) { return n.type === "group" });
var historyEvent = {
t:"ungroup",
groups: [ ],
dirty: RED.nodes.dirty()
}
RED.history.push(historyEvent);
groups.forEach(function(g) {
newSelection = newSelection.concat(ungroup(g))
historyEvent.groups.push(g);
})
RED.history.push(historyEvent);
RED.view.select({nodes:newSelection})
RED.nodes.dirty(true);
}
}
function ungroup(g) {
var nodes = [];
var parentGroup = RED.nodes.group(g.g);
g.nodes.forEach(function(n) {
nodes.push(n);
if (parentGroup) {
// Move nodes to parent group
n.g = parentGroup.id;
parentGroup.nodes.push(n);
parentGroup.dirty = true;
n.dirty = true;
} else {
delete n.g;
}
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
RED.events.emit("nodes:change",n)
}
})
RED.nodes.removeGroup(g);
return nodes;
}
function mergeSelection() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
var nodes = [];
var historyEvent = {
t: "multi",
events: []
}
var ungroupHistoryEvent = {
t: "ungroup",
groups: []
}
var n;
var parentGroup;
// First pass, check they are all in the same parent
// TODO: DRY mergeSelection,removeSelection,...
for (var i=0; i<selection.nodes.length; i++) {
n = selection.nodes[i];
if (i === 0) {
parentGroup = n.g;
} else if (n.g !== parentGroup) {
RED.notify(RED._("group.errors.cannotCreateDiffGroups"),"error");
return;
}
}
var existingGroup;
// Second pass, ungroup any groups in the selection and add their contents
// to the selection
for (var i=0; i<selection.nodes.length; i++) {
n = selection.nodes[i];
if (n.type === "group") {
if (!existingGroup) {
existingGroup = n;
}
ungroupHistoryEvent.groups.push(n);
nodes = nodes.concat(ungroup(n));
} else {
nodes.push(n);
}
n.dirty = true;
}
if (ungroupHistoryEvent.groups.length > 0) {
historyEvent.events.push(ungroupHistoryEvent);
}
// Finally, create the new group
var group = createGroup(nodes);
if (group) {
if (existingGroup) {
group.style = existingGroup.style;
group.name = existingGroup.name;
}
RED.view.select({nodes:[group]})
}
historyEvent.events.push({
t:"createGroup",
groups: [ group ],
dirty: RED.nodes.dirty()
});
RED.history.push(historyEvent);
RED.nodes.dirty(true);
}
}
function removeSelection() {
if (RED.view.state() !== RED.state.DEFAULT) { return }
var selection = RED.view.selection();
if (selection.nodes) {
var nodes = [];
var n;
var parentGroup = RED.nodes.group(selection.nodes[0].g);
if (parentGroup) {
try {
removeFromGroup(parentGroup,selection.nodes,true);
var historyEvent = {
t: "removeFromGroup",
dirty: RED.nodes.dirty(),
group: parentGroup,
nodes: selection.nodes
}
RED.history.push(historyEvent);
RED.nodes.dirty(true);
} catch(err) {
RED.notify(err,"error");
return;
}
}
RED.view.select({nodes:selection.nodes})
}
}
function createGroup(nodes) {
if (nodes.length === 0) {
return;
}
if (nodes.filter(function(n) { return n.type === "subflow" }).length > 0) {
RED.notify(RED._("group.errors.cannotAddSubflowPorts"),"error");
return;
}
// nodes is an array
// each node must be on the same tab (z)
var group = {
id: RED.nodes.id(),
type: 'group',
nodes: [],
style: JSON.parse(JSON.stringify(defaultGroupStyle)),
x: Number.POSITIVE_INFINITY,
y: Number.POSITIVE_INFINITY,
w: 0,
h: 0,
_def: RED.group.def
}
group.z = nodes[0].z;
RED.nodes.addGroup(group);
try {
addToGroup(group,nodes);
} catch(err) {
RED.notify(err,"error");
return;
}
return group;
}
function addToGroup(group,nodes) {
if (!Array.isArray(nodes)) {
nodes = [nodes];
}
var i,n,z;
var g;
// First pass - validate we can safely add these nodes to the group
for (i=0;i<nodes.length;i++) {
n = nodes[i]
if (!n.z) {
throw new Error("Cannot add node without a z property to a group")
}
if (!z) {
z = n.z;
} else if (z !== n.z) {
throw new Error("Cannot add nooes with different z properties")
}
if (n.g) {
// This is already in a group.
// - check they are all in the same group
if (!g) {
if (i!==0) {
// TODO: this might be ok when merging groups
throw new Error(RED._("group.errors.cannotCreateDiffGroups"))
}
g = n.g
}
}
if (g !== n.g) {
throw new Error(RED._("group.errors.cannotCreateDiffGroups"))
}
}
// The nodes are already in a group. The assumption is they should be
// wrapped in the newly provided group, and that group added to in their
// place to the existing containing group.
if (g) {
g = RED.nodes.group(g);
g.nodes.push(group);
g.dirty = true;
group.g = g.id;
}
// Second pass - add them to the group
for (i=0;i<nodes.length;i++) {
n = nodes[i];
if (n.type !== "subflow") {
if (g && n.g === g.id) {
var ni = g.nodes.indexOf(n);
if (ni > -1) {
g.nodes.splice(ni,1)
}
}
n.g = group.id;
n.dirty = true;
group.nodes.push(n);
group.x = Math.min(group.x,n.x-n.w/2-25-((n._def.button && n._def.align!=="right")?20:0));
group.y = Math.min(group.y,n.y-n.h/2-25);
group.w = Math.max(group.w,n.x+n.w/2+25+((n._def.button && n._def.align=="right")?20:0) - group.x);
group.h = Math.max(group.h,n.y+n.h/2+25-group.y);
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
RED.events.emit("nodes:change",n)
}
}
}
if (g) {
RED.events.emit("groups:change",group)
}
markDirty(group);
}
function removeFromGroup(group, nodes, reparent) {
if (!Array.isArray(nodes)) {
nodes = [nodes];
}
var n;
// First pass, check they are all in the same parent
// TODO: DRY mergeSelection,removeSelection,...
for (var i=0; i<nodes.length; i++) {
if (nodes[i].g !== group.id) {
return;
}
}
var parentGroup = RED.nodes.group(group.g);
for (var i=0; i<nodes.length; i++) {
n = nodes[i];
n.dirty = true;
var index = group.nodes.indexOf(n);
group.nodes.splice(index,1);
if (reparent && group.g) {
n.g = group.g
parentGroup.nodes.push(n);
} else {
delete n.g;
}
if (n.type === 'group') {
RED.events.emit("groups:change",n)
} else {
RED.events.emit("nodes:change",n)
}
}
markDirty(group);
}
function getNodes(group,recursive) {
var nodes = [];
group.nodes.forEach(function(n) {
nodes.push(n);
if (recursive && n.type === 'group') {
nodes = nodes.concat(getNodes(n,recursive))
}
})
return nodes;
}
function groupContains(group,item) {
if (item.g === group.id) {
return true;
}
for (var i=0;i<group.nodes.length;i++) {
if (group.nodes[i].type === "group") {
if (groupContains(group.nodes[i],item)) {
return true;
}
}
}
return false;
}
function getRootGroup(group) {
if (!group.g) {
return group;
}
return getRootGroup(RED.nodes.group(group.g))
}
function createLayoutPicker(options) {
var container = $("<div>",{style:"display:inline-block"});
var layoutHiddenInput = $("<input/>", { id: options.id, type: "hidden", value: options.value }).appendTo(container);
var layoutButton = $('<button type="button" class="red-ui-button red-ui-editor-node-appearance-button">').appendTo(container);
$('<i class="fa fa-caret-down"></i>').appendTo(layoutButton);
var layoutDispContainer = $('<div>',{class:"red-ui-search-result-node"}).appendTo(layoutButton);
var layoutDisp = $('<div>',{class:"red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"}).appendTo(layoutDispContainer);
var refreshDisplay = function() {
var val = layoutHiddenInput.val();
layoutDisp.removeClass().addClass("red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"+val)
}
layoutButton.on("click", function(e) {
var picker = $("<div/>", {
class: "red-ui-group-layout-picker"
}).css({
width: "126px"
});
var row = null;
row = $("<div/>").appendTo(picker);
var currentButton;
for (var y=0;y<2;y++) { //red-ui-group-layout-text-pos
var yComponent= "ns"[y];
row = $("<div/>").appendTo(picker);
for (var x=0;x<3;x++) {
var xComponent = ["w","","e"][x];
var val = yComponent+xComponent;
var button = $("<button/>", { class:"red-ui-search-result-node red-ui-button","data-pos":val }).appendTo(row);
button.on("click", function (e) {
e.preventDefault();
layoutHiddenInput.val($(this).data("pos"));
layoutPanel.hide()
refreshDisplay();
});
$('<div>',{class:"red-ui-group-layout-picker-cell-text red-ui-group-layout-text-pos-"+val}).appendTo(button);
if (val === layoutHiddenInput.val()) {
currentButton = button;
}
}
}
refreshDisplay();
var layoutPanel = RED.popover.panel(picker);
layoutPanel.show({
target: layoutButton,
onclose: function() {
layoutButton.focus();
}
});
if (currentButton) {
currentButton.focus();
}
})
refreshDisplay();
return container;
}
function markDirty(group) {
group.dirty = true;
while(group) {
group.dirty = true;
group = RED.nodes.group(group.g);
}
}
return {
def: groupDef,
init: init,
createGroup: createGroup,
ungroup: ungroup,
addToGroup: addToGroup,
removeFromGroup: removeFromGroup,
getNodes: getNodes,
contains: groupContains,
markDirty: markDirty
}
})();

View File

@@ -17,6 +17,8 @@ RED.keyboard = (function() {
var isMac = /Mac/i.test(window.navigator.platform);
var handlersActive = true;
var handlers = {};
var partialState;
@@ -68,6 +70,11 @@ RED.keyboard = (function() {
}
}
}
function getUserKey(action) {
var currentEditorSettings = RED.settings.get('editor') || {};
var userKeymap = currentEditorSettings.keymap || {};
return userKeymap[action];
}
function init() {
// Migrate from pre-0.18
migrateOldKeymap();
@@ -225,6 +232,9 @@ RED.keyboard = (function() {
}
}
d3.select(window).on("keydown",function() {
if (!handlersActive) {
return;
}
if (metaKeyCodes[d3.event.keyCode]) {
return;
}
@@ -250,6 +260,19 @@ RED.keyboard = (function() {
var i=0;
if (typeof key === 'string') {
if (typeof cbdown === 'string') {
if (!ondown && !defaultKeyMap.hasOwnProperty(cbdown)) {
defaultKeyMap[cbdown] = {
scope:scope,
key:key,
user:false
}
}
if (!ondown) {
var userAction = getUserKey(cbdown);
if (userAction) {
return;
}
}
actionToKeyMap[cbdown] = {scope:scope,key:key};
if (typeof ondown === 'boolean') {
actionToKeyMap[cbdown].user = ondown;
@@ -412,11 +435,9 @@ RED.keyboard = (function() {
});
revertButton.on("click", function(e) {
e.stopPropagation();
RED.keyboard.revertToDefault(object.id);
container.empty();
container.removeClass('keyboard-shortcut-entry-expanded');
var shortcut = RED.keyboard.getShortcut(object.id);
var userKeymap = RED.settings.get('keymap') || {};
// var userKeymap = RED.settings.get('keymap') || {};
var currentEditorSettings = RED.settings.get('editor') || {};
var userKeymap = currentEditorSettings.keymap || {};
@@ -424,6 +445,9 @@ RED.keyboard = (function() {
currentEditorSettings.keymap = userKeymap;
RED.settings.set('editor',currentEditorSettings);
RED.keyboard.revertToDefault(object.id);
var shortcut = RED.keyboard.getShortcut(object.id);
var obj = {
id:object.id,
scope:shortcut?shortcut.scope:undefined,
@@ -570,6 +594,13 @@ RED.keyboard = (function() {
return pane;
}
function enable() {
handlersActive = true;
}
function disable() {
handlersActive = false;
}
return {
init: init,
add: addHandler,
@@ -577,9 +608,12 @@ RED.keyboard = (function() {
getShortcut: function(actionName) {
return actionToKeyMap[actionName];
},
getUserShortcut: getUserKey,
revertToDefault: revertToDefault,
formatKey: formatKey,
validateKey: validateKey
validateKey: validateKey,
disable: disable,
enable: enable
}
})();

View File

@@ -22,7 +22,7 @@ RED.library = (function() {
var _libraryLookup = '<div id="red-ui-library-dialog-load" class="hide">'+
'<form class="form-horizontal">'+
'<div style="height: 400px; position:relative; ">'+
'<div class="red-ui-library-dialog-box" style="height: 400px; position:relative; ">'+
'<div id="red-ui-library-dialog-load-panes">'+
'<div class="red-ui-panel" id="red-ui-library-dialog-load-browser"></div>'+
'<div class="red-ui-panel">'+
@@ -41,7 +41,7 @@ RED.library = (function() {
var _librarySave = '<div id="red-ui-library-dialog-save" class="hide">'+
'<form class="form-horizontal">'+
'<div style="height: 400px; position:relative; ">'+
'<div class="red-ui-library-dialog-box" style="height: 400px; position:relative; ">'+
'<div id="red-ui-library-dialog-save-browser"></div>'+
'<div class="form-row">'+
'<label data-i18n="clipboard.export.exportAs"></label><input id="red-ui-library-dialog-save-filename" type="text">'+
@@ -64,12 +64,14 @@ RED.library = (function() {
var queryArgs = [];
var data = {};
for (var i=0; i<activeLibrary.fields.length; i++) {
for (var i=0; i < activeLibrary.fields.length; i++) {
var field = activeLibrary.fields[i];
if (field == "name") {
if (field === "name") {
data.name = name;
} else if (typeof(field) === 'object') {
data[field.name] = field.get();
} else {
data[field] = $("#"+elementPrefix+field).val();
data[field] = $("#" + elementPrefix + field).val();
}
}
data.text = activeLibrary.editor.getValue();
@@ -254,6 +256,13 @@ RED.library = (function() {
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
libraryEditor.$blockScrolling = Infinity;
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 570) {
dialogHeight = 400 - (570 - winHeight);
}
$("#red-ui-library-dialog-load .red-ui-library-dialog-box").height(dialogHeight);
$( "#red-ui-library-dialog-load" ).dialog("option","title",RED._("library.typeLibrary", {type:options.type})).dialog( "open" );
}
},
@@ -293,6 +302,15 @@ RED.library = (function() {
saveLibraryBrowser.select(listing[0].children[0]);
},200);
});
var dialogHeight = 400;
var winHeight = $(window).height();
if (winHeight < 570) {
dialogHeight = 400 - (570 - winHeight);
}
$("#red-ui-library-dialog-save .red-ui-library-dialog-box").height(dialogHeight);
$( "#red-ui-library-dialog-save" ).dialog( "open" );
}
}
@@ -454,6 +472,8 @@ RED.library = (function() {
autoOpen: false,
width: 800,
resizable: false,
open: function( event, ui ) { RED.keyboard.disable() },
close: function( event, ui ) { RED.keyboard.enable() },
classes: {
"ui-dialog": "red-ui-editor-dialog",
"ui-dialog-titlebar-close": "hide",
@@ -518,23 +538,31 @@ RED.library = (function() {
{
text: RED._("common.label.load"),
class: "primary",
click: function() {
click: function () {
if (selectedLibraryItem) {
var elementPrefix = activeLibrary.elementPrefix || "node-input-";
for (var i=0; i<activeLibrary.fields.length; i++) {
for (var i = 0; i < activeLibrary.fields.length; i++) {
var field = activeLibrary.fields[i];
$("#"+elementPrefix+field).val(selectedLibraryItem[field]);
if (typeof(field) === 'object') {
var val = selectedLibraryItem[field.name];
field.set(val);
}
else {
$("#"+elementPrefix+field).val(selectedLibraryItem[field]);
}
}
activeLibrary.editor.setValue(libraryEditor.getValue(),-1);
activeLibrary.editor.setValue(libraryEditor.getValue(), -1);
}
$( this ).dialog( "close" );
}
}
],
open: function(e) {
RED.keyboard.disable();
$(this).parent().find(".ui-dialog-titlebar-close").hide();
},
close: function(e) {
RED.keyboard.enable();
if (libraryEditor) {
libraryEditor.destroy();
libraryEditor = null;

View File

@@ -75,13 +75,16 @@ RED.palette.editor = (function() {
});
})
}
function installNodeModule(id,version,callback) {
function installNodeModule(id,version,url,callback) {
var requestBody = {
module: id
};
if (version) {
requestBody.version = version;
}
if (url) {
requestBody.url = url;
}
$.ajax({
url:"nodes",
type: "POST",
@@ -220,7 +223,11 @@ RED.palette.editor = (function() {
var setElements = nodeEntry.sets[setName];
if (set.err) {
errorCount++;
$("<li>").text(set.err).appendTo(nodeEntry.errorList);
var errMessage = set.err;
if (set.err.message) {
errMessage = set.err.message;
}
$("<li>").text(errMessage).appendTo(nodeEntry.errorList);
}
if (set.enabled) {
activeTypeCount += set.types.length;
@@ -535,8 +542,6 @@ RED.palette.editor = (function() {
return settingsPane;
}
function createSettingsPane() {
settingsPane = $('<div id="red-ui-settings-tab-palette"></div>');
var content = $('<div id="red-ui-palette-editor">'+
@@ -567,7 +572,11 @@ RED.palette.editor = (function() {
minimumActiveTabWidth: 110
});
createNodeTab(content);
createInstallTab(content);
}
function createNodeTab(content) {
var modulesTab = $('<div>',{class:"red-ui-palette-editor-tab"}).appendTo(content);
editorTabs.addTab({
@@ -623,7 +632,7 @@ RED.palette.editor = (function() {
if ($(this).hasClass('disabled')) {
return;
}
update(entry,loadedIndex[entry.name].version,container,function(err){});
update(entry,loadedIndex[entry.name].version,loadedIndex[entry.name].pkg_url,container,function(err){});
})
@@ -719,9 +728,9 @@ RED.palette.editor = (function() {
}
}
});
}
function createInstallTab(content) {
var installTab = $('<div>',{class:"red-ui-palette-editor-tab hide"}).appendTo(content);
editorTabs.addTab({
@@ -754,7 +763,6 @@ RED.palette.editor = (function() {
}
});
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
var sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
@@ -788,6 +796,7 @@ RED.palette.editor = (function() {
loadedIndex = {};
initInstallTab();
})
RED.popover.tooltip(refreshButton,RED._("palette.editor.refresh"));
packageList = $('<ol>',{style:"position: absolute;top: 79px;bottom: 0;left: 0;right: 0px;"}).appendTo(installTab).editableList({
addButton: false,
@@ -871,9 +880,88 @@ RED.palette.editor = (function() {
}
});
if (RED.settings.theme('palette.upload') !== false) {
var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
var uploadInput = uploadButton.find('input[type="file"]');
uploadInput.on("change", function(evt) {
if (this.files.length > 0) {
uploadFilenameLabel.text(this.files[0].name)
uploadToolbar.slideDown(200);
}
})
var uploadToolbar = $('<div class="red-ui-palette-editor-upload"></div>').appendTo(installTab);
var uploadForm = $('<div>').appendTo(uploadToolbar);
var uploadFilename = $('<div class="placeholder-input"><i class="fa fa-upload"></i> </div>').appendTo(uploadForm);
var uploadFilenameLabel = $('<span></span>').appendTo(uploadFilename);
var uploadButtons = $('<div class="red-ui-palette-editor-upload-buttons"></div>').appendTo(uploadForm);
$('<button class="editor-button"></button>').text(RED._("common.label.cancel")).appendTo(uploadButtons).on("click", function(evt) {
evt.preventDefault();
uploadToolbar.slideUp(200);
uploadInput.val("");
});
$('<button class="editor-button primary"></button>').text(RED._("common.label.upload")).appendTo(uploadButtons).on("click", function(evt) {
evt.preventDefault();
var spinner = RED.utils.addSpinnerOverlay(uploadToolbar, true);
var buttonRow = $('<div style="position: relative;bottom: calc(50% + 17px); padding-right: 10px;text-align: right;"></div>').appendTo(spinner);
$('<button class="red-ui-button"></button>').text(RED._("eventLog.view")).appendTo(buttonRow).on("click", function(evt) {
evt.preventDefault();
RED.actions.invoke("core:show-event-log");
});
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+uploadInput[0].files[0].name);
var data = new FormData();
data.append("tarball",uploadInput[0].files[0]);
var filename = uploadInput[0].files[0].name;
$.ajax({
url: 'nodes',
data: data,
cache: false,
contentType: false,
processData: false,
method: 'POST',
}).always(function(data,textStatus,xhr) {
spinner.remove();
uploadInput.val("");
uploadToolbar.slideUp(200);
}).fail(function(xhr,textStatus,err) {
var message = textStatus;
if (xhr.responseJSON) {
message = xhr.responseJSON.message;
}
var notification = RED.notify(RED._('palette.editor.errors.installFailed',{module: filename,message:message}),{
type: 'error',
modal: true,
fixed: true,
buttons: [
{
text: RED._("common.label.close"),
click: function() {
notification.close();
}
},{
text: RED._("eventLog.view"),
click: function() {
notification.close();
RED.actions.invoke("core:show-event-log");
}
}
]
});
uploadInput.val("");
uploadToolbar.slideUp(200);
})
})
RED.popover.tooltip(uploadButton,RED._("palette.editor.upload"));
}
$('<div id="red-ui-palette-module-install-shade" class="red-ui-palette-module-shade hide"><div class="red-ui-palette-module-shade-status"></div><img src="red/images/spin.svg" class="red-ui-palette-spinner"/></div>').appendTo(installTab);
}
function update(entry,version,container,done) {
function update(entry,version,url,container,done) {
if (RED.settings.theme('palette.editable') === false) {
done(new Error('Palette not editable'));
return;
@@ -899,7 +987,7 @@ RED.palette.editor = (function() {
RED.actions.invoke("core:show-event-log");
});
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.name+" "+version);
installNodeModule(entry.name,version,function(xhr) {
installNodeModule(entry.name,version,url,function(xhr) {
spinner.remove();
if (xhr) {
if (xhr.responseJSON) {
@@ -1024,7 +1112,7 @@ RED.palette.editor = (function() {
RED.actions.invoke("core:show-event-log");
});
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+entry.id+" "+entry.version);
installNodeModule(entry.id,entry.version,function(xhr) {
installNodeModule(entry.id,entry.version,entry.pkg_url,function(xhr) {
spinner.remove();
if (xhr) {
if (xhr.responseJSON) {

View File

@@ -105,7 +105,7 @@ RED.palette = (function() {
for (var i=0;i<words.length;i++) {
var word = words[i];
var sep = (i == 0) ? "" : " ";
var newWidth = RED.view.calculateTextWidth(currentLine+sep+word, "red-ui-palette-label", 0);
var newWidth = RED.view.calculateTextWidth(currentLine+sep+word, "red-ui-palette-label");
if (newWidth < nodeWidth) {
currentLine += sep +word;
} else {
@@ -113,12 +113,12 @@ RED.palette = (function() {
displayLines.push(currentLine);
}
while (true) {
var wordWidth = RED.view.calculateTextWidth(word, "red-ui-palette-label", 0);
var wordWidth = RED.view.calculateTextWidth(word, "red-ui-palette-label");
if (wordWidth >= nodeWidth) {
// break word if too wide
for(var j = word.length; j > 0; j--) {
var s = word.substring(0, j);
var width = RED.view.calculateTextWidth(s, "red-ui-palette-label", 0);
var width = RED.view.calculateTextWidth(s, "red-ui-palette-label");
if (width < nodeWidth) {
displayLines.push(s);
word = word.substring(j);
@@ -165,6 +165,7 @@ RED.palette = (function() {
metaData = typeInfo.set.module+" : ";
}
metaData += type;
$('<button type="button" onclick="RED.sidebar.help.show(\''+type+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
}
} catch(err) {
@@ -181,7 +182,11 @@ RED.palette = (function() {
function setIcon(element,sf) {
var icon_url = RED.utils.getNodeIcon(sf._def);
var iconContainer = element.find(".red-ui-palette-icon-container");
RED.utils.createIconElement(icon_url, iconContainer, true);
var currentIcon = iconContainer.attr("data-palette-icon");
if (currentIcon !== icon_url) {
iconContainer.attr("data-palette-icon", icon_url);
RED.utils.createIconElement(icon_url, iconContainer, true);
}
}
function getPaletteNode(type) {
@@ -224,6 +229,7 @@ RED.palette = (function() {
var iconContainer = $('<div/>', {
class: "red-ui-palette-icon-container"+(((!def.align && def.inputs !== 0 && def.outputs === 0) || "right" === def.align) ? " red-ui-palette-icon-container-right" : "")
}).appendTo(d);
iconContainer.attr("data-palette-icon", icon_url);
RED.utils.createIconElement(icon_url, iconContainer, true);
}
@@ -250,6 +256,7 @@ RED.palette = (function() {
var popover = RED.popover.create({
target:d,
trigger: "hover",
interactive: true,
width: "300px",
content: "hi",
delay: { show: 750, hide: 50 }
@@ -265,25 +272,28 @@ RED.palette = (function() {
// html: true,
// container:'body'
// });
d.on("click", function() {
RED.view.focus();
var helpText;
if (nt.indexOf("subflow:") === 0) {
helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
} else {
helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
}
// Don't look too closely. RED.sidebar.info.set will set the 'Description'
// section of the sidebar. Pass in the title of the Help section so it looks
// right.
RED.sidebar.info.set(helpText,RED._("sidebar.info.nodeHelp"));
});
// d.on("click", function() {
// RED.view.focus();
// var helpText;
// if (nt.indexOf("subflow:") === 0) {
// helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// } else {
// helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// }
// // Don't look too closely. RED.sidebar.info.set will set the 'Description'
// // section of the sidebar. Pass in the title of the Help section so it looks
// // right.
// RED.sidebar.type.show(helpText,RED._("sidebar.info.nodeHelp"));
// });
var chart = $("#red-ui-workspace-chart");
var chartSVG = $("#red-ui-workspace-chart>svg").get(0);
var activeSpliceLink;
var mouseX;
var mouseY;
var spliceTimer;
var groupTimer;
var activeGroup;
var hoverGroup;
var paletteWidth;
var paletteTop;
$(d).draggable({
@@ -295,16 +305,53 @@ RED.palette = (function() {
start: function() {
paletteWidth = $("#red-ui-palette").width();
paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top;
hoverGroup = null;
activeGroup = RED.view.getActiveGroup();
if (activeGroup) {
document.getElementById("group_select_"+activeGroup.id).classList.add("red-ui-flow-group-active-hovered");
}
RED.view.focus();
},
stop: function() { d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}},
stop: function() {
d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
}
if (activeGroup) {
document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
}
if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
},
drag: function(e,ui) {
var paletteNode = getPaletteNode(nt);
ui.originalPosition.left = paletteNode.offset().left;
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop();
if (!groupTimer) {
groupTimer = setTimeout(function() {
mouseX /= RED.view.scale();
mouseY /= RED.view.scale();
var group = RED.view.getGroupAtPoint(mouseX,mouseY);
if (group !== hoverGroup) {
if (hoverGroup) {
document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
}
if (group) {
document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
}
hoverGroup = group;
if (hoverGroup) {
$(ui.helper).data('group',hoverGroup);
} else {
$(ui.helper).removeData('group');
}
}
groupTimer = null;
},200)
}
if (def.inputs > 0 && def.outputs > 0) {
mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop();
if (!spliceTimer) {
spliceTimer = setTimeout(function() {
var nodes = [];
@@ -412,61 +459,67 @@ RED.palette = (function() {
categoryNode.show();
paletteNode.show();
}
function refreshNodeTypes() {
RED.nodes.eachSubflow(function(sf) {
var paletteNode = getPaletteNode('subflow:'+sf.id);
var portInput = paletteNode.find(".red-ui-palette-port-input");
var portOutput = paletteNode.find(".red-ui-palette-port-output");
RED.nodes.eachSubflow(refreshSubflow)
}
function refreshSubflow(sf) {
var paletteNode = getPaletteNode('subflow:'+sf.id);
var portInput = paletteNode.find(".red-ui-palette-port-input");
var portOutput = paletteNode.find(".red-ui-palette-port-output");
var paletteLabel = paletteNode.find(".red-ui-palette-label");
paletteLabel.attr("class","red-ui-palette-label" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-label-right" : ""));
var paletteLabel = paletteNode.find(".red-ui-palette-label");
paletteLabel.attr("class","red-ui-palette-label" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-label-right" : ""));
var paletteIconContainer = paletteNode.find(".red-ui-palette-icon-container");
paletteIconContainer.attr("class","red-ui-palette-icon-container" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-icon-container-right" : ""));
var paletteIconContainer = paletteNode.find(".red-ui-palette-icon-container");
paletteIconContainer.attr("class","red-ui-palette-icon-container" + (((!sf._def.align && sf.in.length !== 0 && sf.out.length === 0) || "right" === sf._def.align) ? " red-ui-palette-icon-container-right" : ""));
if (portInput.length === 0 && sf.in.length > 0) {
var portIn = document.createElement("div");
portIn.className = "red-ui-palette-port red-ui-palette-port-input";
paletteNode.append(portIn);
} else if (portInput.length !== 0 && sf.in.length === 0) {
portInput.remove();
}
if (portInput.length === 0 && sf.in.length > 0) {
var portIn = document.createElement("div");
portIn.className = "red-ui-palette-port red-ui-palette-port-input";
paletteNode.append(portIn);
} else if (portInput.length !== 0 && sf.in.length === 0) {
portInput.remove();
}
if (portOutput.length === 0 && sf.out.length > 0) {
var portOut = document.createElement("div");
portOut.className = "red-ui-palette-port red-ui-palette-port-output";
paletteNode.append(portOut);
} else if (portOutput.length !== 0 && sf.out.length === 0) {
portOutput.remove();
}
if (portOutput.length === 0 && sf.out.length > 0) {
var portOut = document.createElement("div");
portOut.className = "red-ui-palette-port red-ui-palette-port-output";
paletteNode.append(portOut);
} else if (portOutput.length !== 0 && sf.out.length === 0) {
portOutput.remove();
}
var currentLabel = paletteNode.attr("data-palette-label");
var currentInfo = paletteNode.attr("data-palette-info");
if (currentLabel !== sf.name || currentInfo !== sf.info) {
paletteNode.attr("data-palette-info",sf.info);
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||""));
setIcon(paletteNode,sf);
}
setIcon(paletteNode,sf);
var currentCategory = paletteNode.data('category');
var newCategory = (sf.category||"subflows");
if (currentCategory !== newCategory) {
var category = escapeCategory(newCategory);
createCategory(newCategory,category,category,"node-red");
var currentCategory = paletteNode.data('category');
var newCategory = (sf.category||"subflows");
if (currentCategory !== newCategory) {
var category = escapeCategory(newCategory);
createCategory(newCategory,category,category,"node-red");
var currentCategoryNode = paletteNode.closest(".red-ui-palette-category");
var newCategoryNode = $("#red-ui-palette-"+category);
newCategoryNode.append(paletteNode);
if (newCategoryNode.find(".red-ui-palette-node").length === 1) {
categoryContainers[category].open();
}
paletteNode.data('category',newCategory);
if (currentCategoryNode.find(".red-ui-palette-node").length === 0) {
if (currentCategoryNode.find("i").hasClass("expanded")) {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
}
var currentCategoryNode = paletteNode.closest(".red-ui-palette-category");
var newCategoryNode = $("#red-ui-palette-"+category);
newCategoryNode.append(paletteNode);
if (newCategoryNode.find(".red-ui-palette-node").length === 1) {
categoryContainers[category].open();
}
paletteNode.css("backgroundColor", sf.color);
});
paletteNode.data('category',newCategory);
if (currentCategoryNode.find(".red-ui-palette-node").length === 0) {
if (currentCategoryNode.find("i").hasClass("expanded")) {
currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded");
}
}
}
paletteNode.css("backgroundColor", sf.color);
}
function filterChange(val) {
@@ -504,6 +557,8 @@ RED.palette = (function() {
$('<div class="red-ui-component-footer"></div>').appendTo("#red-ui-palette");
$('<div id="red-ui-palette-shade" class="hide"></div>').appendTo("#red-ui-palette");
$("#red-ui-palette > .red-ui-palette-spinner").show();
RED.events.on('registry:node-type-added', function(nodeType) {
var def = RED.nodes.getType(nodeType);
@@ -545,7 +600,8 @@ RED.palette = (function() {
}
});
$("#red-ui-palette > .red-ui-palette-spinner").show();
RED.events.on("subflows:change",refreshSubflow);
$("#red-ui-palette-search input").searchBox({

View File

@@ -166,34 +166,42 @@ RED.projects.settings = (function() {
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
}
function editSummary(activeProject, summary, container) {
function editSummary(activeProject, summary, container, version, versionContainer) {
var editButton = container.prev();
editButton.hide();
container.empty();
versionContainer.empty();
var bg = $('<span class="button-row" style="position: relative; float: right; margin-right:0;"></span>').appendTo(container);
var input = $('<input type="text" style="width: calc(100% - 150px); margin-right: 10px;">').val(summary||"").appendTo(container);
var versionInput = $('<input type="text" style="width: calc(100% - 150px); margin-right: 10px;">').val(version||"").appendTo(versionContainer);
$('<button class="red-ui-button">' + RED._("common.label.cancel") + '</button>')
.appendTo(bg)
.on("click", function(evt) {
evt.preventDefault();
updateProjectSummary(activeProject.summary, container);
updateProjectVersion(activeProject.version, versionContainer);
editButton.show();
});
$('<button class="red-ui-button">' + RED._("common.label.save") + '</button>')
.appendTo(bg)
.on("click", function(evt) {
evt.preventDefault();
var v = input.val();
updateProjectSummary(v, container);
var spinner = utils.addSpinnerOverlay(container);
var newSummary = input.val();
var newVersion = versionInput.val();
updateProjectSummary(newSummary, container);
updateProjectVersion(newVersion, versionContainer);
var spinner = utils.addSpinnerOverlay(container).addClass('red-ui-component-spinner-contain');
var done = function(err,res) {
if (err) {
spinner.remove();
return editSummary(activeProject, summary, container);
return editSummary(activeProject, summary, container, version, versionContainer);
}
activeProject.summary = v;
activeProject.summary = newSummary;
activeProject.version = newVersion;
spinner.remove();
updateProjectSummary(activeProject.summary, container);
updateProjectVersion(activeProject.version, versionContainer);
editButton.show();
}
utils.sendRequest({
@@ -214,31 +222,39 @@ RED.projects.settings = (function() {
}
},
}
},{summary:v});
},{summary:newSummary, version: newVersion});
});
}
function updateProjectSummary(summary, container) {
container.empty();
if (summary) {
container.text(summary).removeClass('node-info-node');
container.text(summary).removeClass('red-ui-help-info-none');
} else {
container.text(RED._("sidebar.project.noSummaryAvailable")).addClass('red-ui-help-info-none');
}
}
function updateProjectVersion(version, container) {
container.empty();
if (version) {
container.text(version);
}
}
function createMainPane(activeProject) {
var pane = $('<div id="red-ui-project-settings-tab-main" class="red-ui-project-settings-tab-pane red-ui-help"></div>');
$('<h1>').text(activeProject.name).appendTo(pane);
var summary = $('<div style="position: relative">').appendTo(pane);
var summaryContent = $('<div></div>').appendTo(summary);
var versionContent = $('<div></div>').addClass('red-ui-help-info-none').appendTo(summary);
updateProjectSummary(activeProject.summary, summaryContent);
updateProjectVersion(activeProject.version, versionContent);
if (RED.user.hasPermission("projects.write")) {
$('<button class="red-ui-button red-ui-button-small" style="float: right;">' + RED._('sidebar.project.editDescription') + '</button>')
.prependTo(summary)
.on("click", function(evt) {
evt.preventDefault();
editSummary(activeProject, activeProject.summary, summaryContent);
editSummary(activeProject, activeProject.summary, summaryContent, activeProject.version, versionContent);
});
}
$('<hr>').appendTo(pane);
@@ -1017,7 +1033,7 @@ RED.projects.settings = (function() {
var credentialSecretExistingRow = $('<div class="red-ui-settings-row red-ui-settings-row-credentials"></div>').appendTo(credentialFormRows);
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.currentKey")).appendTo(credentialSecretExistingRow);
var credentialSecretExistingInput = $('<input type="password">').appendTo(credentialSecretExistingRow)
var credentialSecretExistingInput = $('<input type="text">').appendTo(credentialSecretExistingRow).typedInput({type:"cred"})
.on("change keyup paste",function() {
if (popover) {
popover.close();
@@ -1030,7 +1046,7 @@ RED.projects.settings = (function() {
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.newKey")).appendTo(credentialSecretNewRow);
var credentialSecretNewInput = $('<input type="password">').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles);
var credentialSecretNewInput = $('<input type="text">').appendTo(credentialSecretNewRow).typedInput({type:"cred"}).on("change keyup paste",checkFiles);
var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i>' + RED._("sidebar.project.projectSettings.credentialsAlert") + '</div>').hide().appendTo(credentialFormRows);
@@ -1573,8 +1589,6 @@ RED.projects.settings = (function() {
updateForm();
}
function createSettingsPane(activeProject) {
var pane = $('<div id="red-ui-project-settings-tab-settings" class="red-ui-project-settings-tab-pane red-ui-help"></div>');
createFilesSection(activeProject,pane);

View File

@@ -38,13 +38,34 @@ RED.projects.userSettings = (function() {
$('<label for="user-settings-gitconfig-email"></label>').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row);
gitEmailInput = $('<input type="text" id="user-settings-gitconfig-email">').appendTo(row);
gitEmailInput.val(currentGitSettings.user.email||"");
}
function createWorkflowSection(pane) {
var currentGitSettings = RED.settings.get('git') || {};
currentGitSettings.workflow = currentGitSettings.workflow || {};
currentGitSettings.workflow.mode = currentGitSettings.workflow.mode || "manual";
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.workflow")).appendTo(pane);
var workflowContainer = $('<div class="red-ui-settings-section"></div>').appendTo(pane);
$('<div class="red-ui-settings-section-description"></div>').appendTo(workflowContainer).text(RED._("editor:sidebar.project.userSettings.workfowTip"));
var row = $('<div class="red-ui-settings-row"></div>').appendTo(workflowContainer);
$('<label><input type="radio" name="user-setting-gitworkflow" value="manual"> <div style="margin-left: 3px; display: inline-block"><div data-i18n="editor:sidebar.project.userSettings.workflowManual"></div><div style="color:#aaa;" data-i18n="editor:sidebar.project.userSettings.workflowManualTip"></div></div></label>').appendTo(row);
row = $('<div class="red-ui-settings-row"></div>').appendTo(workflowContainer);
$('<label><input type="radio" name="user-setting-gitworkflow" value="auto"> <div style="margin-left: 3px; display: inline-block"><div data-i18n="editor:sidebar.project.userSettings.workflowAuto"></div><div style="color:#aaa;" data-i18n="editor:sidebar.project.userSettings.workflowAutoTip"></div></div></label>').appendTo(row);
workflowContainer.find('[name="user-setting-gitworkflow"][type="radio"][value="'+currentGitSettings.workflow.mode+'"]').prop('checked',true)
}
function createSSHKeySection(pane) {
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(pane);
var container = $('<div class="red-ui-settings-section"></div>').appendTo(pane);
var popover;
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(container);
var subtitle = $('<div class="red-ui-settings-section-description"></div>').appendTo(container).text(RED._("editor:sidebar.project.userSettings.sshKeysTip"));
var addKeyButton = $('<button id="user-settings-gitconfig-add-key" class="red-ui-button red-ui-button-small" style="float: right; margin-right: 10px;">'+RED._("editor:sidebar.project.userSettings.add")+'</button>')
@@ -391,6 +412,7 @@ RED.projects.userSettings = (function() {
function createSettingsPane(activeProject) {
var pane = $('<div id="red-ui-settings-tab-gitconfig" class="project-settings-tab-pane red-ui-help"></div>');
createGitUserSection(pane);
createWorkflowSection(pane);
createSSHKeySection(pane);
return pane;
}
@@ -407,6 +429,9 @@ RED.projects.userSettings = (function() {
currentGitSettings.user = currentGitSettings.user || {};
currentGitSettings.user.name = gitUsernameInput.val();
currentGitSettings.user.email = gitEmailInput.val();
currentGitSettings.workflow = currentGitSettings.workflow || {};
currentGitSettings.workflow.mode = $('[name="user-setting-gitworkflow"][type="radio"]:checked').val()
RED.settings.set('git', currentGitSettings);
}
});

View File

@@ -81,8 +81,8 @@ RED.projects = (function() {
$('<p>').text(RED._("projects.welcome.desc2")).appendTo(body);
var row = $('<div style="text-align: center"></div>').appendTo(body);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
createAsEmpty.on("click", function(e) {
e.preventDefault();
@@ -511,7 +511,8 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.clone-project.passwd")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// -----------------------------------------------------
// Repo credentials - key/passphrase -------------------
@@ -539,12 +540,12 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
dialog.dialog( "close" );
RED.userSettings.show('gitconfig');
@@ -558,8 +559,8 @@ RED.projects = (function() {
// Secret - clone
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label>'+RED._("projects.clone-project.credential-key")+'</label>').appendTo(row);
projectSecretInput = $('<input type="password"></input>').appendTo(row);
projectSecretInput = $('<input style="width: 100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
return container;
@@ -685,6 +686,8 @@ RED.projects = (function() {
}
}
},projectData).then(function() {
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
@@ -892,6 +895,7 @@ RED.projects = (function() {
$('<label class="red-ui-projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">'+RED._("projects.encryption-config.use-custom")+'</span></label>').appendTo(row);
row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row);
emptyProjectCredentialInput.typedInput({type:"cred"});
emptyProjectCredentialInput.on("change keyup paste", validateForm);
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
@@ -1167,11 +1171,11 @@ RED.projects = (function() {
row = $('<div class="form-row button-group"></div>').appendTo(container);
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
@@ -1296,6 +1300,7 @@ RED.projects = (function() {
$('<label class="red-ui-projects-edit-form-inline-label">'+RED._("projects.create.encryption-key")+'</label>').appendTo(row);
// row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row);
emptyProjectCredentialInput.typedInput({type:"cred"});
emptyProjectCredentialInput.on("change keyup paste", validateForm);
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.desc0")+'</small></label>').appendTo(row);
@@ -1354,7 +1359,8 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.create.password")+'</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
projectRepoPasswordInput.typedInput({type:"cred"});
// -----------------------------------------------------
// Repo credentials - key/passphrase -------------------
@@ -1382,12 +1388,13 @@ RED.projects = (function() {
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.passphrase")+'</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
projectRepoPassphrase.typedInput({type:"cred"});
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig');
@@ -1401,8 +1408,8 @@ RED.projects = (function() {
// Secret - clone
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
$('<label>'+RED._("projects.create.credentials-encryption-key")+'</label>').appendTo(row);
projectSecretInput = $('<input type="password"></input>').appendTo(row);
projectSecretInput = $('<input style="width:100%" type="password"></input>').appendTo(row);
projectSecretInput.typedInput({type:"cred"});
switch(options.screen||"empty") {
case "empty": createAsEmpty.trigger("click"); break;
@@ -1495,7 +1502,6 @@ RED.projects = (function() {
}
} else if (projectType === 'open') {
return switchProject(selectedProject.name,function(err,data) {
dialog.dialog( "close" );
if (err) {
if (err.code !== 'credentials_load_failed') {
console.log(RED._("projects.create.unexpected_error"),err)
@@ -1604,6 +1610,7 @@ RED.projects = (function() {
},
}
},{active:true}).then(function() {
dialog.dialog( "close" );
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
@@ -1613,25 +1620,16 @@ RED.projects = (function() {
}
function deleteProject(row,name,done) {
var cover = $('<div>').css({
background:"white",
position:"absolute",
top:0,right:0,bottom:0,left:"100%",
overflow:"hidden",
padding: "5px 20px",
transition: "left 0.4s",
whitespace: "nowrap",
width:"1000px"
}).on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').css({"lineHeight":"40px"}).text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button style="margin-left:20px" class="red-ui-button">'+RED._("common.label.cancel")+'</button>')
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
cover.remove();
done(true);
});
$('<button style="margin-left:20px" class="red-ui-button primary">'+RED._("common.label.delete")+'</button>')
$('<button class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
.appendTo(cover)
.on("click", function(e) {
e.stopPropagation();
@@ -1671,16 +1669,27 @@ RED.projects = (function() {
if (typeof buttons === 'function') {
buttons = buttons(options||{});
}
dialog.dialog('option','buttons',buttons);
dialogBody.append(container);
var dialogHeight = 590;
var winHeight = $(window).height();
if (winHeight < 750) {
dialogHeight = 590 - (750 - winHeight);
}
$(".red-ui-projects-dialog-box").height(dialogHeight);
$(".red-ui-projects-dialog-project-list-inner-container").height(Math.max(500,dialogHeight) - 180);
dialog.dialog('option','title',screen.title||"");
dialog.dialog("open");
dialog.dialog({position: { 'my': 'center top', 'at': 'center top+20', 'of': window }});
}
function createProjectList(options) {
options = options||{};
var height = options.height || "300px";
var height = options.height || "200px";
var container = $('<div></div>',{class:"red-ui-projects-dialog-project-list-container" });
var filterTerm = "";
@@ -1804,7 +1813,7 @@ RED.projects = (function() {
header.addClass("selectable");
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
$('<button class="red-ui-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
$('<button class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
.appendTo(tools)
.on("click", function(e) {
e.stopPropagation();
@@ -1958,7 +1967,8 @@ RED.projects = (function() {
var isSSH = false;
if (/^https?:\/\//.test(url)) {
$('<div class="form-row"><label for="projects-user-auth-username">'+RED._("projects.send-req.username")+'</label><input id="projects-user-auth-username" type="text"></input></div>'+
'<div class="form-row"><label for=projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
'<div class="form-row"><label for="projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
message.find("#projects-user-auth-password").typedInput({type:"cred"})
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
isSSH = true;
var row = $('<div class="form-row"></div>').appendTo(message);
@@ -1976,7 +1986,7 @@ RED.projects = (function() {
});
row = $('<div class="form-row"></div>').appendTo(message);
$('<label for="projects-user-auth-passphrase">'+RED._("projects.send-req.passphrase")+'</label>').appendTo(row);
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row);
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row).typedInput({type:"cred"});
}
var notification = RED.notify(message,{
@@ -2087,6 +2097,8 @@ RED.projects = (function() {
var branchFilterTerm = "";
var branchFilterCreateItem;
var branches = [];
var branchNames = new Set();
var remotes = [];
var branchPrefix = "";
var container = $('<div class="red-ui-projects-branch-list">').appendTo(options.container);
@@ -2094,18 +2106,30 @@ RED.projects = (function() {
delay: 200,
change: function() {
branchFilterTerm = $(this).val();
if (/(\.\.|\/\.|[?*[~^: \\]|\/\/|\/.$|\/$)/.test(branchFilterTerm)) {
// if there is a / then
// - check what preceeds it is a known remote
var valid = false;
var hasRemote = false;
var m = /^([^/]+)\/[^/.~*?\[]/.exec(branchFilterTerm);
if (m && remotes.indexOf(m[1]) > -1) {
valid = true;
hasRemote = true;
}
if (!valid && /(\.\.|\/\.|[?*[~^: \\]|\/\/|\/.$|\/$)/.test(branchFilterTerm)) {
if (!branchFilterCreateItem.hasClass("input-error")) {
branchFilterCreateItem.addClass("input-error");
branchFilterCreateItem.find("i").addClass("fa-warning").removeClass("fa-code-fork");
}
branchFilterCreateItem.find("span").text(RED._("projects.create-branch-list.invalid")+": "+branchPrefix+branchFilterTerm);
branchFilterCreateItem.find("span").text(RED._("projects.create-branch-list.invalid")+": "+(hasRemote?"":branchPrefix)+branchFilterTerm);
} else {
if (branchFilterCreateItem.hasClass("input-error")) {
branchFilterCreateItem.removeClass("input-error");
branchFilterCreateItem.find("i").removeClass("fa-warning").addClass("fa-code-fork");
}
branchFilterCreateItem.find(".red-ui-sidebar-vc-branch-list-entry-create-name").text(branchPrefix+branchFilterTerm);
branchFilterCreateItem.find("span").text(RED._("projects.create-branch-list.create")+":");
branchFilterCreateItem.find(".red-ui-sidebar-vc-branch-list-entry-create-name").text((hasRemote?"":branchPrefix)+branchFilterTerm);
}
branchList.editableList("filter");
}
@@ -2138,8 +2162,12 @@ RED.projects = (function() {
if (!entry.hasOwnProperty('commit')) {
body.name = branchFilter.val();
body.create = true;
if (options.remote) {
body.name = options.remote()+"/"+body.name;
if (options.remotes) {
var m = /^([^/]+)\/[^/.~*?\[]/.exec(body.name);
if (!m || remotes.indexOf(m[1]) === -1) {
body.name = remotes[0]+"/"+body.name;
}
}
} else {
if ($(this).hasClass('selected')) {
@@ -2154,11 +2182,17 @@ RED.projects = (function() {
},
filter: function(data) {
var isCreateEntry = (!data.hasOwnProperty('commit'));
var filterTerm = branchFilterTerm;
if (remotes.length > 0) {
var m = /^([^/]+)\/[^/.~*?\[]/.exec(filterTerm);
if (filterTerm !== "" && (!m || remotes.indexOf(m[1]) == -1)) {
filterTerm = remotes[0]+"/"+filterTerm;
}
}
return (
isCreateEntry &&
(
branchFilterTerm !== "" &&
branches.indexOf(branchPrefix+branchFilterTerm) === -1
filterTerm !== "" && !branchNames.has(filterTerm)
)
) ||
(
@@ -2173,12 +2207,14 @@ RED.projects = (function() {
branchList.editableList('empty');
var start = Date.now();
var spinner = addSpinnerOverlay(container).addClass("red-ui-component-spinner-contain");
if (options.remote) {
branchPrefix = options.remote()+"/";
if (options.remotes) {
remotes = options.remotes();
branchPrefix = remotes[0]+"/";
} else {
branchPrefix = "";
remotes = [];
}
branchNames = new Set();
sendRequest({
url: url,
type: "GET",
@@ -2190,6 +2226,7 @@ RED.projects = (function() {
branches = result.branches;
result.branches.forEach(function(b) {
branchList.editableList('addItem',b);
branchNames.add(b.name);
});
branchList.editableList('addItem',{});
setTimeout(function() {
@@ -2225,13 +2262,19 @@ RED.projects = (function() {
}
function init() {
dialog = $('<div id="red-ui-projects-dialog" class="hide red-ui-projects-edit-form"><form class="form-horizontal"></form><div class="red-ui-component-spinner hide"><img src="red/images/spin.svg"/></div></div>')
dialog = $('<div id="red-ui-projects-dialog" class="hide red-ui-projects-edit-form"><div class="red-ui-projects-dialog-box"><form class="form-horizontal"></form><div class="red-ui-component-spinner hide"><img src="red/images/spin.svg"/></div></div></div>')
.appendTo("#red-ui-editor")
.dialog({
modal: true,
autoOpen: false,
width: 600,
resizable: false,
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
},
classes: {
"ui-dialog": "red-ui-editor-dialog",
"ui-dialog-titlebar-close": "hide",
@@ -2312,6 +2355,7 @@ RED.projects = (function() {
if (data.active) {
$.getJSON("projects/"+data.active, function(project) {
activeProject = project;
RED.events.emit("projects:load",activeProject);
RED.sidebar.versionControl.refresh(true);
if (done) {
done(activeProject);

View File

@@ -293,14 +293,20 @@ RED.sidebar.versionControl = (function() {
if (activeProject) {
// TODO: this is a full refresh of the files - should be able to
// just do an incremental refresh
allChanges = {};
unstagedChangesList.editableList('empty');
stagedChangesList.editableList('empty');
unmergedChangesList.editableList('empty');
$.getJSON("projects/"+activeProject.name+"/status",function(result) {
refreshFiles(result);
});
var workflowMode = ((RED.settings.get('git') || {}).workflow || {}).mode || "manual";
if (workflowMode === 'auto') {
refresh(true);
} else {
allChanges = {};
unstagedChangesList.editableList('empty');
stagedChangesList.editableList('empty');
unmergedChangesList.editableList('empty');
$.getJSON("projects/"+activeProject.name+"/status",function(result) {
refreshFiles(result);
});
}
}
});
RED.events.on("login",function() {
@@ -830,10 +836,9 @@ RED.sidebar.versionControl = (function() {
var remoteBranchList = utils.createBranchList({
placeholder: RED._("sidebar.project.versionControl.createRemoteBranchPlaceholder"),
currentLabel: RED._("sidebar.project.versionControl.upstream"),
remote: function() {
remotes: function() {
var project = RED.projects.getActiveProject();
var remotes = Object.keys(project.git.remotes);
return remotes[0];
return Object.keys(project.git.remotes);
},
container: remoteBranchSubRow,
onselect: function(body) {

View File

@@ -23,11 +23,9 @@ RED.search = (function() {
var visible = false;
var index = {};
var keys = [];
var results = [];
var currentResults = [];
var previousActiveElement;
function indexProperty(node,label,property) {
if (typeof property === 'string' || typeof property === 'number') {
property = (""+property).toLowerCase();
@@ -61,53 +59,110 @@ RED.search = (function() {
}
for (var i=0;i<properties.length;i++) {
if (n.hasOwnProperty(properties[i])) {
if (n.type === "group" && properties[i] === "nodes") {
continue;
}
indexProperty(n, l, n[properties[i]]);
}
}
}
function indexWorkspace() {
index = {};
RED.nodes.eachWorkspace(indexNode);
RED.nodes.eachSubflow(indexNode);
RED.nodes.eachConfig(indexNode);
RED.nodes.eachNode(indexNode);
keys = Object.keys(index);
keys.sort();
keys.forEach(function(key) {
index[key] = Object.keys(index[key]).map(function(id) {
return index[key][id];
})
})
function extractFlag(val, flagName, flags) {
// is:XYZ
var regEx = new RegExp("(?:^| )is:"+flagName+"(?: |$)");
var m = regEx.exec(val);
if (m) {
val = val.replace(regEx," ").trim();
flags[flagName] = true;
}
return val;
}
function extractValue(val, flagName, flags) {
// flagName:XYZ
var regEx = new RegExp("(?:^| )"+flagName+":([^ ]+)(?: |$)");
var m
while(!!(m = regEx.exec(val))) {
val = val.replace(regEx," ").trim();
flags[flagName] = flags[flagName] || [];
flags[flagName].push(m[1]);
}
return val;
}
function search(val) {
searchResults.editableList('empty');
var results = [];
var keys = [];
var typeFilter;
var m = /(?:^| )type:([^ ]+)/.exec(val);
if (m) {
val = val.replace(/(?:^| )type:[^ ]+/,"");
typeFilter = m[1];
}
var flags = {};
val = extractFlag(val,"invalid",flags);
val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags);
// uses:<node-id>
val = extractValue(val,"uses",flags);
var hasFlags = Object.keys(flags).length > 0;
val = val.trim();
selected = -1;
results = [];
if (val.length > 0 || typeFilter) {
if (val.length > 0 || typeFilter || hasFlags) {
val = val.toLowerCase();
var i;
var j;
var list = [];
var nodes = {};
if (flags.uses) {
keys = flags.uses;
} else {
keys = Object.keys(index);
}
for (i=0;i<keys.length;i++) {
var key = keys[i];
var kpos = keys[i].indexOf(val);
if (kpos > -1) {
for (j=0;j<index[key].length;j++) {
var node = index[key][j];
var ids = Object.keys(index[key]);
for (j=0;j<ids.length;j++) {
var node = index[key][ids[j]];
var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
if (flags.uses && key === node.node.id) {
continue;
}
if (flags.hasOwnProperty("invalid")) {
var nodeIsValid = !node.node.hasOwnProperty("valid") || node.node.valid;
if (flags.invalid === nodeIsValid) {
continue;
}
}
if (flags.hasOwnProperty("config")) {
if (flags.config !== isConfigNode) {
continue;
}
}
if (flags.hasOwnProperty("subflow")) {
if (flags.subflow !== (node.node.type === 'subflow')) {
continue;
}
}
if (flags.hasOwnProperty("unused")) {
var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
(isConfigNode && node.node.users.length === 0)
if (flags.unused !== isUnused) {
continue;
}
}
if (!typeFilter || node.node.type === typeFilter) {
nodes[node.node.id] = nodes[node.node.id] = node;
nodes[node.node.id] = nodes[node.node.id] = {
node: node.node,
label: node.label
};
nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos);
}
}
@@ -121,22 +176,8 @@ RED.search = (function() {
for (i=0;i<list.length;i++) {
results.push(nodes[list[i]]);
}
if (results.length > 0) {
for (i=0;i<Math.min(results.length,25);i++) {
searchResults.editableList('addItem',results[i])
}
if (results.length > 25) {
searchResults.editableList('addItem', {
more: {
results: results,
start: 25
}
})
}
} else {
searchResults.editableList('addItem',{});
}
}
return results;
}
function ensureSelectedIsVisible() {
@@ -161,13 +202,37 @@ RED.search = (function() {
searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.searchInput">').appendTo(searchDiv).searchBox({
delay: 200,
change: function() {
search($(this).val());
searchResults.editableList('empty');
selected = -1;
currentResults = search($(this).val());
if (currentResults.length > 0) {
for (i=0;i<Math.min(currentResults.length,25);i++) {
searchResults.editableList('addItem',currentResults[i])
}
if (currentResults.length > 25) {
searchResults.editableList('addItem', {
more: {
results: currentResults,
start: 25
}
})
}
} else {
searchResults.editableList('addItem',{});
}
}
});
var copySearchContainer = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-caret-right"></button>').appendTo(searchDiv).on('click', function(evt) {
evt.preventDefault();
RED.sidebar.info.outliner.search(searchInput.val())
hide();
});
searchInput.on('keydown',function(evt) {
var children;
if (results.length > 0) {
if (currentResults.length > 0) {
if (evt.keyCode === 40) {
// Down
children = searchResults.children();
@@ -199,21 +264,21 @@ RED.search = (function() {
var object = $(children[selected]).find(".red-ui-editableList-item-content").data('data');
if (object) {
searchResults.editableList('removeItem',object);
for (i=object.more.start;i<Math.min(results.length,object.more.start+25);i++) {
searchResults.editableList('addItem',results[i])
for (i=object.more.start;i<Math.min(currentResults.length,object.more.start+25);i++) {
searchResults.editableList('addItem',currentResults[i])
}
if (results.length > object.more.start+25) {
if (currentResults.length > object.more.start+25) {
searchResults.editableList('addItem', {
more: {
results: results,
results: currentResults,
start: object.more.start+25
}
})
}
}
} else {
if (results.length > 0) {
reveal(results[Math.max(0,selected)].node);
if (currentResults.length > 0) {
reveal(currentResults[Math.max(0,selected)].node);
}
}
}
@@ -234,13 +299,13 @@ RED.search = (function() {
div.on("click", function(evt) {
evt.preventDefault();
searchResults.editableList('removeItem',object);
for (i=object.more.start;i<Math.min(results.length,object.more.start+25);i++) {
searchResults.editableList('addItem',results[i])
for (i=object.more.start;i<Math.min(currentResults.length,object.more.start+25);i++) {
searchResults.editableList('addItem',currentResults[i])
}
if (results.length > object.more.start+25) {
if (currentResults.length > object.more.start+25) {
searchResults.editableList('addItem', {
more: {
results: results,
results: currentResults,
start: object.more.start+25
}
})
@@ -253,17 +318,7 @@ RED.search = (function() {
var def = node._def;
div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container);
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div);
var colour = RED.utils.getNodeColor(node.type,def);
var icon_url = RED.utils.getNodeIcon(def,node);
if (node.type === 'tab') {
colour = "#C0DEED";
}
nodeDiv.css('backgroundColor',colour);
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
RED.utils.createNodeIcon(node).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-node-description"}).appendTo(div);
if (node.z) {
var workspace = RED.nodes.workspace(node.z);
@@ -308,7 +363,7 @@ RED.search = (function() {
$("#red-ui-palette-shade").show();
$("#red-ui-sidebar-shade").show();
$("#red-ui-sidebar-separator").hide();
indexWorkspace();
if (dialog === null) {
createDialog();
}
@@ -342,6 +397,28 @@ RED.search = (function() {
}
}
function clearIndex() {
index = {};
}
function addItemToIndex(item) {
indexNode(item);
}
function removeItemFromIndex(item) {
var keys = Object.keys(index);
for (var i=0,l=keys.length;i<l;i++) {
delete index[keys[i]][item.id];
if (Object.keys(index[keys[i]]).length === 0) {
delete index[keys[i]];
}
}
}
function updateItemOnIndex(item) {
removeItemFromIndex(item);
addItemToIndex(item);
}
function init() {
RED.actions.add("core:search",show);
@@ -358,12 +435,33 @@ RED.search = (function() {
$("#red-ui-editor-shade").on('mousedown',hide);
$("#red-ui-palette-shade").on('mousedown',hide);
$("#red-ui-sidebar-shade").on('mousedown',hide);
RED.events.on("workspace:clear", clearIndex);
RED.events.on("flows:add", addItemToIndex);
RED.events.on("flows:remove", removeItemFromIndex);
RED.events.on("flows:change", updateItemOnIndex);
RED.events.on("subflows:add", addItemToIndex);
RED.events.on("subflows:remove", removeItemFromIndex);
RED.events.on("subflows:change", updateItemOnIndex);
RED.events.on("nodes:add",addItemToIndex);
RED.events.on("nodes:remove",removeItemFromIndex);
RED.events.on("nodes:change",updateItemOnIndex);
RED.events.on("groups:add",addItemToIndex);
RED.events.on("groups:remove",removeItemFromIndex);
RED.events.on("groups:change",updateItemOnIndex);
}
return {
init: init,
show: show,
hide: hide
hide: hide,
search: search
};
})();

View File

@@ -232,7 +232,11 @@ RED.sidebar = (function() {
}
},
// minimumActiveTabWidth: 70,
collapsible: true
collapsible: true,
onreorder: function(order) {
RED.settings.set("editor.sidebar.order",order);
},
order: RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])
// scrollable: true
});
@@ -250,6 +254,7 @@ RED.sidebar = (function() {
RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar");
showSidebar();
RED.sidebar.info.init();
RED.sidebar.help.init();
RED.sidebar.config.init();
RED.sidebar.context.init();
// hide info bar at start if screen rather narrow...

View File

@@ -25,5 +25,7 @@ RED.state = {
IMPORT_DRAGGING: 8,
QUICK_JOINING: 9,
PANNING: 10,
SELECTING_NODE: 11
SELECTING_NODE: 11,
GROUP_DRAGGING: 12,
GROUP_RESIZE: 13
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,336 @@
/**
* 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.sidebar.help = (function() {
var content;
var toolbar;
var helpSection;
var panels;
var panelRatio;
var helpTopics = [];
var treeList;
var tocPanel;
var helpIndex = {};
function resizeStack() {
var h = $(content).parent().height() - toolbar.outerHeight();
panels.resize(h)
}
function init() {
content = document.createElement("div");
content.className = "red-ui-sidebar-info"
toolbar = $("<div>", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(content);
$('<span class="button-group"><a id="red-ui-sidebar-help-show-toc" class="red-ui-button red-ui-button-small selected" href="#"><i class="fa fa-list-ul"></i></a></span>').appendTo(toolbar)
var showTOCButton = toolbar.find('#red-ui-sidebar-help-show-toc')
RED.popover.tooltip(showTOCButton,RED._("sidebar.help.showTopics"));
showTOCButton.on("click",function(e) {
e.preventDefault();
if ($(this).hasClass('selected')) {
hideTOC();
} else {
showTOC();
}
});
var stackContainer = $("<div>",{class:"red-ui-sidebar-help-stack"}).appendTo(content);
tocPanel = $("<div>", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer);
var helpPanel = $("<div>").css({
"overflow-y": "scroll"
}).appendTo(stackContainer);
panels = RED.panels.create({
container: stackContainer
})
panels.ratio(0.3);
helpSearch = $('<input type="text" data-i18n="[placeholder]sidebar.help.search">').appendTo(toolbar).searchBox({
style: "compact",
delay: 100,
change: function() {
var val = $(this).val().toLowerCase();
if (val) {
showTOC();
var c = treeList.treeList('filter',function(item) {
if (item.depth === 0) {
return true;
}
return (item.nodeType && item.nodeType.indexOf(val) > -1) ||
(item.subflowLabel && item.subflowLabel.indexOf(val) > -1)
},true)
} else {
treeList.treeList('filter',null);
var selected = treeList.treeList('selected');
if (selected.id) {
treeList.treeList('show',selected.id);
}
}
}
})
helpSection = $("<div>",{class:"red-ui-help"}).css({
"padding":"6px",
}).appendTo(helpPanel)
$('<span class="red-ui-help-info-none">'+RED._("sidebar.help.noHelp")+'</span>').appendTo(helpSection);
treeList = $("<div>").css({width: "100%"}).appendTo(tocPanel).treeList({data: []})
treeList.on('treelistselect', function(e,item) {
if (item.nodeType) {
showHelp(item.nodeType);
}
})
RED.sidebar.addTab({
id: "help",
label: RED._("sidebar.help.label"),
name: RED._("sidebar.help.name"),
iconClass: "fa fa-book",
action:"core:show-help-tab",
content: content,
pinned: true,
enableOnEdit: true,
onchange: function() {
resizeStack()
}
});
$(window).on("resize", resizeStack);
$(window).on("focus", resizeStack);
RED.events.on('registry:node-type-added', queueRefresh);
RED.events.on('registry:node-type-removed', queueRefresh);
RED.events.on('subflows:change', refreshSubflow);
RED.actions.add("core:show-help-tab",show);
}
var refreshTimer;
function queueRefresh() {
if (!refreshTimer) {
refreshTimer = setTimeout(function() {
refreshTimer = null;
refreshHelpIndex();
},500);
}
}
function refreshSubflow(sf) {
var item = treeList.treeList('get',"node-type:subflow:"+sf.id);
item.subflowLabel = sf._def.label().toLowerCase();
item.treeList.replaceElement(getNodeLabel({_def:sf._def,type:sf._def.label()}));
}
function hideTOC() {
var tocButton = $('#red-ui-sidebar-help-show-toc')
if (tocButton.hasClass('selected')) {
tocButton.removeClass('selected');
panelRatio = panels.ratio();
tocPanel.css({"transition":"height 0.2s"})
panels.ratio(0)
setTimeout(function() {
tocPanel.css({"transition":""})
},250);
}
}
function showTOC() {
var tocButton = $('#red-ui-sidebar-help-show-toc')
if (!tocButton.hasClass('selected')) {
tocButton.addClass('selected');
tocPanel.css({"transition":"height 0.2s"})
panels.ratio(Math.max(0.3,Math.min(panelRatio,0.7)));
setTimeout(function() {
tocPanel.css({"transition":""})
var selected = treeList.treeList('selected');
if (selected.id) {
treeList.treeList('show',selected);
}
},250);
}
}
function refreshHelpIndex() {
helpTopics = [];
var modules = RED.nodes.registry.getModuleList();
var moduleNames = Object.keys(modules);
moduleNames.sort();
var helpData = [{
label: RED._("sidebar.help.nodeHelp"),
children: [],
expanded: true
}]
var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
if (subflows.length > 0) {
helpData[0].children.push({
label: RED._("menu.label.subflows"),
children: []
})
subflows.forEach(function(nodeType) {
var sf = RED.nodes.getType(nodeType);
helpData[0].children[0].children.push({
id:"node-type:"+nodeType,
nodeType: nodeType,
subflowLabel: sf.label().toLowerCase(),
element: getNodeLabel({_def:sf,type:sf.label()})
})
})
}
moduleNames.forEach(function(moduleName) {
var module = modules[moduleName];
var nodeTypes = [];
var setNames = Object.keys(module.sets);
setNames.forEach(function(setName) {
module.sets[setName].types.forEach(function(nodeType) {
if ($("script[data-help-name='"+nodeType+"']").length) {
nodeTypes.push({
id: "node-type:"+nodeType,
nodeType: nodeType,
element:getNodeLabel({_def:RED.nodes.getType(nodeType),type:nodeType})
})
}
})
})
if (nodeTypes.length > 0) {
nodeTypes.sort(function(A,B) {
return A.nodeType.localeCompare(B.nodeType)
})
helpData[0].children.push({
id: moduleName,
icon: "fa fa-cube",
label: moduleName,
children: nodeTypes
})
}
});
treeList.treeList("data",helpData);
}
function getNodeLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
$('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).text(n.name||n.type).appendTo(contentDiv);
return div;
}
function showHelp(nodeType) {
helpSection.empty();
var helpText;
var title;
var m = /^subflow(:(.+))?$/.exec(nodeType);
if (m && m[2]) {
var subflowNode = RED.nodes.subflow(m[2]);
helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
title = subflowNode.name || nodeType;
} else {
helpText = $("script[data-help-name='"+nodeType+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
title = nodeType;
}
setInfoText(title, helpText, helpSection);
var ratio = panels.ratio();
if (ratio > 0.7) {
panels.ratio(0.7)
}
treeList.treeList("show","node-type:"+nodeType)
treeList.treeList("select","node-type:"+nodeType, false);
}
function show(type, bringToFront) {
if (bringToFront !== false) {
RED.sidebar.show("help");
}
if (type) {
// hideTOC();
showHelp(type);
}
resizeStack();
}
// TODO: DRY - projects.js
function addTargetToExternalLinks(el) {
$(el).find("a").each(function(el) {
var href = $(this).attr('href');
if (/^https?:/.test(href)) {
$(this).attr('target','_blank');
}
});
return el;
}
function setInfoText(title, infoText,target) {
if (title) {
$("<h1>",{class:"red-ui-help-title"}).text(title).appendTo(target);
}
var info = addTargetToExternalLinks($('<div class="red-ui-help"><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(infoText)+'">'+infoText+'</span></div>')).appendTo(target);
info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
var foldingHeader = "H3";
info.find(foldingHeader).wrapInner('<a class="red-ui-help-info-header expanded" href="#"></a>')
.find("a").prepend('<i class="fa fa-angle-right">').on("click", function(e) {
e.preventDefault();
var isExpanded = $(this).hasClass('expanded');
var el = $(this).parent().next();
while(el.length === 1 && el[0].nodeName !== foldingHeader) {
el.toggle(!isExpanded);
el = el.next();
}
$(this).toggleClass('expanded',!isExpanded);
})
target.parent().scrollTop(0);
}
function set(html,title) {
$(helpSection).empty();
setInfoText(title,html,helpSection);
hideTOC();
show();
}
function refreshSelection(selection) {
if (selection === undefined) {
selection = RED.view.selection();
}
if (selection.nodes) {
if (selection.nodes.length == 1) {
var node = selection.nodes[0];
if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports
} else if (node.type !== 'group'){
showHelp(node.type);
}
}
}
}
RED.events.on("view:selection-changed",refreshSelection);
return {
init: init,
show: show,
set: set
}
})();

View File

@@ -0,0 +1,629 @@
RED.sidebar.info.outliner = (function() {
var treeList;
var searchInput;
var activeSearch;
var projectInfo;
var projectInfoLabel;
var flowList;
var subflowList;
var globalConfigNodes;
var objects = {};
var missingParents = {};
var configNodeTypes;
function getFlowData() {
var flowData = [
{
label: RED._("menu.label.flows"),
expanded: true,
children: []
},
{
id: "__subflow__",
label: RED._("menu.label.subflows"),
children: [
getEmptyItem("__subflow__")
]
},
{
id: "__global__",
flow: "__global__",
label: RED._("sidebar.info.globalConfig"),
types: {},
children: [
getEmptyItem("__global__")
]
}
]
flowList = flowData[0];
subflowList = flowData[1];
globalConfigNodes = flowData[2];
configNodeTypes = { __global__: globalConfigNodes};
return flowData;
}
function getProjectLabel(p) {
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
div.css("width", "calc(100% - 40px)");
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
contentDiv.text(p.name);
var controls = $('<div>',{class:"red-ui-info-outline-item-controls"}).appendTo(div);
var editProjectButton = $('<button class="red-ui-button red-ui-button-small" style="position:absolute;right:5px;top: 3px;"><i class="fa fa-ellipsis-h"></i></button>')
.appendTo(controls)
.on("click", function(evt) {
evt.preventDefault();
RED.projects.editProject();
});
RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings'));
return div;
}
var empties = {};
function getEmptyItem(id) {
var item = {
empty: true,
element: $('<div class="red-ui-info-outline-item red-ui-info-outline-item-empty">').text(RED._("sidebar.info.empty")),
}
empties[id] = item;
return item;
}
function getNodeLabelText(n) {
var label = n.name || n.type+": "+n.id;
if (n._def.label) {
try {
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
} catch(err) {
console.log("Definition error: "+n.type+".label",err);
}
}
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
return label;
}
function getNodeLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
addControls(n, div);
return div;
}
function getFlowLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
var label = (typeof n === "string")? n : n.label;
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
contentDiv.text(label);
addControls(n, div);
return div;
}
function getSubflowLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
addControls(n, div);
return div;
// var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
// var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
// contentDiv.text(n.name || n.id);
// addControls(n, div);
// return div;
}
function addControls(n,div) {
var controls = $('<div>',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div);
if (n._def.category === "config" && n.type !== "group") {
var userCountBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.users.length).appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.search.show("uses:"+n.id);
})
RED.popover.tooltip(userCountBadge,function() { return RED._('editor.nodesUse',{count:n.users.length})});
}
if (n._def.button) {
var triggerButton = $('<button type="button" class="red-ui-info-outline-item-control-action red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.view.clickNodeButton(n);
})
RED.popover.tooltip(triggerButton,RED._("sidebar.info.triggerAction"));
}
// $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-eye"></i></button>').appendTo(controls).on("click",function(evt) {
// evt.preventDefault();
// evt.stopPropagation();
// RED.view.reveal(n.id);
// })
if (n.type !== 'group' && n.type !== 'subflow') {
var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (n.type === 'tab') {
if (n.disabled) {
RED.workspaces.enable(n.id)
} else {
RED.workspaces.disable(n.id)
}
} else {
// TODO: this ought to be a utility function in RED.nodes
var historyEvent = {
t: "edit",
node: n,
changed: n.changed,
changes: {
d: n.d
},
dirty:RED.nodes.dirty()
}
if (n.d) {
delete n.d;
} else {
n.d = true;
}
n.dirty = true;
n.changed = true;
RED.events.emit("nodes:change",n);
RED.nodes.dirty(true)
RED.view.redraw();
}
});
RED.popover.tooltip(toggleButton,function() {
return RED._("common.label."+(((n.type==='tab' && n.disabled) || (n.type!=='tab' && n.d))?"enable":"disable"));
});
} else {
$('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
}
controls.find("button").on("dblclick", function(evt) {
evt.preventDefault();
evt.stopPropagation();
})
}
function onProjectLoad(activeProject) {
objects = {};
var newFlowData = getFlowData();
projectInfoLabel.empty();
getProjectLabel(activeProject).appendTo(projectInfoLabel);
projectInfo.show();
treeList.treeList('data',newFlowData);
}
function build() {
var container = $("<div>", {class:"red-ui-info-outline"}).css({'height': '100%'});
var toolbar = $("<div>", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(container);
searchInput = $('<input type="text" data-i18n="[placeholder]menu.label.search">').appendTo(toolbar).searchBox({
style: "compact",
delay: 500,
change: function() {
var val = $(this).val();
var searchResults = RED.search.search(val);
if (val) {
activeSearch = val;
var resultMap = {};
for (var i=0,l=searchResults.length;i<l;i++) {
resultMap[searchResults[i].node.id] = true;
}
var c = treeList.treeList('filter',function(item) {
if (item.depth === 0) {
return true;
}
return item.id && objects[item.id] && resultMap[item.id]
},true)
} else {
activeSearch = null;
treeList.treeList('filter',null);
var selected = treeList.treeList('selected');
if (selected.id) {
treeList.treeList('show',selected.id);
}
}
},
options: [
{label:RED._("sidebar.info.search.configNodes"), value:"is:config"},
{label:RED._("sidebar.info.search.unusedConfigNodes"), value:"is:config is:unused"},
{label:RED._("sidebar.info.search.invalidNodes"), value: "is:invalid"},
{label:RED._("sidebar.info.search.uknownNodes"), value: "type:unknown"},
{label:RED._("sidebar.info.search.unusedSubflows"), value:"is:subflow is:unused"},
]
});
projectInfo = $('<div class="red-ui-treeList-label red-ui-info-outline-project"><span class="red-ui-treeList-icon"><i class="fa fa-archive"></i></span></div>').hide().appendTo(container)
projectInfoLabel = $('<span>').appendTo(projectInfo);
// <div class="red-ui-info-outline-item red-ui-info-outline-item-flow" style=";"><div class="red-ui-search-result-description red-ui-info-outline-item-label">Space Monkey</div><div class="red-ui-info-outline-item-controls"><button class="red-ui-button red-ui-button-small" style="position:absolute;right:5px;"><i class="fa fa-ellipsis-h"></i></button></div></div></div>').appendTo(container)
treeList = $("<div>").css({width: "100%"}).appendTo(container).treeList({
data:getFlowData()
})
treeList.on('treelistselect', function(e,item) {
var node = RED.nodes.node(item.id) || RED.nodes.group(item.id);
if (node) {
if (node.type === 'group' || node._def.category !== "config") {
RED.view.select({nodes:[node]})
} else {
RED.view.select({nodes:[]})
}
}
})
treeList.on('treelistconfirm', function(e,item) {
var node = RED.nodes.node(item.id);
if (node) {
if (node._def.category === "config") {
RED.editor.editConfig("", node.type, node.id);
} else {
RED.editor.edit(node);
}
}
})
RED.events.on("projects:load", onProjectLoad)
RED.events.on("flows:add", onFlowAdd)
RED.events.on("flows:remove", onObjectRemove)
RED.events.on("flows:change", onFlowChange)
RED.events.on("flows:reorder", onFlowsReorder)
RED.events.on("subflows:add", onSubflowAdd)
RED.events.on("subflows:remove", onObjectRemove)
RED.events.on("subflows:change", onSubflowChange)
RED.events.on("nodes:add",onNodeAdd);
RED.events.on("nodes:remove",onObjectRemove);
RED.events.on("nodes:change",onNodeChange);
RED.events.on("groups:add",onNodeAdd);
RED.events.on("groups:remove",onObjectRemove);
RED.events.on("groups:change",onNodeChange);
RED.events.on("workspace:clear", onWorkspaceClear)
return container;
}
function onWorkspaceClear() {
treeList.treeList('data',getFlowData());
}
function onFlowAdd(ws) {
objects[ws.id] = {
id: ws.id,
element: getFlowLabel(ws),
children:[],
deferBuild: true,
icon: "red-ui-icons red-ui-icons-flow",
gutter: getGutter(ws)
}
if (missingParents[ws.id]) {
objects[ws.id].children = missingParents[ws.id];
delete missingParents[ws.id]
} else {
objects[ws.id].children.push(getEmptyItem(ws.id));
}
flowList.treeList.addChild(objects[ws.id])
objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
updateSearch();
}
function onFlowChange(n) {
var existingObject = objects[n.id];
var label = n.label || n.id;
var newlineIndex = label.indexOf("\\n");
if (newlineIndex > -1) {
label = label.substring(0,newlineIndex)+"...";
}
existingObject.element.find(".red-ui-info-outline-item-label").text(label);
existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
updateSearch();
}
function onFlowsReorder(order) {
var indexMap = {};
order.forEach(function(id,index) {
indexMap[id] = index;
})
flowList.treeList.sortChildren(function(A,B) {
if (A.id === "__global__") { return -1 }
if (B.id === "__global__") { return 1 }
return indexMap[A.id] - indexMap[B.id]
})
}
function onSubflowAdd(sf) {
objects[sf.id] = {
id: sf.id,
element: getNodeLabel(sf),
children:[],
deferBuild: true,
gutter: getGutter(sf)
}
if (missingParents[sf.id]) {
objects[sf.id].children = missingParents[sf.id];
delete missingParents[sf.id]
} else {
objects[sf.id].children.push(getEmptyItem(sf.id));
}
if (empties["__subflow__"]) {
empties["__subflow__"].treeList.remove();
delete empties["__subflow__"];
}
subflowList.treeList.addChild(objects[sf.id])
updateSearch();
}
function onSubflowChange(sf) {
var existingObject = objects[sf.id];
existingObject.treeList.replaceElement(getNodeLabel(sf));
// existingObject.element.find(".red-ui-info-outline-item-label").text(n.name || n.id);
RED.nodes.eachNode(function(n) {
if (n.type == "subflow:"+sf.id) {
var sfInstance = objects[n.id];
sfInstance.treeList.replaceElement(getNodeLabel(n));
}
});
updateSearch();
}
function onNodeChange(n) {
var existingObject = objects[n.id];
var parent = n.g||n.z||"__global__";
var nodeLabelText = getNodeLabelText(n);
if (nodeLabelText) {
existingObject.element.find(".red-ui-info-outline-item-label").text(nodeLabelText);
} else {
existingObject.element.find(".red-ui-info-outline-item-label").html("&nbsp;");
}
var existingParent = existingObject.parent.id;
if (!existingParent) {
existingParent = existingObject.parent.parent.flow
}
if (parent !== existingParent) {
var parentItem = existingObject.parent;
existingObject.treeList.remove(true);
if (parentItem.children.length === 0) {
if (parentItem.config) {
// this is a config
parentItem.treeList.remove();
// console.log("Removing",n.type,"from",parentItem.parent.id||parentItem.parent.parent.id)
delete configNodeTypes[parentItem.parent.id||parentItem.parent.parent.id].types[n.type];
if (parentItem.parent.children.length === 0) {
if (parentItem.parent.id === "__global__") {
parentItem.parent.treeList.addChild(getEmptyItem(parentItem.parent.id));
} else {
delete configNodeTypes[parentItem.parent.parent.id];
parentItem.parent.treeList.remove();
if (parentItem.parent.parent.children.length === 0) {
parentItem.parent.parent.treeList.addChild(getEmptyItem(parentItem.parent.parent.id));
}
}
}
} else {
parentItem.treeList.addChild(getEmptyItem(parentItem.id));
}
}
if (n._def.category === 'config' && n.type !== 'group') {
// This must be a config node that has been rescoped
createFlowConfigNode(parent,n.type);
configNodeTypes[parent].types[n.type].treeList.addChild(objects[n.id]);
} else {
// This is a node that has moved groups
if (empties[parent]) {
empties[parent].treeList.remove();
delete empties[parent];
}
objects[parent].treeList.addChild(existingObject)
}
// if (parent === "__global__") {
// // Global always exists here
// if (!configNodeTypes[parent][n.type]) {
// configNodeTypes[parent][n.type] = {
// config: true,
// label: n.type,
// children: []
// }
// globalConfigNodes.treeList.addChild(configNodeTypes[parent][n.type])
// }
// configNodeTypes[parent][n.type].treeList.addChild(existingObject);
// } else {
// if (empties[parent]) {
// empties[parent].treeList.remove();
// delete empties[parent];
// }
// objects[parent].treeList.addChild(existingObject)
// }
}
existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.d)
if (n._def.category === "config" && n.type !== 'group') {
existingObject.element.find(".red-ui-info-outline-item-control-users").text(n.users.length);
}
updateSearch();
}
function onObjectRemove(n) {
var existingObject = objects[n.id];
existingObject.treeList.remove();
delete objects[n.id]
// If this is a group being removed, it may have an empty item
if (empties[n.id]) {
delete empties[n.id];
}
var parent = existingObject.parent;
if (parent.children.length === 0) {
if (parent.config) {
// this is a config
parent.treeList.remove();
delete configNodeTypes[parent.parent.id||n.z].types[n.type];
if (parent.parent.children.length === 0) {
if (parent.parent.id === "__global__") {
parent.parent.treeList.addChild(getEmptyItem(parent.parent.id));
} else {
delete configNodeTypes[n.z];
parent.parent.treeList.remove();
if (parent.parent.parent.children.length === 0) {
parent.parent.parent.treeList.addChild(getEmptyItem(parent.parent.parent.id));
}
}
}
} else {
parent.treeList.addChild(getEmptyItem(parent.id));
}
}
}
function getGutter(n) {
var span = $("<span>",{class:"red-ui-info-outline-gutter"});
var revealButton = $('<button type="button" class="red-ui-info-outline-item-control-reveal red-ui-button red-ui-button-small"><i class="fa fa-search"></i></button>').appendTo(span).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.view.reveal(n.id);
})
RED.popover.tooltip(revealButton,RED._("sidebar.info.find"));
return span;
}
function createFlowConfigNode(parent,type) {
// console.log("createFlowConfig",parent,type,configNodeTypes[parent]);
if (empties[parent]) {
empties[parent].treeList.remove();
delete empties[parent];
}
if (!configNodeTypes[parent]) {
// There is no 'config nodes' item in the parent flow
configNodeTypes[parent] = {
config: true,
flow: parent,
types: {},
label: RED._("menu.label.displayConfig"),
children: []
}
objects[parent].treeList.insertChildAt(configNodeTypes[parent],0);
// console.log("CREATED", parent)
}
if (!configNodeTypes[parent].types[type]) {
configNodeTypes[parent].types[type] = {
config: true,
label: type,
children: []
}
configNodeTypes[parent].treeList.addChild(configNodeTypes[parent].types[type]);
// console.log("CREATED", parent,type)
}
}
function onNodeAdd(n) {
objects[n.id] = {
id: n.id,
element: getNodeLabel(n),
gutter: getGutter(n)
}
if (n.type === "group") {
objects[n.id].children = [];
objects[n.id].deferBuild = true;
if (missingParents[n.id]) {
objects[n.id].children = missingParents[n.id];
delete missingParents[n.id]
}
}
var parent = n.g||n.z||"__global__";
if (n._def.category !== "config" || n.type === 'group') {
if (objects[parent]) {
if (empties[parent]) {
empties[parent].treeList.remove();
delete empties[parent];
}
if (objects[parent].treeList) {
objects[parent].treeList.addChild(objects[n.id]);
} else {
objects[parent].children.push(objects[n.id])
}
} else {
missingParents[parent] = missingParents[parent]||[];
missingParents[parent].push(objects[n.id])
}
} else {
createFlowConfigNode(parent,n.type);
configNodeTypes[parent].types[n.type].treeList.addChild(objects[n.id]);
}
objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d)
updateSearch();
}
var updateSearchTimer;
function updateSearch() {
if (updateSearchTimer) {
clearTimeout(updateSearchTimer)
}
if (activeSearch) {
updateSearchTimer = setTimeout(function() {
searchInput.searchBox("change");
},100);
}
}
function onSelectionChanged(selection) {
// treeList.treeList('clearSelection');
}
return {
build: build,
search: function(val) {
searchInput.searchBox('value',val)
},
select: function(node) {
if (node) {
if (Array.isArray(node)) {
treeList.treeList('select', node.map(function(n) { return objects[n.id] }), false)
} else {
treeList.treeList('select', objects[node.id], false)
}
} else {
treeList.treeList('clearSelection')
}
},
reveal: function(node) {
treeList.treeList('show', objects[node.id])
}
}
})();

View File

@@ -16,16 +16,32 @@
RED.sidebar.info = (function() {
var content;
var sections;
var propertiesSection;
var panels;
var infoSection;
var helpSection;
var propertiesPanelContent;
var propertiesPanelHeader;
var propertiesPanelHeaderIcon;
var propertiesPanelHeaderLabel;
var propertiesPanelHeaderReveal;
var propertiesPanelHeaderHelp;
var selectedObject;
var tipContainer;
var tipBox;
// TODO: remove this
var expandedSections = {
"property": false
};
function resizeStack() {
if (panels) {
var h = $(content).parent().height() - tipContainer.outerHeight();
panels.resize(h)
}
}
function init() {
content = document.createElement("div");
@@ -35,31 +51,81 @@ RED.sidebar.info = (function() {
var stackContainer = $("<div>",{class:"red-ui-sidebar-info-stack"}).appendTo(content);
sections = RED.stack.create({
container: stackContainer
}).hide();
var outlinerPanel = $("<div>").css({
"overflow": "hidden",
"height": "calc(70%)"
}).appendTo(stackContainer);
var propertiesPanel = $("<div>").css({
"overflow":"hidden",
"height":"100%",
"display": "flex",
"flex-direction": "column"
}).appendTo(stackContainer);
propertiesPanelHeader = $("<div>", {class:"red-ui-palette-header red-ui-info-header"}).css({
"flex":"0 0 auto"
}).appendTo(propertiesPanel);
propertiesSection = sections.add({
title: RED._("sidebar.info.info"),
collapsible: true
propertiesPanelHeaderIcon = $("<span>").appendTo(propertiesPanelHeader);
propertiesPanelHeaderLabel = $("<span>").appendTo(propertiesPanelHeader);
propertiesPanelHeaderHelp = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
position: 'absolute',
top: '12px',
right: '32px'
}).on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (selectedObject) {
RED.sidebar.help.show(selectedObject.type);
}
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderHelp,RED._("sidebar.help.showHelp"));
propertiesPanelHeaderReveal = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
position: 'absolute',
top: '12px',
right: '8px'
}).on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (selectedObject) {
RED.sidebar.info.outliner.reveal(selectedObject);
RED.view.reveal(selectedObject.id);
}
}).appendTo(propertiesPanelHeader);
RED.popover.tooltip(propertiesPanelHeaderReveal,RED._("sidebar.help.showInOutline"));
propertiesPanelContent = $("<div>").css({
"flex":"1 1 auto",
"overflow-y":"scroll",
}).appendTo(propertiesPanel);
panels = RED.panels.create({container: stackContainer})
panels.ratio(0.6);
RED.sidebar.info.outliner.build().appendTo(outlinerPanel);
RED.sidebar.addTab({
id: "info",
label: RED._("sidebar.info.label"),
name: RED._("sidebar.info.name"),
iconClass: "fa fa-info",
action:"core:show-info-tab",
content: content,
pinned: true,
enableOnEdit: true
});
propertiesSection.expand();
infoSection = sections.add({
title: RED._("sidebar.info.desc"),
collapsible: true
});
infoSection.expand();
infoSection.content.css("padding","6px");
RED.events.on("sidebar:resize", resizeStack);
helpSection = sections.add({
title: RED._("sidebar.info.nodeHelp"),
collapsible: true
});
helpSection.expand();
helpSection.content.css("padding","6px");
$(window).on("resize", resizeStack);
$(window).on("focus", resizeStack);
var tipContainer = $('<div class="red-ui-help-tips"></div>').appendTo(content);
// Tip Box
tipContainer = $('<div class="red-ui-help-tips"></div>').appendTo(content);
tipBox = $('<div class="red-ui-help-tip"></div>').appendTo(tipContainer);
var tipButtons = $('<div class="red-ui-help-tips-buttons"></div>').appendTo(tipContainer);
@@ -75,17 +141,6 @@ RED.sidebar.info = (function() {
RED.actions.invoke("core:toggle-show-tips");
RED.notify(RED._("sidebar.info.showTips"));
});
RED.sidebar.addTab({
id: "info",
label: RED._("sidebar.info.label"),
name: RED._("sidebar.info.name"),
iconClass: "fa fa-info",
action:"core:show-info-tab",
content: content,
pinned: true,
enableOnEdit: true
});
if (tips.enabled()) {
tips.start();
} else {
@@ -113,46 +168,36 @@ RED.sidebar.info = (function() {
refreshSelection();
return;
}
sections.show();
$(propertiesSection.content).empty();
$(infoSection.content).empty();
$(helpSection.content).empty();
infoSection.title.text(RED._("sidebar.info.desc"));
$(propertiesPanelContent).empty();
var propRow;
var table = $('<table class="red-ui-info-table"></table>').appendTo(propertiesSection.content);
var table = $('<table class="red-ui-info-table"></table>').appendTo(propertiesPanelContent);
var tableBody = $('<tbody>').appendTo(table);
var subflowNode;
var subflowUserCount;
var activeProject = RED.projects.getActiveProject();
if (activeProject) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+ RED._("sidebar.project.name") + '</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).text(activeProject.name||"");
$('<tr class="red-ui-help-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody);
var editProjectButton = $('<button class="red-ui-button red-ui-button-small" style="position:absolute;right:2px;"><i class="fa fa-ellipsis-h"></i></button>')
.appendTo(propRow.children()[1])
.on("click", function(evt) {
evt.preventDefault();
RED.projects.editProject();
});
RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings'));
}
propertiesSection.container.show();
infoSection.container.show();
helpSection.container.show();
if (node === null) {
RED.sidebar.info.outliner.select(null);
return;
} else if (Array.isArray(node)) {
// Multiple things selected
// - hide help and info sections
RED.sidebar.info.outliner.select(node);
propertiesPanelHeaderIcon.empty();
RED.utils.createNodeIcon({type:"_selection_"}).appendTo(propertiesPanelHeaderIcon);
propertiesPanelHeaderLabel.text("Selection");
propertiesPanelHeaderReveal.hide();
propertiesPanelHeaderHelp.hide();
selectedObject = null;
var types = {
nodes:0,
flows:0,
subflows:0
subflows:0,
groups: 0
}
node.forEach(function(n) {
if (n.type === 'tab') {
@@ -160,12 +205,13 @@ RED.sidebar.info = (function() {
types.nodes += RED.nodes.filterNodes({z:n.id}).length;
} else if (n.type === 'subflow') {
types.subflows++;
} else if (n.type === 'group') {
types.groups++;
} else {
types.nodes++;
}
});
helpSection.container.hide();
infoSection.container.hide();
// infoSection.container.hide();
// - show the count of selected nodes
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.selection")+"</td><td></td></tr>").appendTo(tableBody);
@@ -179,53 +225,96 @@ RED.sidebar.info = (function() {
if (types.nodes > 0) {
$('<div>').text(RED._("clipboard.node",{count:types.nodes})).appendTo(counts);
}
if (types.groups > 0) {
$('<div>').text(RED._("clipboard.group",{count:types.groups})).appendTo(counts);
}
} else {
// A single 'thing' selected.
RED.sidebar.info.outliner.select(node);
// Check to see if this is a subflow or subflow instance
var m = /^subflow(:(.+))?$/.exec(node.type);
if (m) {
if (m[2]) {
subflowNode = RED.nodes.subflow(m[2]);
var subflowRegex = /^subflow(:(.+))?$/.exec(node.type);
if (subflowRegex) {
if (subflowRegex[2]) {
subflowNode = RED.nodes.subflow(subflowRegex[2]);
} else {
subflowNode = node;
}
subflowUserCount = 0;
var subflowType = "subflow:"+subflowNode.id;
RED.nodes.eachNode(function(n) {
if (n.type === subflowType) {
subflowUserCount++;
}
});
subflowUserCount = subflowNode.instances.length;
}
propertiesPanelHeaderIcon.empty();
RED.utils.createNodeIcon(node).appendTo(propertiesPanelHeaderIcon);
var objectLabel = RED.utils.getNodeLabel(node, node.type+": "+node.id)
var newlineIndex = objectLabel.indexOf("\\n");
if (newlineIndex > -1) {
objectLabel = objectLabel.substring(0,newlineIndex)+"...";
}
propertiesPanelHeaderLabel.text(objectLabel);
propertiesPanelHeaderReveal.show();
selectedObject = node;
propRow = $('<tr class="red-ui-help-info-row"><td></td><td></td></tr>').appendTo(tableBody);
var objectType = "node";
if (node.type === "subflow" || subflowRegex) {
objectType = "subflow";
} else if (node.type === "tab") {
objectType = "flow";
}else if (node.type === "group") {
objectType = "group";
}
$(propRow.children()[0]).text(RED._("sidebar.info."+objectType))
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
if (node.type === "tab" || node.type === "subflow") {
// If nothing is selected, but we're on a flow or subflow tab.
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info."+(node.type==='tab'?'flow':'subflow'))+'</td><td></td></tr>').appendTo(tableBody);
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.tabName")+"</td><td></td></tr>").appendTo(tableBody);
$(propRow.children()[1]).text(node.label||node.name||"");
if (node.type === "tab") {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.status")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).text((!!!node.disabled)?RED._("sidebar.info.enabled"):RED._("sidebar.info.disabled"))
propertiesPanelHeaderHelp.hide();
} else if (node.type === "group") {
propertiesPanelHeaderHelp.hide();
propRow = $('<tr class="red-ui-help-info-row"><td>&nbsp;</td><td></td></tr>').appendTo(tableBody);
var typeCounts = {
nodes:0,
groups: 0
}
var allNodes = RED.group.getNodes(node,true);
allNodes.forEach(function(n) {
if (n.type === "group") {
typeCounts.groups++;
} else {
typeCounts.nodes++
}
});
var counts = $('<div>').appendTo($(propRow.children()[1]));
if (typeCounts.nodes > 0) {
$('<div>').text(RED._("clipboard.node",{count:typeCounts.nodes})).appendTo(counts);
}
if (typeCounts.groups > 0) {
$('<div>').text(RED._("clipboard.group",{count:typeCounts.groups})).appendTo(counts);
}
} else {
// An actual node is selected in the editor - build up its properties table
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.node")+"</td><td></td></tr>").appendTo(tableBody);
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
if (node.type !== "subflow" && node.type !== "unknown" && node.name) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("common.label.name")+'</td><td></td></tr>').appendTo(tableBody);
$('<span class="red-ui-text-bidi-aware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'"></span>').text(node.name).appendTo(propRow.children()[1]);
}
if (!m) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+"</td><td></td></tr>").appendTo(tableBody);
propertiesPanelHeaderHelp.show();
if (!subflowRegex) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("sidebar.info.type")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).text((node.type === "unknown")?node._orig.type:node.type);
if (node.type === "unknown") {
$('<span style="float: right; font-size: 0.8em"><i class="fa fa-warning"></i></span>').prependTo($(propRow.children()[1]))
}
}
var count = 0;
if (!m && node.type != "subflow") {
if (!subflowRegex && node.type != "subflow" && node.type != "group") {
var blankRow = $('<tr class="red-ui-help-property-expand blank"><td colspan="2"></td></tr>').appendTo(tableBody);
var defaults;
if (node.type === 'unknown') {
defaults = {};
@@ -240,7 +329,6 @@ RED.sidebar.info = (function() {
$(propRow.children()[1]).text(RED.nodes.getType(node.type).set.module);
count++;
}
$('<tr class="red-ui-help-property-expand red-ui-help-info-property-row blank'+(expandedSections.property?"":" hide")+'"><td colspan="2"></td></tr>').appendTo(tableBody);
if (defaults) {
for (var n in defaults) {
@@ -248,7 +336,8 @@ RED.sidebar.info = (function() {
var val = node[n];
var type = typeof val;
count++;
propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody);
propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td></td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[0]).text(n);
if (defaults[n].type) {
var configNode = RED.nodes.node(val);
if (!configNode) {
@@ -278,37 +367,35 @@ RED.sidebar.info = (function() {
}
}
if (count > 0) {
$('<tr class="red-ui-help-property-expand blank"><td colspan="2"><a href="#" class="node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="red-ui-help-property-more">'+RED._("sidebar.info.showMore")+'</span><span class="red-ui-help-property-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a></td></tr>').appendTo(tableBody);
$('<a href="#" class="node-info-property-header'+(expandedSections.property?" expanded":"")+'"><span class="red-ui-help-property-more">'+RED._("sidebar.info.showMore")+'</span><span class="red-ui-help-property-less">'+RED._("sidebar.info.showLess")+'</span> <i class="fa fa-caret-down"></i></a>').appendTo(blankRow.children()[0]);
}
}
if (node.type !== 'tab') {
if (m) {
if (subflowRegex) {
$('<tr class="blank"><th colspan="2">'+RED._("sidebar.info.subflow")+'</th></tr>').appendTo(tableBody);
$('<tr class="node-info-subflow-row"><td>'+RED._("common.label.name")+'</td><td><span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+RED.utils.sanitize(subflowNode.name)+'</span></td></tr>').appendTo(tableBody);
}
}
}
if (m) {
if (subflowRegex) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("subflow.category")+'</td><td></td></tr>').appendTo(tableBody);
var category = subflowNode.category||"subflows";
$(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category}))
$('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody);
}
var helpText = "";
if (node.type === "tab" || node.type === "subflow") {
$(helpSection.container).hide();
} else {
$(helpSection.container).show();
if (subflowNode && node.type !== "subflow") {
// Selected a subflow instance node.
// - The subflow template info goes into help
helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
} else {
helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
}
setInfoText(helpText, helpSection.content);
}
// var helpText = "";
// if (node.type === "tab" || node.type === "subflow") {
// } else {
// if (subflowNode && node.type !== "subflow") {
// // Selected a subflow instance node.
// // - The subflow template info goes into help
// helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
// } else {
// helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// }
// setInfoText(helpText, helpSection.content);
// }
var infoText = "";
@@ -320,7 +407,26 @@ RED.sidebar.info = (function() {
if (node.info) {
infoText = infoText + RED.utils.renderMarkdown(node.info || "")
}
setInfoText(infoText, infoSection.content);
var infoSectionContainer = $("<div>").css("padding","0 6px 6px").appendTo(propertiesPanelContent)
// var editInfo = $('<button class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-file-text-o"></button>').appendTo(infoSectionContainer).on("click", function(evt) {
// //.text(RED._("sidebar.info.editDescription"))
// evt.preventDefault();
// evt.stopPropagation();
// if (node.type === 'tab') {
//
// } else if (node.type === 'subflow') {
//
// } else if (node.type === 'group') {
//
// } else if (node._def.category !== 'config') {
// RED.editor.edit(node,"editor-tab-description");
// } else {
//
// }
// })
setInfoText(infoText, infoSectionContainer);
$(".red-ui-sidebar-info-stack").scrollTop(0);
$(".node-info-property-header").on("click", function(e) {
@@ -336,7 +442,7 @@ RED.sidebar.info = (function() {
// propRow = $('<tr class="red-ui-help-info-row"><td>Actions</td><td></td></tr>').appendTo(tableBody);
// var actionBar = $(propRow.children()[1]);
//
// // var actionBar = $('<div>',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesSection.content);
// // var actionBar = $('<div>',{style:"background: #fefefe; padding: 3px;"}).appendTo(propertiesPanel);
// $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar);
// $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar);
// $('<button type="button" class="red-ui-button"><i class="fa fa-code"></i></button>').appendTo(actionBar);
@@ -411,6 +517,7 @@ RED.sidebar.info = (function() {
}
function startTips() {
$(".red-ui-sidebar-info").addClass('show-tips');
resizeStack();
if (enabled) {
if (!startTimeout && !refreshTimeout) {
if (tipCount === -1) {
@@ -424,6 +531,7 @@ RED.sidebar.info = (function() {
}
function stopTips() {
$(".red-ui-sidebar-info").removeClass('show-tips');
resizeStack();
clearInterval(refreshTimeout);
clearTimeout(startTimeout);
refreshTimeout = null;
@@ -448,15 +556,8 @@ RED.sidebar.info = (function() {
}
function set(html,title) {
// tips.stop();
// sections.show();
refresh(null);
propertiesSection.container.hide();
helpSection.container.hide();
infoSection.container.show();
infoSection.title.text(title||RED._("sidebar.info.desc"));
setInfoText(html,infoSection.content);
$(".red-ui-sidebar-info-stack").scrollTop(0);
console.warn("Deprecated use of RED.sidebar.info.set - use RED.sidebar.help.set instead")
RED.sidebar.help.set(html,title);
}
function refreshSelection(selection) {

View File

@@ -112,14 +112,14 @@ RED.touch.radialMenu = (function() {
if (!opt.disabled) {
if (p[0]>opt.x-30 && p[0]<opt.x+30 && p[1]>opt.y-30 && p[1]<opt.y+30) {
if (opt !== activeOption) {
opt.el.style("background","#999");
opt.el.classed("selected",true);
activeOption = opt;
}
} else if (opt === activeOption) {
opt.el.style("background","#fff");
activeOption = null;
} else {
opt.el.style("background","#fff");
if (opt === activeOption) {
activeOption = null;
}
opt.el.classed("selected",false);
}
}
}

View File

@@ -241,10 +241,13 @@ RED.typeSearch = (function() {
$(document).off('mousedown.red-ui-type-search');
$(document).off('mouseup.red-ui-type-search');
$(document).off('click.red-ui-type-search');
$(document).off('touchstart.red-ui-type-search');
$(document).off('mousedown.red-ui-type-search');
setTimeout(function() {
$(document).on('mousedown.red-ui-type-search',handleMouseActivity);
$(document).on('mouseup.red-ui-type-search',handleMouseActivity);
$(document).on('click.red-ui-type-search',handleMouseActivity);
$(document).on('touchstart.red-ui-type-search',handleMouseActivity);
},200);
refreshTypeList(opts);
@@ -260,7 +263,9 @@ RED.typeSearch = (function() {
searchResultsDiv.slideDown(300);
setTimeout(function() {
searchResultsDiv.find(".red-ui-editableList-container").scrollTop(0);
searchInput.trigger("focus");
if (!opts.disableFocus) {
searchInput.trigger("focus");
}
},100);
}
function hide(fast) {
@@ -279,6 +284,7 @@ RED.typeSearch = (function() {
$(document).off('mousedown.red-ui-type-search');
$(document).off('mouseup.red-ui-type-search');
$(document).off('click.red-ui-type-search');
$(document).off('touchstart.red-ui-type-search');
}
}
function getTypeLabel(type, def) {

View File

@@ -52,13 +52,15 @@ RED.utils = (function() {
} else if (value === null) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">null</span>');
} else if (typeof value === 'object') {
if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
if (value.hasOwnProperty('type') && value.type === 'undefined') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">undefined</span>');
} else if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('buffer['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
} else if (value.hasOwnProperty('type') && value.type === 'function') {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
} else if (value.hasOwnProperty('type') && value.type === 'number') {
} else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
} else {
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
@@ -348,7 +350,9 @@ RED.utils = (function() {
}
if (obj === null || obj === undefined) {
$('<span class="red-ui-debug-msg-type-null">'+obj+'</span>').appendTo(entryObj);
} else if (obj.__enc__ && obj.type === 'number') {
} else if (obj.__enc__ && obj.type === 'undefined') {
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
} else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);
@@ -806,9 +810,9 @@ RED.utils = (function() {
function separateIconPath(icon) {
var result = {module: "", file: ""};
if (icon) {
var index = icon.indexOf('icons/');
if (index !== -1) {
icon = icon.substring(index+6);
var index = icon.indexOf(RED.settings.apiRootUrl+'icons/');
if (index === 0) {
icon = icon.substring((RED.settings.apiRootUrl+'icons/').length);
}
index = icon.indexOf('/');
if (index !== -1) {
@@ -859,10 +863,15 @@ RED.utils = (function() {
}
function getNodeIcon(def,node) {
if (def.category === 'config') {
if (node && node.type === '_selection_') {
return "font-awesome/fa-object-ungroup";
} else if (node && node.type === 'group') {
return "font-awesome/fa-object-group"
} else if (def.category === 'config') {
return RED.settings.apiRootUrl+"icons/node-red/cog.svg"
} else if (node && node.type === 'tab') {
return RED.settings.apiRootUrl+"icons/node-red/subflow.svg"
return "red-ui-icons/red-ui-icons-flow"
// return RED.settings.apiRootUrl+"images/subflow_tab.svg"
} else if (node && node.type === 'unknown') {
return RED.settings.apiRootUrl+"icons/node-red/alert.svg"
} else if (node && node.icon) {
@@ -921,6 +930,8 @@ RED.utils = (function() {
var l;
if (node.type === 'tab') {
l = node.label || defaultLabel
} else if (node.type === 'group') {
l = node.name || defaultLabel
} else {
l = node._def.label;
try {
@@ -1054,11 +1065,63 @@ RED.utils = (function() {
}
// If the specified name is not defined in font-awesome, show arrow-in icon.
iconUrl = RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg"
} else if (iconPath.module === "red-ui-icons") {
var redIconElement = $('<i/>').appendTo(iconContainer);
redIconElement.addClass("red-ui-palette-icon red-ui-icons " + iconPath.file);
return;
}
var imageIconElement = $('<div/>',{class:"red-ui-palette-icon"}).appendTo(iconContainer);
imageIconElement.css("backgroundImage", "url("+iconUrl+")");
}
function createNodeIcon(node) {
var def = node._def;
var nodeDiv = $('<div>',{class:"red-ui-search-result-node"})
if (node.type === "_selection_") {
nodeDiv.addClass("red-ui-palette-icon-selection");
} else if (node.type === "group") {
nodeDiv.addClass("red-ui-palette-icon-group");
} else if (node.type === 'tab') {
nodeDiv.addClass("red-ui-palette-icon-flow");
} else {
var colour = RED.utils.getNodeColor(node.type,def);
// if (node.type === 'tab') {
// colour = "#C0DEED";
// }
nodeDiv.css('backgroundColor',colour);
var borderColor = getDarkerColor(colour);
if (borderColor !== colour) {
nodeDiv.css('border-color',borderColor)
}
}
var icon_url = RED.utils.getNodeIcon(def,node);
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
RED.utils.createIconElement(icon_url, iconContainer, true);
return nodeDiv;
}
function getDarkerColor(c) {
var r,g,b;
if (/^#[a-f0-9]{6}$/i.test(c)) {
r = parseInt(c.substring(1, 3), 16);
g = parseInt(c.substring(3, 5), 16);
b = parseInt(c.substring(5, 7), 16);
} else if (/^#[a-f0-9]{3}$/i.test(c)) {
r = parseInt(c.substring(1, 2)+c.substring(1, 2), 16);
g = parseInt(c.substring(2, 3)+c.substring(2, 3), 16);
b = parseInt(c.substring(3, 4)+c.substring(3, 4), 16);
} else {
return c;
}
var l = 0.3 * r/255 + 0.59 * g/255 + 0.11 * b/255 ;
r = Math.max(0,r-50);
g = Math.max(0,g-50);
b = Math.max(0,b-50);
var s = ((r<<16) + (g<<8) + b).toString(16);
return '#'+'000000'.slice(0, 6-s.length)+s;
}
return {
createObjectElement: buildMessageElement,
getMessageProperty: getMessageProperty,
@@ -1076,6 +1139,8 @@ RED.utils = (function() {
parseContextKey: parseContextKey,
createIconElement: createIconElement,
sanitize: sanitize,
renderMarkdown: renderMarkdown
renderMarkdown: renderMarkdown,
createNodeIcon: createNodeIcon,
getDarkerColor: getDarkerColor
}
})();

View File

@@ -67,9 +67,16 @@ RED.view.tools = (function() {
function moveSelection(dx,dy) {
if (moving_set === null) {
moving_set = [];
var selection = RED.view.selection();
if (selection.nodes) {
moving_set = selection.nodes.map(function(n) { return {n:n}});
while (selection.nodes.length > 0) {
var n = selection.nodes.shift();
moving_set.push({n:n});
if (n.type === "group") {
selection.nodes = selection.nodes.concat(n.nodes);
}
}
}
}
if (moving_set && moving_set.length > 0) {
@@ -93,10 +100,15 @@ RED.view.tools = (function() {
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 (node.n.type === "group") {
RED.group.markDirty(node.n);
minX = Math.min(node.n.x - 5,minX);
minY = Math.min(node.n.y - 5,minY);
} else {
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];
@@ -105,13 +117,86 @@ RED.view.tools = (function() {
}
}
RED.view.redraw();
} else {
RED.view.scroll(dx*10,dy*10);
}
}
function setSelectedNodeLabelState(labelShown) {
var selection = RED.view.selection();
var historyEvents = [];
var nodes = [];
if (selection.nodes) {
selection.nodes.forEach(function(n) {
if (n.type !== 'subflow' && n.type !== 'group') {
nodes.push(n);
} else if (n.type === 'group') {
nodes = nodes.concat( RED.group.getNodes(n,true));
}
});
}
nodes.forEach(function(n) {
var modified = false;
var oldValue = n.l === undefined?true:n.l;
var isLink = /^link (in|out)$/.test(n._def.type);
if (labelShown) {
if (n.l === false || (isLink && !n.hasOwnProperty('l'))) {
n.l = true;
modified = true;
}
} else {
if ((!isLink && (!n.hasOwnProperty('l') || n.l === true)) || (isLink && n.l === true) ) {
n.l = false;
modified = true;
}
}
if (modified) {
historyEvents.push({
t: "edit",
node: n,
changed: n.changed,
changes: {
l: oldValue
}
})
n.changed = true;
n.dirty = true;
n.resize = true;
}
})
if (historyEvents.length > 0) {
RED.history.push({
t: "multi",
events: historyEvents,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
}
RED.view.redraw();
}
return {
init: function() {
RED.actions.add("core:show-selected-node-labels", function() { setSelectedNodeLabelState(true); })
RED.actions.add("core:hide-selected-node-labels", function() { setSelectedNodeLabelState(false); })
RED.actions.add("core:align-selection-to-grid", alignToGrid);
RED.actions.add("core:scroll-view-up", function() { RED.view.scroll(0,-RED.view.gridSize());});
RED.actions.add("core:scroll-view-right", function() { RED.view.scroll(RED.view.gridSize(),0);});
RED.actions.add("core:scroll-view-down", function() { RED.view.scroll(0,RED.view.gridSize());});
RED.actions.add("core:scroll-view-left", function() { RED.view.scroll(-RED.view.gridSize(),0);});
RED.actions.add("core:step-view-up", function() { RED.view.scroll(0,-5*RED.view.gridSize());});
RED.actions.add("core:step-view-right", function() { RED.view.scroll(5*RED.view.gridSize(),0);});
RED.actions.add("core:step-view-down", function() { RED.view.scroll(0,5*RED.view.gridSize());});
RED.actions.add("core:step-view-left", function() { RED.view.scroll(-5*RED.view.gridSize(),0);});
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);});

File diff suppressed because it is too large Load Diff

View File

@@ -128,10 +128,6 @@ RED.workspaces = (function() {
RED.history.push(historyEvent);
RED.nodes.dirty(true);
RED.sidebar.config.refresh();
var selection = RED.view.selection();
if (!selection.nodes && !selection.links) {
RED.sidebar.info.refresh(workspace);
}
if (changes.hasOwnProperty('disabled')) {
RED.nodes.eachNode(function(n) {
if (n.z === workspace.id) {
@@ -140,6 +136,7 @@ RED.workspaces = (function() {
});
RED.view.redraw();
}
RED.events.emit("flows:change",workspace);
}
RED.tray.close();
}
@@ -219,7 +216,10 @@ RED.workspaces = (function() {
if (RED.view.state() != RED.state.IMPORT_DRAGGING) {
RED.view.state(RED.state.DEFAULT);
}
RED.sidebar.info.refresh(workspace);
var selection = RED.view.selection();
if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
RED.sidebar.info.refresh(workspace);
}
tabflowEditor.destroy();
}
}
@@ -371,7 +371,9 @@ RED.workspaces = (function() {
var changes = { disabled: workspace.disabled };
workspace.disabled = disabled;
$("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
if (id === activeWorkspace) {
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
}
var historyEvent = {
t: "edit",
changes:changes,
@@ -380,10 +382,11 @@ RED.workspaces = (function() {
}
workspace.changed = true;
RED.history.push(historyEvent);
RED.events.emit("flows:change",workspace);
RED.nodes.dirty(true);
RED.sidebar.config.refresh();
var selection = RED.view.selection();
if (!selection.nodes && !selection.links) {
if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
RED.sidebar.info.refresh(workspace);
}
if (changes.hasOwnProperty('disabled')) {
@@ -412,9 +415,14 @@ RED.workspaces = (function() {
}
function setWorkspaceOrder(order) {
RED.nodes.setWorkspaceOrder(order.filter(function(id) {
var newOrder = order.filter(function(id) {
return RED.nodes.workspace(id) !== undefined;
}));
})
var currentOrder = RED.nodes.getWorkspaceOrder();
if (JSON.stringify(newOrder) !== JSON.stringify(currentOrder)) {
RED.nodes.setWorkspaceOrder(newOrder);
RED.events.emit("flows:reorder",newOrder);
}
workspace_tabs.order(order);
}

View File

@@ -39,7 +39,11 @@ RED.user = (function() {
closeOnEscape: !!opts.cancelable,
width: 600,
resizable: false,
draggable: false
draggable: false,
close: function( event, ui ) {
$("#node-dialog-login").dialog('destroy').remove();
RED.keyboard.enable()
}
});
$("#node-dialog-login-fields").empty();
@@ -98,10 +102,10 @@ RED.user = (function() {
data: body
}).done(function(data,textStatus,xhr) {
RED.settings.set("auth-tokens",data);
$("#node-dialog-login").dialog('destroy').remove();
if (opts.updateMenu) {
updateUserMenu();
}
$("#node-dialog-login").dialog("close");
done();
}).fail(function(jqXHR,textStatus,errorThrown) {
RED.settings.remove("auth-tokens");
@@ -143,7 +147,8 @@ RED.user = (function() {
}
if (opts.cancelable) {
$("#node-dialog-login-cancel").button().on("click", function( event ) {
$("#node-dialog-login").dialog('destroy').remove();
$("#node-dialog-login").dialog('close');
});
}
@@ -152,8 +157,7 @@ RED.user = (function() {
$("#node-dialog-login-image").load(function() {
dialog.dialog("open");
}).attr("src",loginImageSrc);
RED.keyboard.disable();
}
});
}

View File

@@ -9,19 +9,15 @@
color: transparent !important;
}
}
.ace_gutter {
background: $text-editor-gutter-background;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.ace_scroller {
background: $text-editor-background;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.ace_scroller {
background: $text-editor-background;
color: $text-editor-color;
}
.ace_marker-layer .ace_active-line {
@@ -37,9 +33,6 @@
.ace_gutter-active-line {
background: $text-editor-gutter-active-line-background;
}
.ace_gutter {
background: $text-editor-gutter-background;
}
.ace_tooltip {
font-family: $primary-font;
line-height: 1.4em;
@@ -52,6 +45,9 @@
@include component-shadow;
border-color: $popover-background;
}
.ace_content {
line-height: 1;
}
textarea.ace_text-input {
overflow: hidden;
padding: 0px 1px !important;

View File

@@ -67,6 +67,9 @@
text-decoration: none;
color: $primary-text-color;
}
a:focus {
outline: 1px solid $form-input-focus-color;
}
p {
margin: 0 0 10px;

View File

@@ -23,7 +23,7 @@ $primary-background: #f3f3f3;//#0ff;
$secondary-background: #fff;//#ff0;
$secondary-background-selected: #efefef;//#e9e900;
$secondary-background-inactive: #f0f0f0;//#f0f000;
$secondary-background-hover: #ddd;//#dd0;
$secondary-background-hover: #e6e6e6;//#dd0;
$secondary-background-disabled: #f9f9f9;//#fafa0;
$tertiary-background: #f7f7f7;//#f0f;
@@ -94,7 +94,7 @@ $list-item-secondary-color: $secondary-text-color;
$list-item-background: $secondary-background;
$list-item-background-disabled: $secondary-background-inactive;
$list-item-background-hover: $secondary-background-hover;
$list-item-background-selected: $secondary-background-selected;
$list-item-background-selected: #ffebc7; // #fff1e5;
$list-item-border-selected: $secondary-text-color-selected;
$tab-text-color-active: $header-text-color;
@@ -129,7 +129,7 @@ $workspace-button-color-primary: #eee;
$workspace-button-background-primary: #AD1625;
$workspace-button-background-primary-hover: #6E0A1E;
$workspace-button-color-focus-outline: $form-input-border-color;
$workspace-button-color-focus-outline: $form-input-focus-color;
$shade-color: rgba(160,160,160,0.5);
@@ -284,3 +284,9 @@ $debug-message-border: #eee;
$debug-message-border-hover: #999;
$debug-message-border-warning: #ffdf9d;
$debug-message-border-error: #f99;
$group-default-fill: none;
$group-default-fill-opacity: 1;
$group-default-stroke: #999;
$group-default-stroke-opacity: 1;
$group-default-label-color: #a4a4a4;

View File

@@ -175,3 +175,38 @@
.red-ui-menu-dropdown-submenu.disabled > a:before {
border-right-color: $menuCaret;
}
// Menu NG
ul.red-ui-menu:not(.red-ui-menu-dropdown) {
font-family: $primary-font;
font-size: 12px;
list-style-type: none;
padding: 0;
margin: 0;
li a {
display: block;
padding: 4px 8px 4px 16px;
clear: both;
font-weight: normal;
line-height: 20px;
color: $menuColor;
white-space: nowrap;
text-decoration: none;
&:hover,&:focus {
color: $menuHoverColor;
text-decoration: none;
background-color: $menuHoverBackground;
border: none;
outline: none;
}
}
&.red-ui-menu-compact {
font-size: 12px;
li a {
line-height: 16px;
}
}
}

View File

@@ -304,9 +304,6 @@ button.red-ui-button-small
&:first-child {
padding: 20px 20px 0;
}
&:last-child {
padding-bottom: 20px;
}
}
}
.red-ui-editor-type-expression-tab-content {
@@ -411,6 +408,133 @@ button.red-ui-button.red-ui-editor-node-appearance-button {
}
}
.red-ui-group-layout-picker {
padding: 5px;
background: $secondary-background;
}
.red-ui-group-layout-picker-cell-text {
position: absolute;
width: 14px;
height: 2px;
border-top: 2px solid $secondary-text-color;
border-bottom: 2px solid $secondary-text-color;
margin: 2px;
&.red-ui-group-layout-text-pos-nw { top: 0; left: 0; }
&.red-ui-group-layout-text-pos-n { top: 0; left: calc(50% - 9px); }
&.red-ui-group-layout-text-pos-ne { top: 0; right: 0; }
&.red-ui-group-layout-text-pos-sw { bottom: 0; left: 0; }
&.red-ui-group-layout-text-pos-s { bottom: 0; left: calc(50% - 9px); }
&.red-ui-group-layout-text-pos-se { bottom: 0; right: 0; }
&.red-ui-group-layout-text-pos- {
width: 100%;
height: 100%;
border-radius: 5px;
margin: 0;
background-color: #FFF;
background-size: 100% 100%;
background-position: 0 0, 50% 50%;
background-image: linear-gradient(45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%, transparent 55%, transparent),linear-gradient(-45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%, transparent 55%, transparent);
border: none;
}
}
.red-ui-group-layout-picker button.red-ui-search-result-node {
float: none;
position: relative;
padding: 0;
margin: 5px;
width: 32px;
height: 27px;
}
button.red-ui-group-layout-picker-none {
width: 100%;
}
.red-ui-color-picker {
input[type="text"] {
border-radius:0;
width: 100%;
margin-bottom: 0;
border: none;
border-bottom: 1px solid $form-input-border-color;
}
small {
color: $secondary-text-color;
margin-left: 5px;
margin-right: 4px;
display: inline-block;
min-width: 35px;
text-align: right;
}
background: $primary-background;
}
.red-ui-editor-node-appearance-button {
.red-ui-search-result-node {
overflow: hidden
}
}
.red-ui-color-picker-cell {
padding: 0;
border-style: solid;
border-width: 1px;
border-color: $secondary-border-color;
}
.red-ui-color-picker-swatch {
position: absolute;
top:-1px;right:-1px;left:-1px;bottom:-1px;
border-radius: 4px;
}
.red-ui-color-picker-cell-none {
height: 100%;
background-color: #FFF;
background-size: 100% 100%;
background-position: 0 0, 50% 50%;
background-image: linear-gradient(45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%, transparent 55%, transparent),linear-gradient(-45deg, transparent 45%, $secondary-border-color 45%, $secondary-border-color 55%, transparent 55%, transparent)
}
.red-ui-search-result-node .red-ui-color-picker-cell-none {
border-radius: 4px;
background-size: 50% 50%;
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
}
.red-ui-color-picker-opacity-slider {
position:relative;
vertical-align: middle;
display: inline-block;
width: calc(100% - 50px);
height: 14px;
margin: 6px 3px 8px;
box-sizing: border-box;
background-color: white;
background-image:
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 25%),
linear-gradient(-45deg, #eee 25%, transparent 25%, transparent 75%, #eee 25%);
background-size: 6px 6px;
}
.red-ui-color-picker-opacity-slider-overlay {
position: absolute;
top:0;right:0;left:0;bottom:0;
background-image:linear-gradient(90deg, transparent 0%, #f00 100%);
background-size: 100% 100%;
border: 1px solid $primary-border-color;
}
div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle {
z-Index: 10;
top: -4px;
cursor: pointer;
min-width: 0;
width: 10px;
height: 22px;
padding: 0;
border: 1px solid $primary-border-color;
border-radius: 1px;
background: $secondary-background;
box-sizing: border-box;
}
.red-ui-icon-picker {
select {
box-sizing: border-box;
@@ -473,6 +597,7 @@ button.red-ui-button.red-ui-editor-node-appearance-button {
padding: 4px;
color: $secondary-text-color;
font-size: 0.9em;
line-height: 24px;
}
button {
float: right;
@@ -591,6 +716,9 @@ button.red-ui-button.red-ui-editor-node-appearance-button {
button.red-ui-toggleButton.toggle {
text-align: left;
i {
min-width: 15px;
}
}
@@ -704,8 +832,18 @@ span.red-ui-editor-subflow-env-lang-icon {
right: 4px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.red-ui-editor-subflow-env-input-type {
background: $secondary-background;
height: 100%;
box-sizing: border-box;
}
.red-ui-editor-subflow-env-input-type-placeholder {
color: $tertiary-text-color;
padding-left: 4px;
}
// .red-ui-editor-subflow-ui-grid {
// width: 100%;
// .red-ui-editableList-container {

View File

@@ -35,13 +35,21 @@
pointer-events: none;
-webkit-touch-callout: none;
@include disable-selection;
.red-ui-flow-node-label-text {
dominant-baseline: middle;
}
&.red-ui-flow-node-label-right .red-ui-flow-node-label-text {
text-anchor: end;
}
}
.red-ui-flow-port-label {
stroke-width: 0;
fill: $secondary-text-color;
font-size: 16px;
alignment-baseline: middle;
dominant-baseline: middle;
text-anchor: middle;
pointer-events: none;
-webkit-touch-callout: none;
@@ -71,6 +79,54 @@
}
}
.red-ui-flow-group {
&.red-ui-flow-group-hovered {
.red-ui-flow-group-outline-select {
stroke-opacity: 0.8 !important;
stroke-dasharray: 10 4 !important;
}
}
&.red-ui-flow-group-active-hovered:not(.red-ui-flow-group-hovered) {
.red-ui-flow-group-outline-select {
stroke: $link-link-color;
}
}
}
.red-ui-flow-group-outline {
fill: none;
stroke: $node-selected-color;
stroke-opacity: 0;
stroke-width: 12;
pointer-events: stroke;
}
.red-ui-flow-group-outline-select {
fill: none;
stroke: $node-selected-color;
pointer-events: stroke;
stroke-opacity: 0;
stroke-width: 3;
&.red-ui-flow-group-outline-select-background {
stroke: $view-background;
stroke-width: 6;
}
}
.red-ui-flow-group-body {
pointer-events: none;
fill: $group-default-fill;
fill-opacity: $group-default-fill-opacity;
stroke-width: 2;
stroke: $group-default-stroke;
stroke-opacity: $group-default-stroke-opacity;
}
.red-ui-flow-group-label {
@include disable-selection;
fill: $group-default-label-color;
}
.red-ui-flow-node-unknown {
stroke-dasharray:10,4;
stroke: $node-border-unknown;
@@ -84,6 +140,7 @@
}
.red-ui-flow-node-icon-group {
.fa-lg {
@include disable-selection;
stroke: none;
fill: $node-icon-color;
text-anchor: middle;
@@ -103,6 +160,15 @@
.red-ui-flow-node-button {
fill: inherit;
&.red-ui-flow-node-button-disabled {
opacity: 0.4;
.red-ui-flow-node-button-button {
cursor: default;
}
}
}
.red-ui-flow-node-button-button {
cursor: pointer;
}
.red-ui-flow-node-button-background {
fill: $node-background-placeholder;
@@ -166,6 +232,9 @@ g.red-ui-flow-node-selected {
fill-opacity: 1;
stroke-dasharray: none;
}
.red-ui-flow-group, .red-ui-flow-group-body {
stroke-dasharray: 8, 3;
}
}
.red-ui-flow-node-disabled {
&.red-ui-flow-node, .red-ui-flow-node {
@@ -182,7 +251,7 @@ g.red-ui-flow-node-selected {
stroke-dasharray: none;
}
}
@each $current-color in red green yellow blue grey {
@each $current-color in red green yellow blue grey gray {
.red-ui-flow-node-status-dot-#{$current-color} {
fill: map-get($node-status-colors,$current-color);
stroke: map-get($node-status-colors,$current-color);
@@ -248,6 +317,7 @@ g.red-ui-flow-node-selected {
.red-ui-flow-link-outline {
stroke: $view-background;
stroke-opacity: 0.4;
stroke-width: 5;
cursor: crosshair;
fill: none;
@@ -273,7 +343,10 @@ g.red-ui-flow-link-unknown path.red-ui-flow-link-line {
stroke-dasharray: 10, 4;
}
@keyframes red-ui-flow-port-tooltip-fadeIn { from { opacity:0; } to { opacity:1; } }
// @keyframes *must* be on multiple lines so build-custom-theme can filter them out
@keyframes red-ui-flow-port-tooltip-fadeIn {
from { opacity:0; } to { opacity:1; }
}
.red-ui-flow-port-tooltip {
opacity:0;

View File

@@ -37,7 +37,7 @@
border-radius: 4px;
font-family: $monospace-font !important;
font-size: 13px !important;
height: 300px;
height: 100%;
line-height: 1.3em;
padding: 6px 10px;
background: $clipboard-textarea-background;
@@ -62,6 +62,7 @@
background: $form-input-background;
&>div {
height: 100%;
box-sizing: border-box;
}
}
.red-ui-clipboard-dialog-box {
@@ -152,3 +153,61 @@
border-top: none;
}
}
.red-ui-clipboard-dialog-import-conflicts-list-container {
min-height: 300px;
position: relative;
li:not(:first-child) .red-ui-clipboard-dialog-import-conflicts-item-header {
// border-top: 1px solid $secondary-border-color;
}
}
.red-ui-clipboard-dialog-import-conflicts-item-header {
background: $tertiary-background;
& > span:first-child {
color: $header-text-color;
padding-left: 4px;
font-size: 12px;
}
}
.red-ui-clipboard-dialog-import-conflicts-controls {
position: absolute;
top:0;
bottom: 0;
right: 0px;
text-align: center;
color: $form-text-color;
.form-row & label {
padding: 2px 0;
line-height: 23px;
margin-bottom: 0;
width: 80px;
display: inline-block;
position: relative;
height: 100%;
width: 80px;
text-align: center;
border-left: 1px solid $secondary-border-color;
}
input[type="checkbox"] {
display: inline-block;
width: auto;
margin: 0;
}
}
#red-ui-clipboard-dialog-import-conflicts-list .disabled .red-ui-info-outline-item {
opacity: 0.4;
}
.form-row label.red-ui-clipboard-dialog-import-conflicts-gutter {
box-sizing: border-box;
width: 22px;
text-align: center;
.red-ui-editor-dialog & input[type="checkbox"] {
width: auto;
padding: 0;
margin: 0;
}
}

View File

@@ -71,38 +71,9 @@
}
.red-ui-notification-shake-horizontal {
-webkit-animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
}
@-webkit-keyframes red-ui-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);
// }
animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
}
// @keyframes *must* be on multiple lines so build-custom-theme can filter them out
@keyframes red-ui-notification-shake-horizontal {
0%,
100% {
@@ -122,12 +93,4 @@
-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

@@ -237,3 +237,45 @@ ul.red-ui-palette-module-error-list {
#red-ui-palette-module-install-shade {
padding-top: 80px;
}
button.red-ui-palette-editor-upload-button {
padding: 0;
height: 25px;
margin-top: -1px;
input[type="file"] {
opacity: 0;
margin: 0;
height: 0;
width: 0;
}
.red-ui-settings-tabs-content & label {
margin: 0;
min-width: 0;
padding: 2px 8px;
}
form {
width: 0;
}
}
.red-ui-palette-editor-upload {
display: none;
position: absolute;
left: 0;
right: 0;
top: 44px;
padding: 20px;
background: $secondary-background;
border-bottom: 1px $secondary-border-color solid;
box-shadow: 1px 1px 4px $shadow;
.placeholder-input {
width: calc(100% - 180px);
margin: 0;
}
}
.red-ui-palette-editor-upload-buttons {
float: right;
button {
margin-left: 10px;
}
}

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