Compare commits

..

95 Commits

Author SHA1 Message Date
Dave Conway-Jones
fda95dfc5d csv node: remove xs console.log 2020-04-14 11:02:14 +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
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
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
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
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
KAZUHIRO ITO
bba6855872 Add admin api authentication function 2020-02-26 12:59:40 +09: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
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
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
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
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
224 changed files with 4398 additions and 1246 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,42 +1,3 @@
#### 1.0.5: Maintenance Release
Runtime
- #2500 Support for context stores using JSONata and evaluateNodeProperty()
- Add better handling of host-key-verify error with projects
- #2517 Handle false values in $env() properly
- #2514 Ensure complete node scope is remapped in subflows
- #2513 Flows/subflows must preinitialise their context objects
- Clear node.close timeout to avoid unnecessary work on restart
- #2532 Set flow.disabled when disabled property is false
- #2522 Ensure file context does not write 'undefined' to store
Editor
- #2489 Fix XPath in UI tests
- #2504 Fix paletteCategories order
- #2501 Add page objects for UI testing
- #2494 Check node props when deciding if pasted node can splice links
- #2521 Don't double-sanitize node name in debug sidebar
- #2519 German i18n updates
- #2523 Update nodeTabMap when replacing unknown nodes
- Update TypedInput to use flexbox and remove resizing code
- Handle nodes with no wires array
- Do not collapse whitespace in Debug string messages
Nodes
- File: Remove old legacy wording from file node info to stop confusing users.
- Join: Ensure join node handles missing buffer joiner when not in string mode
- Exec: make exec node logging consistent with itself. (only be verbose when in verbose mode)
- Trigger: reset default timeout value when switching away from wait for reset
- Join: Fix join to not crash on appending invalid types to buffer.
- MQTT out: Add warning if topic contains + or #
- #2502 WebSocket i18n update
- #2508 Add Japanese translation for join node
- TCP out: tidy up select of which rows to display
#### 1.0.4: Maintenance Release
Runtime

View File

@@ -151,6 +151,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",
@@ -177,6 +178,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",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "1.0.5",
"version": "1.0.4",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -32,7 +32,7 @@
"clone": "2.1.2",
"content-type": "1.0.4",
"cookie": "0.4.0",
"cookie-parser": "1.4.5",
"cookie-parser": "1.4.4",
"cors": "2.8.5",
"cron": "1.8.2",
"denque": "1.4.1",
@@ -47,17 +47,18 @@
"is-utf8": "0.2.1",
"js-yaml": "3.13.1",
"json-stringify-safe": "5.0.1",
"jsonata": "1.8.2",
"jsonata": "1.8.1",
"lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0",
"memorystore": "1.6.2",
"mime": "2.4.4",
"mqtt": "2.18.8",
"multer": "1.4.2",
"mustache": "4.0.1",
"mustache": "4.0.0",
"node-red-node-rbe": "^0.2.6",
"node-red-node-sentiment": "^0.1.6",
"node-red-node-tail": "^0.1.0",
"nopt": "4.0.3",
"nopt": "4.0.1",
"oauth2orize": "1.11.0",
"on-headers": "1.0.2",
"passport": "0.4.1",
@@ -66,16 +67,16 @@
"raw-body": "2.4.1",
"request": "2.88.0",
"semver": "6.3.0",
"uglify-js": "3.8.1",
"uglify-js": "3.8.0",
"when": "3.7.8",
"ws": "6.2.1",
"xml2js": "0.4.23"
},
"optionalDependencies": {
"bcrypt": "3.0.8"
"bcrypt": "3.0.6"
},
"devDependencies": {
"marked": "0.8.2",
"marked": "0.8.0",
"dompurify": "2.0.8",
"grunt": "~1.0.4",
"grunt-chmod": "~1.1.1",

View File

@@ -44,6 +44,7 @@ module.exports = {
user: req.user,
module: req.body.module,
version: req.body.version,
url: req.body.url,
req: apiUtils.getRequestLogObject(req)
}
runtimeAPI.nodes.addModule(opts).then(function(info) {

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,38 @@ AnonymousStrategy.prototype.authenticate = function(req) {
});
}
function TokensStrategy() {
passport.Strategy.call(this);
this.name = 'tokens';
}
util.inherits(TokensStrategy, passport.Strategy);
TokensStrategy.prototype.authenticate = function(req) {
var self = this;
var token = null;
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(admin) {
if (admin) {
self.success(admin,{scope:admin.permissions});
} else {
self.fail(401);
}
});
} else {
self.fail(401);
}
}
module.exports = {
bearerStrategy: bearerStrategy,
clientPasswordStrategy: clientPasswordStrategy,
passwordTokenExchange: passwordTokenExchange,
anonymousStrategy: new AnonymousStrategy()
anonymousStrategy: new AnonymousStrategy(),
tokensStrategy: new TokensStrategy()
}

View File

@@ -59,7 +59,9 @@ function getDefaultUser() {
var api = {
get: get,
authenticate: authenticate,
default: getDefaultUser
default: getDefaultUser,
tokens: getDefaultUser,
tokenHeader: "authorization"
}
function init(config) {
@@ -105,6 +107,12 @@ 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();
}
}
}
function cleanUser(user) {
if (user && user.hasOwnProperty('password')) {
@@ -118,5 +126,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

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "1.0.5",
"version": "1.0.4",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "1.0.5",
"@node-red/editor-client": "1.0.5",
"@node-red/util": "1.0.4",
"@node-red/editor-client": "1.0.4",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"clone": "2.1.2",
@@ -26,7 +26,7 @@
"express": "4.17.1",
"memorystore": "1.6.2",
"mime": "2.4.4",
"mustache": "4.0.1",
"mustache": "4.0.0",
"oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2",

View File

@@ -14,7 +14,11 @@
"back": "Back",
"next": "Next",
"clone": "Clone project",
"cont": "Continue"
"cont": "Continue",
"line": "Outline",
"fill": "Fill",
"color": "Color",
"position": "Position"
},
"type": {
"string": "string",
@@ -91,7 +95,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,6 +180,8 @@
"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",
@@ -186,6 +197,9 @@
"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__",
"export": {
"selected":"selected nodes",
@@ -308,6 +322,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",
@@ -351,7 +372,8 @@
"bool": "bool",
"json": "JSON",
"bin": "buffer",
"env": "env variable"
"env": "env variable",
"cred": "credential"
},
"menu": {
"input": "input",
@@ -538,6 +560,7 @@
"label": "info",
"node": "Node",
"type": "Type",
"group": "Group",
"module": "Module",
"id": "ID",
"status": "Status",
@@ -613,7 +636,6 @@
"removeFromProject": "remove from project",
"addToProject": "add to project",
"files": "Files",
"package": "Package",
"flow": "Flow",
"credentials": "Credentials",
"package":"Package",
@@ -757,7 +779,8 @@
"bin": "buffer",
"date": "timestamp",
"jsonata": "expression",
"env": "env variable"
"env": "env variable",
"cred": "credential"
}
},
"editableList": {

View File

@@ -351,7 +351,8 @@
"bool": "真偽",
"json": "JSON",
"bin": "バッファ",
"env": "環境変数"
"env": "環境変数",
"cred": "認証情報"
},
"menu": {
"input": "入力",

View File

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

View File

@@ -21,6 +21,7 @@ RED.history = (function() {
var i;
var len;
var node;
var group;
var subflow;
var modifiedTabs = {};
var inverseEv;
@@ -74,6 +75,15 @@ RED.history = (function() {
RED.nodes.removeLink(ev.links[i]);
}
}
if (ev.groups) {
inverseEv.groups = [];
for (i=0;i<ev.groups.length;i++) {
group = ev.groups[i];
modifiedTabs[group.z] = true;
inverseEv.groups.push(group);
RED.nodes.removeGroup(group);
}
}
if (ev.workspaces) {
inverseEv.workspaces = [];
for (i=0;i<ev.workspaces.length;i++) {
@@ -193,12 +203,35 @@ RED.history = (function() {
n.dirty = true;
});
}
if (ev.groups) {
inverseEv.groups = [];
var groupsToAdd = new Set(ev.groups.map(function(g) { return g.id }));
for (i=0;i<ev.groups.length;i++) {
RED.nodes.addGroup(ev.groups[i])
modifiedTabs[ev.groups[i].z] = true;
inverseEv.groups.push(ev.groups[i]);
if (ev.groups[i].g && !groupsToAdd.has(ev.groups[i].g)) {
group = RED.nodes.group(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) {
@@ -260,6 +293,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 }),true);
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",
@@ -370,7 +410,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 +453,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 +467,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 +483,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 +498,55 @@ 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);
}
}
Object.keys(modifiedTabs).forEach(function(id) {
@@ -460,8 +557,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();
@@ -482,6 +579,9 @@ RED.history = (function() {
list: function() {
return undoHistory;
},
listRedo: function() {
return redoHistory;
},
depth: function() {
return undoHistory.length;
},

View File

@@ -44,6 +44,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 +61,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"
}
}

View File

@@ -27,6 +27,9 @@ RED.nodes = (function() {
var subflows = {};
var loadedFlowVersion = null;
var groups = {};
var groupsByZ = {};
var initialLoad;
var dirty = false;
@@ -302,6 +305,10 @@ RED.nodes = (function() {
}
function moveNodeToTab(node, z) {
if (node.type === "group") {
moveGroupToTab(node,z);
return;
}
if (nodeTabMap[node.z]) {
delete nodeTabMap[node.z][node.id];
}
@@ -311,6 +318,13 @@ RED.nodes = (function() {
nodeTabMap[z][node.id] = node;
node.z = z;
}
function moveGroupToTab(group, z) {
var index = groupsByZ[group.z].indexOf(group);
groupsByZ[group.z].splice(index,1);
groupsByZ[z] = groupsByZ[z] || [];
groupsByZ[z].push(group);
group.z = z;
}
function removeLink(l) {
var index = links.indexOf(l);
@@ -340,6 +354,7 @@ RED.nodes = (function() {
var removedNodes = [];
var removedLinks = [];
var removedGroups = [];
var n;
var node;
for (n=0;n<nodes.length;n++) {
@@ -356,11 +371,17 @@ RED.nodes = (function() {
}
}
}
removedGroups = groupsByZ[id] || [];
removedGroups.forEach(function(g) {
delete groups[g.id]
})
delete groupsByZ[id];
for (n=0;n<removedNodes.length;n++) {
var result = removeNode(removedNodes[n].id);
removedLinks = removedLinks.concat(result.links);
}
return {nodes:removedNodes,links:removedLinks};
return {nodes:removedNodes,links:removedLinks, groups: removedGroups};
}
function addSubflow(sf, createNewIds) {
@@ -398,6 +419,10 @@ RED.nodes = (function() {
paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
oneditprepare: function() {
RED.subflow.buildEditForm("subflow",this);
RED.subflow.buildPropertiesForm(this);
},
oneditresize: function(size) {
// var rows = $(".dialog-form>div:not(.node-input-env-container-row)");
var height = size.height;
@@ -493,6 +518,9 @@ RED.nodes = (function() {
if (n.d === true) {
node.d = true;
}
if (n.g) {
node.g = n.g;
}
if (node.type == "unknown") {
for (var p in n._orig) {
if (n._orig.hasOwnProperty(p)) {
@@ -505,19 +533,33 @@ RED.nodes = (function() {
node[d] = n[d];
}
}
if(exportCreds && n.credentials) {
if (exportCreds) {
var credentialSet = {};
node.credentials = {};
for (var cred in n._def.credentials) {
if (n._def.credentials.hasOwnProperty(cred)) {
if (n._def.credentials[cred].type == 'password') {
if (/^subflow:/.test(node.type) && n.credentials) {
// A subflow instance node can have arbitrary creds
for (var sfCred in n.credentials) {
if (n.credentials.hasOwnProperty(sfCred)) {
if (!n.credentials._ ||
n.credentials["has_"+cred] != n.credentials._["has_"+cred] ||
(n.credentials["has_"+cred] && n.credentials[cred])) {
n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] ||
(n.credentials["has_"+sfCred] && n.credentials[sfCred])) {
credentialSet[sfCred] = n.credentials[sfCred];
}
}
}
} else if (n.credentials) {
node.credentials = {};
// All other nodes have a well-defined list of possible credentials
for (var cred in n._def.credentials) {
if (n._def.credentials.hasOwnProperty(cred)) {
if (n._def.credentials[cred].type == 'password') {
if (!n.credentials._ ||
n.credentials["has_"+cred] != n.credentials._["has_"+cred] ||
(n.credentials["has_"+cred] && n.credentials[cred])) {
credentialSet[cred] = n.credentials[cred];
}
} else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) {
credentialSet[cred] = n.credentials[cred];
}
} else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) {
credentialSet[cred] = n.credentials[cred];
}
}
}
@@ -526,6 +568,13 @@ RED.nodes = (function() {
}
}
}
if (n.type === "group") {
node.x = n.x;
node.y = n.y;
node.w = n.w;
node.h = n.h;
node.nodes = node.nodes.map(function(n) { return n.id });
}
if (n._def.category != "config") {
node.x = n.x;
node.y = n.y;
@@ -568,7 +617,7 @@ RED.nodes = (function() {
return node;
}
function convertSubflow(n) {
function convertSubflow(n, exportCreds) {
var node = {};
node.id = n.id;
node.type = n.type;
@@ -578,6 +627,24 @@ RED.nodes = (function() {
node.in = [];
node.out = [];
node.env = n.env;
if (exportCreds) {
var credentialSet = {};
// A subflow node can have arbitrary creds
for (var sfCred in n.credentials) {
if (n.credentials.hasOwnProperty(sfCred)) {
if (!n.credentials._ ||
n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] ||
(n.credentials["has_"+sfCred] && n.credentials[sfCred])) {
credentialSet[sfCred] = n.credentials[sfCred];
}
}
}
if (Object.keys(credentialSet).length > 0) {
node.credentials = credentialSet;
}
}
node.color = n.color;
n.in.forEach(function(p) {
@@ -633,8 +700,18 @@ RED.nodes = (function() {
/**
* Converts the current node selection to an exportable JSON Object
**/
function createExportableNodeSet(set, exportedSubflows, exportedConfigNodes) {
function createExportableNodeSet(set, exportedIds, exportedSubflows, exportedConfigNodes) {
var nns = [];
exportedIds = exportedIds || {};
set = set.filter(function(n) {
if (exportedIds[n.id]) {
return false;
}
exportedIds[n.id] = true;
return true;
})
exportedConfigNodes = exportedConfigNodes || {};
exportedSubflows = exportedSubflows || {};
for (var n=0;n<set.length;n++) {
@@ -650,11 +727,11 @@ RED.nodes = (function() {
subflowSet.push(n);
}
});
var exportableSubflow = createExportableNodeSet(subflowSet, exportedSubflows, exportedConfigNodes);
var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes);
nns = exportableSubflow.concat(nns);
}
}
if (node.type != "subflow") {
if (node.type !== "subflow") {
var convertedNode = RED.nodes.convertNode(node);
for (var d in node._def.defaults) {
if (node._def.defaults[d].type && node[d] in configNodes) {
@@ -671,6 +748,9 @@ RED.nodes = (function() {
}
}
nns.push(convertedNode);
if (node.type === "group") {
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
}
} else {
var convertedSubflow = convertSubflow(node);
nns.push(convertedSubflow);
@@ -693,7 +773,12 @@ RED.nodes = (function() {
}
for (i in subflows) {
if (subflows.hasOwnProperty(i)) {
nns.push(convertSubflow(subflows[i]));
nns.push(convertSubflow(subflows[i], exportCredentials));
}
}
for (i in groups) {
if (groups.hasOwnProperty(i)) {
nns.push(convertNode(groups[i]));
}
}
for (i in configNodes) {
@@ -822,6 +907,7 @@ RED.nodes = (function() {
if (n.type != "workspace" &&
n.type != "tab" &&
n.type != "subflow" &&
n.type != "group" &&
!registry.getNodeType(n.type) &&
n.type.substring(0,8) != "subflow:" &&
unknownTypes.indexOf(n.type)==-1) {
@@ -871,6 +957,7 @@ RED.nodes = (function() {
var node_map = {};
var new_nodes = [];
var new_links = [];
var new_groups = [];
var nid;
var def;
var configNode;
@@ -1038,20 +1125,25 @@ RED.nodes = (function() {
y:parseFloat(n.y || 0),
z:n.z,
type:0,
wires:n.wires||[],
inputLabels: n.inputLabels,
outputLabels: n.outputLabels,
icon: n.icon,
info: n.info,
changed:false,
_config:{}
};
}
if (n.type !== "group") {
node.wires = n.wires||[];
node.inputLabels = n.inputLabels;
node.outputLabels = n.outputLabels;
node.icon = n.icon;
}
if (n.hasOwnProperty('l')) {
node.l = n.l;
}
if (n.hasOwnProperty('d')) {
node.d = n.d;
}
if (n.hasOwnProperty('g')) {
node.g = n.g;
}
if (createNewIds) {
if (subflow_blacklist[n.z]) {
continue;
@@ -1088,7 +1180,17 @@ RED.nodes = (function() {
}
node.type = n.type;
node._def = def;
if (n.type.substring(0,7) === "subflow") {
if (node.type === "group") {
node._def = RED.group.def;
for (d in node._def.defaults) {
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
node[d] = n[d];
node._config[d] = JSON.stringify(n[d]);
}
}
node._config.x = node.x;
node._config.y = node.y;
} else if (n.type.substring(0,7) === "subflow") {
var parentId = n.type.split(":")[1];
var subflow = subflow_blacklist[parentId]||subflow_map[parentId]||getSubflow(parentId);
if (createNewIds) {
@@ -1109,7 +1211,7 @@ RED.nodes = (function() {
defaults: {},
label: "unknown: "+n.type,
labelStyle: "red-ui-flow-node-label-italic",
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
outputs: n.outputs||n.wires.length,
set: registry.getNodeSet("node-red/unknown")
}
} else {
@@ -1178,13 +1280,19 @@ RED.nodes = (function() {
}
}
}
addNode(node);
RED.editor.validateNode(node);
if (node.type !== "group") {
addNode(node);
RED.editor.validateNode(node);
} else {
addGroup(node);
}
node_map[n.id] = node;
// If an 'unknown' config node, it will not have been caught by the
// proper config node handling, so needs adding to new_nodes here
if (node.type === "unknown" || node._def.category !== "config") {
new_nodes.push(node);
} else if (node.type === "group") {
new_groups.push(node);
}
}
}
@@ -1219,6 +1327,11 @@ RED.nodes = (function() {
}
delete n.wires;
}
if (n.g && node_map[n.g]) {
n.g = node_map[n.g].id;
} else {
delete n.g
}
for (var d3 in n._def.defaults) {
if (n._def.defaults.hasOwnProperty(d3)) {
if (n._def.defaults[d3].type && node_map[n[d3]]) {
@@ -1287,9 +1400,20 @@ RED.nodes = (function() {
delete n.status.wires;
}
}
for (i=0;i<new_groups.length;i++) {
n = new_groups[i];
if (n.g && node_map[n.g]) {
n.g = node_map[n.g].id;
} else {
delete n.g;
}
n.nodes = n.nodes.map(function(id) {
return node_map[id];
})
}
RED.workspaces.refresh();
return [new_nodes,new_links,new_workspaces,new_subflows,missingWorkspace];
return [new_nodes,new_links,new_groups,new_workspaces,new_subflows,missingWorkspace];
}
// TODO: supports filter.z|type
@@ -1380,6 +1504,9 @@ RED.nodes = (function() {
nodeTabMap = {};
configNodes = {};
workspacesOrder = [];
groups = {};
groupsByZ = {};
var subflowIds = Object.keys(subflows);
subflowIds.forEach(function(id) {
RED.subflow.removeSubflow(id)
@@ -1408,6 +1535,27 @@ RED.nodes = (function() {
// var loadedFlowVersion = null;
}
function addGroup(group) {
groupsByZ[group.z] = groupsByZ[group.z] || [];
groupsByZ[group.z].push(group);
groups[group.id] = group;
}
function removeGroup(group) {
var i = groupsByZ[group.z].indexOf(group);
groupsByZ[group.z].splice(i,1);
if (group.g) {
if (groups[group.g]) {
var index = groups[group.g].nodes.indexOf(group);
groups[group.g].nodes.splice(index,1);
}
}
RED.group.markDirty(group);
delete groups[group.id];
}
return {
init: function() {
RED.events.on("registry:node-type-added",function(type) {
@@ -1451,8 +1599,9 @@ RED.nodes = (function() {
});
removeLinks.forEach(removeLink);
RED.view.redraw(true);
// Force the redraw to be synchronous so the view updates
// *now* and removes the unknown node
RED.view.redraw(true, true);
var result = importNodes(reimportList,false);
var newNodeMap = {};
result[0].forEach(function(n) {
@@ -1506,6 +1655,11 @@ RED.nodes = (function() {
subflow: getSubflow,
subflowContains: subflowContains,
addGroup: addGroup,
removeGroup: removeGroup,
group: function(id) { return groups[id] },
groups: function(z) { return groupsByZ[z]||[] },
eachNode: function(cb) {
for (var n=0;n<nodes.length;n++) {
if (cb(nodes[n]) === false) {

View File

@@ -472,6 +472,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"});
@@ -524,6 +532,7 @@ var RED = (function() {
}
RED.subflow.init();
RED.group.init();
RED.clipboard.init();
RED.search.init();
RED.actionList.init();

View File

@@ -583,6 +583,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 +593,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);

View File

@@ -0,0 +1,223 @@
RED.colorPicker = (function() {
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);
return '#'+((r<<16) + (g<<8) + b).toString(16).padStart(6,'0')
}
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 = 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);
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": 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
})
});
setTimeout(function() {
refreshDisplay(colorHiddenInput.val())
},50);
return container;
}
return {
create: create
}
})();

View File

@@ -164,6 +164,84 @@
}
})
}
},
cred:{
value:"cred",
label:"credential",
icon:"fa fa-lock",
inputType: "password",
valueLabel: function(container,value) {
var that = this;
container.css("pointer-events","none");
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 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();
},50);
} else {
that.input.attr("type","text");
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
setTimeout(function() {
that.input.focus();
},50);
}
}).hide();
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-1px").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;
@@ -220,6 +298,8 @@
that.input.attr(d,m);
});
this.defaultInputType = this.input.attr('type');
this.uiSelect.addClass("red-ui-typedInput-container");
this.element.attr('type','hidden');
@@ -488,6 +568,56 @@
done(labelWidth);
}
},
_resize: function() {
var that = this;
if (this.uiWidth !== null) {
this.uiSelect.width(this.uiWidth);
}
var type = this.typeMap[this.propertyType];
if (type && type.hasValue === false) {
this.selectTrigger.addClass("red-ui-typedInput-full-width");
} else {
this.selectTrigger.removeClass("red-ui-typedInput-full-width");
this._getLabelWidth(this.selectTrigger, function(labelWidth) {
that.elementDiv.css('left',labelWidth+"px");
that.valueLabelContainer.css('left',labelWidth+"px");
if (that.optionExpandButton.shown) {
that.elementDiv.css('right',"22px");
that.valueLabelContainer.css('right',"22px");
} else {
that.elementDiv.css('right','0');
that.valueLabelContainer.css('right','0');
that.input.css({
'border-top-right-radius': '4px',
'border-bottom-right-radius': '4px'
});
}
if (that.optionSelectTrigger) {
if (type && type.options && type.hasValue === true) {
that.optionSelectLabel.css({'left':'auto'})
that._getLabelWidth(that.optionSelectLabel, function(lw) {
that.optionSelectTrigger.css({'width':(23+lw)+"px"});
that.elementDiv.css('right',(23+lw)+"px");
that.input.css({
'border-top-right-radius': 0,
'border-bottom-right-radius': 0
});
});
} else {
that.optionSelectLabel.css({'left':'0'})
that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'});
if (!that.optionExpandButton.shown) {
that.elementDiv.css({'right':0});
that.input.css({
'border-top-right-radius': '4px',
'border-bottom-right-radius': '4px'
});
}
}
}
});
}
},
_updateOptionSelectLabel: function(o) {
var opt = this.typeMap[this.propertyType];
this.optionSelectLabel.empty();
@@ -515,6 +645,7 @@
}
if (opt.hasValue) {
this.optionValue = o.value;
this._resize();
this.input.trigger('change',this.propertyType,this.value());
}
} else {
@@ -554,12 +685,11 @@
this.propertyType = null;
this.type(currentType);
}
setTimeout(function() {that._resize();},0);
},
width: function(desiredWidth) {
this.uiWidth = desiredWidth;
if (this.uiWidth !== null) {
this.uiSelect.width(this.uiWidth);
}
this._resize();
},
value: function(value) {
var that = this;
@@ -630,23 +760,19 @@
}
else if (opt.icon.indexOf("/") !== -1) {
image = new Image();
image.onload = function() { that._resize(); }
image.onerror = function() { that._resize(); }
image.name = opt.icon;
image.src = mapDeprecatedIcon(opt.icon);
$('<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)) {
this.selectLabel.text(opt.label);
}
if (opt.hasValue === false) {
this.selectTrigger.addClass("red-ui-typedInput-full-width");
} else {
this.selectTrigger.removeClass("red-ui-typedInput-full-width");
}
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
@@ -657,13 +783,11 @@
this.optionExpandButton.shown = false;
}
if (this.optionSelectTrigger) {
this.optionSelectTrigger.css({"display":"inline-flex"});
this.optionSelectTrigger.show();
if (!opt.hasValue) {
this.optionSelectTrigger.css({"flex-grow":1})
this.elementDiv.hide();
this.valueLabelContainer.hide();
} else {
this.optionSelectTrigger.css({"flex-grow":0})
this.elementDiv.show();
this.valueLabelContainer.hide();
}
@@ -778,6 +902,11 @@
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("");
@@ -786,8 +915,8 @@
} else if (opt.valueLabel) {
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);
@@ -837,6 +966,9 @@
this._trigger("typechange",null,this.propertyType);
this.input.trigger('change',this.propertyType,this.value());
}
if (!image) {
this._resize();
}
}
}
},
@@ -863,6 +995,7 @@
},
show: function() {
this.uiSelect.show();
this._resize();
},
hide: function() {
this.uiSelect.hide();

View File

@@ -490,8 +490,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 +498,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 +514,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 +579,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
@@ -819,99 +825,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);
@@ -997,7 +910,30 @@ 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...
nodeDiv.css('backgroundColor',$(this).val());
})
}
@@ -1471,6 +1407,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;
@@ -1599,12 +1548,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')) {
@@ -2252,6 +2202,21 @@ 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;
@@ -2311,7 +2276,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 +2327,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 +2355,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 +2386,249 @@ 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 (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);
}
configNode = RED.nodes.node(newValue);
if (configNode) {
configNode.users.push(editing_node);
}
}
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);
}
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);
}
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) {
@@ -2537,6 +2752,7 @@ 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) },

View File

@@ -0,0 +1,633 @@
/**
* 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>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">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));
colorPalette.push('#'+((r<<16) + (g<<8) + b).toString(16).padStart(6,'0'));
}
var defaultGroupStyle = {};
var groupDef = {
defaults:{
name:{value:""},
style:{value:{}},
nodes:{value:[]}
},
category: "config",
oneditprepare: function() {
var style = this.style || {};
RED.colorPicker.create({
id:"node-input-style-stroke",
value: style.stroke || "#a4a4a4",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
cellHeight: 16,
cellMargin: 3,
none: true,
opacity: style['stroke-opacity'] || 1.0
}).appendTo("#node-input-row-style-stroke");
RED.colorPicker.create({
id:"node-input-style-fill",
value: style.fill || "none",
palette: colorPalette,
cellPerRow: colorCount,
cellWidth: 16,
cellHeight: 16,
cellMargin: 3,
none: true,
opacity: style['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 || "#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.hide")
})
$("#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;
}
if (this.style["stroke-opacity"] === "1") {
delete this.style["stroke-opacity"]
}
if (this.style["fill-opacity"] === "1") {
delete this.style["fill-opacity"]
}
this.resize = true;
},
set:{
module: "node-red"
}
}
function init() {
RED.events.on("view:selection-changed",function(selection) {
RED.menu.setDisabled("menu-item-group-group",!!!selection.nodes);
RED.menu.setDisabled("menu-item-group-ungroup",!!!selection.nodes || selection.nodes.filter(function(n) { return n.type==='group'}).length === 0);
RED.menu.setDisabled("menu-item-group-merge",!!!selection.nodes);
RED.menu.setDisabled("menu-item-group-remove",!!!selection.nodes || selection.nodes.filter(function(n) { return !!n.g }).length === 0);
});
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
}
groupStyleDiv.remove();
}
function convertColorToHex(c) {
var m = /^rgb\((\d+), (\d+), (\d+)\)$/.exec(c);
if (m) {
return "#"+(((parseInt(m[1])<<16) + (parseInt(m[2])<<8) + parseInt(m[3])).toString(16).padStart(6,'0'))
}
return c;
}
var groupStyleClipboard;
function copyGroupStyle() {
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 (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() {
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() {
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;
}
})
RED.nodes.removeGroup(g);
return nodes;
}
function mergeSelection() {
// TODO: this currently creates an entirely new group. Need to merge properties
// of any existing group
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;
}
}
// 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") {
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) {
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() {
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
}
try {
addToGroup(group,nodes);
} catch(err) {
RED.notify(err,"error");
return;
}
group.z = nodes[0].z;
RED.nodes.addGroup(group);
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);
}
}
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;
}
}
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);
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","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);
}
}
refreshDisplay();
var layoutPanel = RED.popover.panel(picker);
layoutPanel.show({
target: layoutButton
})
})
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

@@ -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",
@@ -623,7 +626,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){});
})
@@ -873,7 +876,7 @@ RED.palette.editor = (function() {
$('<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 +902,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 +1027,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

@@ -284,6 +284,9 @@ RED.palette = (function() {
var mouseX;
var mouseY;
var spliceTimer;
var groupTimer;
var activeGroup;
var hoverGroup;
var paletteWidth;
var paletteTop;
$(d).draggable({
@@ -295,16 +298,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 = [];

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

@@ -152,7 +152,8 @@ RED.sidebar.info = (function() {
var types = {
nodes:0,
flows:0,
subflows:0
subflows:0,
groups: 0
}
node.forEach(function(n) {
if (n.type === 'tab') {
@@ -160,6 +161,8 @@ 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++;
}
@@ -179,6 +182,9 @@ 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.
@@ -209,6 +215,36 @@ RED.sidebar.info = (function() {
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"))
}
} else if (node.type === "group") {
// 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.group")+"</td><td></td></tr>").appendTo(tableBody);
RED.utils.createObjectElement(node.id).appendTo(propRow.children()[1]);
if (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]);
}
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);
@@ -225,7 +261,7 @@ RED.sidebar.info = (function() {
}
}
var count = 0;
if (!m && node.type != "subflow") {
if (!m && node.type != "subflow" && node.type != "group") {
var defaults;
if (node.type === 'unknown') {
defaults = {};

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,6 +100,9 @@ RED.view.tools = (function() {
node.n.x += dx;
node.n.y += dy;
node.n.dirty = true;
if (node.n.type === "group") {
RED.group.markDirty(node.n);
}
minX = Math.min(node.n.x-node.n.w/2-5,minX);
minY = Math.min(node.n.y-node.n.h/2-5,minY);
}
@@ -105,6 +115,8 @@ RED.view.tools = (function() {
}
}
RED.view.redraw();
} else {
RED.view.scroll(dx*10,dy*10);
}
}
@@ -112,6 +124,16 @@ RED.view.tools = (function() {
init: function() {
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

@@ -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;

View File

@@ -284,3 +284,8 @@ $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;

View File

@@ -217,10 +217,6 @@
.red-ui-debug-msg-type-number { color: $debug-message-text-color-msg-type-number; };
.red-ui-debug-msg-type-number-toggle { cursor: pointer;}
.red-ui-debug-msg-type-string {
white-space: pre-wrap;
}
.red-ui-debug-msg-row {
display: block;
padding: 4px 2px 2px;

View File

@@ -411,6 +411,133 @@ button.red-ui-button.red-ui-editor-node-appearance-button {
}
}
.red-ui-group-layout-picker {
padding: 5px;
background: $primary-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;
@@ -633,7 +760,7 @@ button.red-ui-toggleButton.toggle {
.red-ui-typedInput-value-label,.red-ui-typedInput-option-label {
select,.placeholder-input {
margin: 3px;
height: 24px;
height: 26px;
width: calc(100% - 10px);
padding-left: 3px;
}

View File

@@ -71,6 +71,48 @@
}
}
.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-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;
}
.red-ui-flow-node-unknown {
stroke-dasharray:10,4;
stroke: $node-border-unknown;
@@ -248,6 +290,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;

View File

@@ -18,7 +18,7 @@
border: 1px solid $form-input-border-color;
border-radius: 4px;
height: 34px;
display: inline-flex;
display: inline-block;
padding: 0;
margin: 0;
vertical-align: middle;
@@ -26,7 +26,12 @@
overflow:visible;
position: relative;
.red-ui-typedInput-input-wrap {
flex-grow: 1;
position: absolute;
left:0;
right:0;
top:0;
bottom:0;
outline: red;
}
input.red-ui-typedInput-input {
width: 100%;
@@ -44,14 +49,17 @@
border-color: $form-input-focus-color !important;
}
.red-ui-typedInput-value-label {
flex-grow: 1;
position: absolute;
display: inline-block;
height: 32px;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
.red-ui-typedInput-value-label-inactive {
background: $secondary-background-disabled;
color: $secondary-text-color-disabled;
}
}
}
.red-ui-typedInput-options {
@@ -99,11 +107,12 @@ button.red-ui-typedInput-option-trigger
{
text-align: left;
border: none;
flex-basis: auto;
position: absolute;
box-sizing: border-box;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
padding: 0 1px 0 5px;
display:inline-block;
background: $form-button-background;
height: 32px;
line-height: 30px;
@@ -117,7 +126,7 @@ button.red-ui-typedInput-option-trigger
}
&.disabled {
cursor: default;
i.red-ui-typedInput-icon {
> i.red-ui-typedInput-icon {
color: $secondary-text-color-disabled;
}
}
@@ -145,7 +154,7 @@ button.red-ui-typedInput-option-trigger
text-decoration: none;
}
&.red-ui-typedInput-full-width {
flex-grow: 1;
width: 100%;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
@@ -161,6 +170,7 @@ button.red-ui-typedInput-option-expand {
border-bottom-right-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
right: 0;
}
button.red-ui-typedInput-option-trigger {
@@ -169,23 +179,27 @@ button.red-ui-typedInput-option-trigger {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
padding: 0 0 0 0;
position:relative;
flex-grow: 1;
line-height: 32px;
display: inline-flex;
position:absolute;
right: 0;
.red-ui-typedInput-option-label {
background:$form-button-background;
color: $form-text-color;
flex-grow: 1;
padding: 0 0 0 8px;
display:inline-block;
position:absolute;
left:0;
right:23px;
top: 0;
padding: 0 5px 0 8px;
i.red-ui-typedInput-icon {
margin-right: 4px;
}
}
.red-ui-typedInput-option-caret {
flex-grow: 0;
display:inline-block;
width: 23px;
text-align: center;
height: 100%;
top: 0;
position: absolute;
right: 0;
bottom: 0;
width: 17px;
padding-left: 5px;
&:before {
content:'';
display: inline-block;

View File

@@ -112,7 +112,7 @@
position: absolute;
bottom: 0;
right:0;
zIndex: 101;
z-index: 101;
border-left: 1px solid $primary-border-color;
border-top: 1px solid $primary-border-color;
background: $view-navigator-background;
@@ -122,7 +122,7 @@
stroke-dasharray: 5,5;
pointer-events: none;
stroke: $secondary-border-color;
strokeWidth: 1;
stroke-width: 1;
fill: $view-background;
}

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="inject">
<script type="text/html" data-template-name="inject">
<div class="form-row">
<label for="node-input-payload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
<input type="text" id="node-input-payload" style="width:70%">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="debug">
<script type="text/html" data-template-name="debug">
<div class="form-row">
<label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
<input id="node-input-typed-complete" type="text" style="width: 70%">

View File

@@ -1,4 +1,4 @@
<script type="text/x-red" data-template-name="complete">
<script type="text/html" data-template-name="complete">
<div class="form-row node-input-target-row">
<button id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div>

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="catch">
<script type="text/html" data-template-name="catch">
<div class="form-row">
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
<select id="node-input-scope-select">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="status">
<script type="text/html" data-template-name="status">
<div class="form-row">
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
<select id="node-input-scope-select">

View File

@@ -1,12 +1,12 @@
<script type="text/x-red" data-template-name="link in">
<script type="text/html" data-template-name="link in">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
</div>
<div class="form-row node-input-link-row"></div>
</script>
<script type="text/x-red" data-template-name="link out">
<script type="text/html" data-template-name="link out">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="comment">
<script type="text/html" data-template-name="comment">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="unknown">
<script type="text/html" data-template-name="unknown">
<div class="form-tips"><span data-i18n="[html]unknown.tip"></span></div>
</script>

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="function">
<script type="text/html" data-template-name="function">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>

View File

@@ -1,10 +1,5 @@
<script type="text/html" data-template-name="change">
<style>
ol#node-input-rule-container .red-ui-typedInput-container {
flex:1;
}
</style>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
@@ -81,6 +76,11 @@
var replace = this._("change.action.replace");
var regex = this._("change.label.regex");
function resizeRule(rule) {
var newWidth = rule.width();
rule.find('.red-ui-typedInput').typedInput("width",newWidth-130);
}
$('#node-input-rule-container').css('min-height','150px').css('min-width','450px').editableList({
addItem: function(container,i,opt) {
var rule = opt;
@@ -106,11 +106,10 @@
overflow: 'hidden',
whiteSpace: 'nowrap'
});
let fragment = document.createDocumentFragment();
var row1 = $('<div/>',{style:"display:flex;"}).appendTo(fragment);
var row2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(fragment);
var row4 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(fragment);
var row1 = $('<div/>').appendTo(container);
var row2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var row3 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var row4 = $('<div/>',{style:"margin-top:8px;"}).appendTo(container);
var selectField = $('<select/>',{class:"node-input-rule-type",style:"width:110px; margin-right:10px;"}).appendTo(row1);
var selectOptions = [{v:"set",l:set},{v:"change",l:change},{v:"delete",l:del},{v:"move",l:move}];
@@ -125,82 +124,41 @@
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
.appendTo(row2);
function createPropertyValue() {
return $('<input/>',{class:"node-input-rule-property-value",type:"text"})
var propertyValue = $('<input/>',{class:"node-input-rule-property-value",type:"text"})
.appendTo(row2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','date','jsonata','env']});
}
var row3_1 = $('<div/>', {style:"display:flex;"}).appendTo(row3);
var row3_1 = $('<div/>').appendTo(row3);
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(search)
.appendTo(row3_1);
function createFromValue() {
return $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
var fromValue = $('<input/>',{class:"node-input-rule-property-search-value",type:"text"})
.appendTo(row3_1)
.typedInput({default:'str',types:['msg','flow','global','str','re','num','bool','env']});
}
var row3_2 = $('<div/>',{style:"display:flex;margin-top:8px;"}).appendTo(row3);
var row3_2 = $('<div/>',{style:"margin-top:8px;"}).appendTo(row3);
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(replace)
.appendTo(row3_2);
function createToValue() {
return $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
var toValue = $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"})
.appendTo(row3_2)
.typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','bin','env']});
}
$('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"})
.text(to)
.appendTo(row4);
function createMoveValue() {
return $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
var moveValue = $('<input/>',{class:"node-input-rule-property-move-value",type:"text"})
.appendTo(row4)
.typedInput({default:'msg',types:['msg','flow','global']});
}
let propertyValue = null;
let fromValue = null;
let toValue = null;
let moveValue = null;
selectField.on("change", function() {
var width = $("#node-input-rule-container").width();
var type = $(this).val();
if (propertyValue) {
propertyValue.typedInput('hide');
}
if (fromValue) {
fromValue.typedInput('hide');
}
if (toValue) {
toValue.typedInput('hide');
}
if (moveValue) {
moveValue.typedInput('hide');
}
if (type == "set") {
if(!propertyValue) {
propertyValue = createPropertyValue();
}
propertyValue.typedInput('show');
row2.show();
row3.hide();
row4.hide();
} else if (type == "change") {
if(!fromValue) {
fromValue = createFromValue();
}
fromValue.typedInput('show');
if(!toValue) {
toValue = createToValue();
}
toValue.typedInput('show');
row2.hide();
row3.show();
row4.hide();
@@ -209,48 +167,30 @@
row3.hide();
row4.hide();
} else if (type == "move") {
if(!moveValue) {
moveValue = createMoveValue();
}
moveValue.typedInput('show');
row2.hide();
row3.hide();
row4.show();
}
resizeRule(container);
});
selectField.val(rule.t);
propertyName.typedInput('value',rule.p);
propertyName.typedInput('type',rule.pt);
if (rule.t == "set") {
if(!propertyValue) {
propertyValue = createPropertyValue();
}
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
}
if (rule.t == "move") {
if(!moveValue) {
moveValue = createMoveValue();
}
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
}
if (rule.t == "change") {
if(!fromValue) {
fromValue = createFromValue();
}
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
if (!toValue) {
toValue = createToValue();
}
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
}
propertyValue.typedInput('value',rule.to);
propertyValue.typedInput('type',rule.tot);
moveValue.typedInput('value',rule.to);
moveValue.typedInput('type',rule.tot);
fromValue.typedInput('value',rule.from);
fromValue.typedInput('type',rule.fromt);
toValue.typedInput('value',rule.to);
toValue.typedInput('type',rule.tot);
selectField.change();
container[0].appendChild(fragment);
var newWidth = $("#node-input-rule-container").width();
resizeRule(container);
},
resizeItem: resizeRule,
removable: true,
sortable: true
});

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="range">
<script type="text/html" data-template-name="range">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:calc(70% - 1px)"/>

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="template">
<script type="text/html" data-template-name="template">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<div style="display: inline-block; width: calc(100% - 105px)"><input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"></div>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="delay">
<script type="text/html" data-template-name="delay">
<div class="form-row">
<label for="node-input-delay-action"><i class="fa fa-tasks"></i> <span data-i18n="delay.action"></span></label>
<select id="node-input-delay-action" style="width:270px !important">

View File

@@ -107,7 +107,9 @@ module.exports = function(RED) {
});
}
var npay;
this.on('input', function(msg) {
if (node.op2type === "payl") { npay = RED.util.cloneMessage(msg); }
processMessageQueue(msg);
});
@@ -189,14 +191,16 @@ module.exports = function(RED) {
}
promise.then(() => {
if (node.op2type === "payl") {
node.send(npay[topic]);
node.send(npay[topic]);
delete npay[topic];
}
else {
else {
msg2.payload = node.topics[topic].m2;
node.send(msg2);
node.send(msg2);
}
delete node.topics[topic];
if (node.op2type === "payl") { node.send(npay); }
else { node.send(msg2); }
node.status({});
}).catch(err => {
node.error(err);
@@ -252,6 +256,9 @@ module.exports = function(RED) {
});
}, node.duration);
}
// else {
// if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
// }
}
return Promise.resolve();
}

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="exec">
<script type="text/html" data-template-name="exec">
<div class="form-row">
<label for="node-input-command"><i class="fa fa-file"></i> <span data-i18n="exec.label.command"></span></label>
<input type="text" id="node-input-command" data-i18n="[placeholder]exec.label.command">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="tls-config">
<script type="text/html" data-template-name="tls-config">
<div class="form-row" class="hide" id="node-config-row-uselocalfiles">
<input type="checkbox" id="node-config-input-uselocalfiles" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-config-input-uselocalfiles" style="width: 70%;"><span data-i18n="tls.label.use-local-files"></label>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http proxy">
<script type="text/html" data-template-name="http proxy">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-config-input-name">

View File

@@ -11,7 +11,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="mqtt in">
<script type="text/html" data-template-name="mqtt in">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">
@@ -75,7 +75,7 @@
});
</script>
<script type="text/x-red" data-template-name="mqtt out">
<script type="text/html" data-template-name="mqtt out">
<div class="form-row">
<label for="node-input-broker"><i class="fa fa-globe"></i> <span data-i18n="mqtt.label.broker"></span></label>
<input type="text" id="node-input-broker">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http in">
<script type="text/html" data-template-name="http in">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:70%;">
@@ -45,7 +45,7 @@
<div id="node-input-tip" class="form-tips"><span data-i18n="httpin.tip.in"></span><code><span id="node-input-path"></span></code>.</div>
</script>
<script type="text/x-red" data-template-name="http response">
<script type="text/html" data-template-name="http response">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="http request">
<script type="text/html" data-template-name="http request">
<div class="form-row">
<label for="node-input-method"><i class="fa fa-tasks"></i> <span data-i18n="httpin.label.method"></span></label>
<select type="text" id="node-input-method" style="width:70%;">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<!-- WebSocket Input Node -->
<script type="text/x-red" data-template-name="websocket in">
<script type="text/html" data-template-name="websocket in">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
@@ -198,7 +198,7 @@
</script>
<!-- WebSocket out Node -->
<script type="text/x-red" data-template-name="websocket out">
<script type="text/html" data-template-name="websocket out">
<div class="form-row">
<label for="node-input-mode"><i class="fa fa-dot-circle-o"></i> <span data-i18n="websocket.label.type"></span></label>
<select id="node-input-mode">
@@ -221,7 +221,7 @@
</script>
<!-- WebSocket Server configuration node -->
<script type="text/x-red" data-template-name="websocket-listener">
<script type="text/html" data-template-name="websocket-listener">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.path"></span></label>
<input id="node-config-input-path" type="text" placeholder="/ws/example">
@@ -240,7 +240,7 @@
</script>
<!-- WebSocket Client configuration node -->
<script type="text/x-red" data-template-name="websocket-client">
<script type="text/html" data-template-name="websocket-client">
<div class="form-row">
<label for="node-config-input-path"><i class="fa fa-bookmark"></i> <span data-i18n="websocket.label.url"></span></label>
<input id="node-config-input-path" type="text" placeholder="ws://example.com/ws">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="tcp in">
<script type="text/html" data-template-name="tcp in">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-server" style="width:120px; margin-right:5px;">
@@ -108,7 +108,7 @@
</script>
<script type="text/x-red" data-template-name="tcp out">
<script type="text/html" data-template-name="tcp out">
<div class="form-row">
<label for="node-input-beserver"><i class="fa fa-dot-circle-o"></i> <span data-i18n="tcpin.label.type"></span></label>
<select id="node-input-beserver" style="width:150px; margin-right:5px;">
@@ -170,15 +170,16 @@
$("#node-input-port-row").hide();
$("#node-input-host-row").hide();
$("#node-input-end-row").hide();
} else if (sockettype == "client"){
$("#node-input-port-row").show();
$("#node-input-host-row").show();
$("#node-input-end-row").show();
} else {
$("#node-input-port-row").show();
$("#node-input-host-row").hide();
$("#node-input-end-row").show();
}
if (sockettype == "client") {
$("#node-input-host-row").show();
} else {
$("#node-input-host-row").hide();
}
};
updateOptions();
$("#node-input-beserver").change(updateOptions);
@@ -187,7 +188,7 @@
</script>
<script type="text/x-red" data-template-name="tcp request">
<script type="text/html" data-template-name="tcp request">
<div class="form-row">
<label for="node-input-server"><i class="fa fa-globe"></i> <span data-i18n="tcpin.label.server"></span></label>
<input type="text" id="node-input-server" placeholder="ip.address" style="width:45%">

View File

@@ -15,7 +15,7 @@
-->
<!-- The Input Node -->
<script type="text/x-red" data-template-name="udp in">
<script type="text/html" data-template-name="udp in">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.listen"></span></label>
<select id="node-input-multicast" style='width:70%'>
@@ -115,7 +115,7 @@
<!-- The Output Node -->
<script type="text/x-red" data-template-name="udp out">
<script type="text/html" data-template-name="udp out">
<div class="form-row">
<label for="node-input-port"><i class="fa fa-envelope"></i> <span data-i18n="udp.label.send"></span></label>
<select id="node-input-multicast" style="width:40%">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="csv">
<script type="text/html" data-template-name="csv">
<div class="form-row">
<label for="node-input-temp"><i class="fa fa-list"></i> <span data-i18n="csv.label.columns"></span></label>
<input type="text" id="node-input-temp" data-i18n="[placeholder]csv.placeholder.columns">
@@ -28,11 +28,15 @@
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.input"></span></label>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:30px; height:25px;"/>&nbsp;<span data-i18n="csv.label.skip-e"></span><br/>
<span data-i18n="csv.label.skip-s"></span>&nbsp;<input type="text" id="node-input-skip" style="width:40px; height:25px;"/>&nbsp;<span data-i18n="csv.label.skip-e"></span><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-hdrin"><label style="width:auto; margin-top:7px;" for="node-input-hdrin"><span data-i18n="csv.label.firstrow"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-strings"><label style="width:auto; margin-top:7px;" for="node-input-strings"><span data-i18n="csv.label.usestrings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_empty_strings"><label style="width:auto; margin-top:7px;" for="node-input-include_empty_strings"><span data-i18n="csv.label.include_empty_strings"></span></label><br/>
<label>&nbsp;</label>
<input style="width:20px; vertical-align:baseline; margin-right:5px;" type="checkbox" id="node-input-include_null_values"><label style="width:auto; margin-top:7px;" for="node-input-include_null_values"><span data-i18n="csv.label.include_null_values"></span></label><br/>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
@@ -45,8 +49,13 @@
<label style="width:100%;"><span data-i18n="csv.label.o2c"></span></label>
</div>
<div class="form-row" style="padding-left:20px;">
<label><i class="fa fa-sign-in"></i> <span data-i18n="csv.label.output"></span></label>
<input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span>
<label><i class="fa fa-sign-out"></i> <span data-i18n="csv.label.output"></span></label>
<!-- <input style="width:20px; vertical-align:top; margin-right:5px;" type="checkbox" id="node-input-hdrout"><label style="width:auto;" for="node-input-hdrout"><span data-i18n="csv.label.includerow"></span></span> -->
<select style="width:60%" id="node-input-hdrout">
<option value="none" data-i18n="csv.hdrout.none"></option>
<option value="all" data-i18n="csv.hdrout.all"></option>
<option value="once" data-i18n="csv.hdrout.once"></option>
</select>
</div>
<div class="form-row" style="padding-left:20px;">
<label></label>
@@ -69,12 +78,14 @@
sep: {value:',',required:true,validate:RED.validators.regex(/^.{1,2}$/)},
//quo: {value:'"',required:true},
hdrin: {value:""},
hdrout: {value:""},
hdrout: {value:"none"},
multi: {value:"one",required:true},
ret: {value:'\\n'},
temp: {value:""},
skip: {value:"0"},
strings: {value:true}
strings: {value:true},
include_empty_strings: {value:""},
include_null_values: {value:""}
},
inputs:1,
outputs:1,
@@ -86,6 +97,8 @@
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.hdrout === false) { this.hdrout = "none"; $("#node-input-hdrout").val("none"); }
if (this.hdrout === true) { this.hdrout = "all"; $("#node-input-hdrout").val("all");}
if (this.strings === undefined) { this.strings = true; $("#node-input-strings").prop('checked', true); }
if (this.skip === undefined) { this.skip = 0; $("#node-input-skip").val("0");}
$("#node-input-skip").spinner({ min:0 });

View File

@@ -26,12 +26,16 @@ module.exports = function(RED) {
this.lineend = "\n";
this.multi = n.multi || "one";
this.hdrin = n.hdrin || false;
this.hdrout = n.hdrout || false;
this.hdrout = n.hdrout || "none";
this.goodtmpl = true;
this.skip = parseInt(n.skip || 0);
this.store = [];
this.parsestrings = n.strings;
this.include_empty_strings = n.include_empty_strings || false;
this.include_null_values = n.include_null_values || false;
if (this.parsestrings === undefined) { this.parsestrings = true; }
if (this.hdrout === false) { this.hdrout = "none"; }
if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true;
var node = this;
@@ -49,14 +53,22 @@ module.exports = function(RED) {
return col;
}
node.template = clean(node.template);
node.hdrSent = false;
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset")) {
node.hdrSent = false;
}
if (msg.hasOwnProperty("payload")) {
if (typeof msg.payload == "object") { // convert object to CSV string
try {
var ou = "";
if (node.hdrout) {
if (node.hdrout !== "none" && node.hdrSent === false) {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) {
node.template = clean((msg.columns || "").split(","));
}
ou += node.template.join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; }
}
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
for (var s = 0; s < msg.payload.length; s++) {
@@ -75,13 +87,15 @@ module.exports = function(RED) {
ou += msg.payload[s].join(node.sep) + node.ret;
}
else {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) {
node.template = clean((msg.columns || "").split(","));
}
if ((node.template.length === 1) && (node.template[0] === '')) {
/* istanbul ignore else */
if (tmpwarn === true) { // just warn about missing template once
node.warn(RED._("csv.errors.obj_csv"));
tmpwarn = false;
}
ou = "";
for (var p in msg.payload[0]) {
/* istanbul ignore else */
if (msg.payload[0].hasOwnProperty(p)) {
@@ -125,6 +139,7 @@ module.exports = function(RED) {
}
}
msg.payload = ou;
msg.columns = node.template.join(',');
if (msg.payload !== '') { node.send(msg); }
}
catch(e) { node.error(e,msg); }
@@ -173,20 +188,29 @@ module.exports = function(RED) {
}
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
j += 1;
k[j] = "";
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
k[j] = line.length - 1 === i ? null : "";
}
else if ((line[i] === "\n") || (line[i] === "\r")) { // handle multiple lines
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
if (line[i-1] === node.sep) k[j] = null;
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
@@ -204,15 +228,19 @@ module.exports = function(RED) {
// Finished so finalize and send anything left
//console.log(j,k,o,k[j]);
if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
if ( (node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { k[j].replace(/\r$/,''); }
o[node.template[j]] = k[j];
if ( node.template[j] && (node.template[j] !== "") ) {
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j]) ) { k[j] = parseFloat(k[j]); }
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
if (node.include_null_values && k[j] === null) o[node.template[j]] = k[j];
if (node.include_empty_strings && k[j] === "") o[node.template[j]] = k[j];
if (k[j] !== null && k[j] !== "") o[node.template[j]] = k[j];
}
if (JSON.stringify(o) !== "{}") { // don't send empty objects
a.push(o); // add to the array
}
var has_parts = msg.hasOwnProperty("parts");
if (node.multi !== "one") {
msg.payload = a;
if (has_parts) {
@@ -221,12 +249,14 @@ module.exports = function(RED) {
}
if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store;
msg.columns = node.template.filter(val => val).join(',');
delete msg.parts;
node.send(msg);
node.store = [];
}
}
else {
msg.columns = node.template.filter(val => val).join(',');
node.send(msg); // finally send the array
}
}
@@ -234,6 +264,7 @@ module.exports = function(RED) {
var len = a.length;
for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = node.template.filter(val => val).join(',');
newMessage.payload = a[i];
if (!has_parts) {
newMessage.parts = {
@@ -259,7 +290,11 @@ module.exports = function(RED) {
}
else { node.warn(RED._("csv.errors.csv_js")); }
}
else { node.send(msg); } // If no payload - just pass it on.
else {
if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on.
}
}
});
}
RED.nodes.registerType("csv",CSVNode);

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="html">
<script type="text/html" data-template-name="html">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="json">
<script type="text/html" data-template-name="json">
<div class="form-row">
<label for="node-input-action"><i class="fa fa-dot-circle-o"></i> <span data-i18n="json.label.action"></span></label>
<select style="width:70%" id="node-input-action">

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="xml">
<script type="text/html" data-template-name="xml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>

View File

@@ -1,5 +1,5 @@
<script type="text/x-red" data-template-name="yaml">
<script type="text/html" data-template-name="yaml">
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="sort">
<script type="text/html" data-template-name="sort">
<div class="form-row">
<label for="node-input-target"><i class="fa fa-dot-circle-o"></i> <span data-i18n="sort.target"></span></label>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-template-name="watch">
<script type="text/html" data-template-name="watch">
<div class="form-row node-input-filename">
<label for="node-input-files"><i class="fa fa-file"></i> <span data-i18n="watch.label.files"></span></label>
<input id="node-input-files" type="text" tabindex="1" data-i18n="[placeholder]watch.placeholder.files">

View File

@@ -0,0 +1 @@
[{"id":"bf16276d.2f1758","type":"tab","label":"Example: Number-based Group Mode","disabled":false,"info":"*Number-based Group mode* of batch node can be used to create new message sequences from incoming messages. Recently received *N*-messages are grouped to a sequence. Creating message sequences that has overwrap with adjacent message group is possible.\n"},{"id":"f5a82278.78d6c","type":"batch","z":"bf16276d.2f1758","name":"","mode":"count","count":"5","overlap":0,"interval":"5","allowEmptySequence":false,"topics":[],"x":370,"y":232,"wires":[["b1e514ed.44f328"]]},{"id":"43720065.2891d","type":"comment","z":"bf16276d.2f1758","name":"Group 5 consecutive messages","info":"","x":170,"y":60,"wires":[]},{"id":"b1e514ed.44f328","type":"join","z":"bf16276d.2f1758","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":510,"y":232,"wires":[["457e5970.8ceaa8"]]},{"id":"457e5970.8ceaa8","type":"debug","z":"bf16276d.2f1758","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":232,"wires":[]},{"id":"8e3d3ceb.bd0fe","type":"comment","z":"bf16276d.2f1758","name":"↑ create message sequence with 5 messages","info":"","x":490,"y":272,"wires":[]},{"id":"fbe20ae3.cbb6f8","type":"comment","z":"bf16276d.2f1758","name":"↓ join sequence to array","info":"","x":560,"y":192,"wires":[]},{"id":"7ebafe58.2a112","type":"inject","z":"bf16276d.2f1758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":152,"wires":[["589603c4.1cb5fc"]]},{"id":"589603c4.1cb5fc","type":"function","z":"bf16276d.2f1758","name":"send: 0-49","func":"for(var x = 0; x < 50; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":152,"wires":[["f5a82278.78d6c"]]},{"id":"9b59b72c.fcd8f8","type":"comment","z":"bf16276d.2f1758","name":"↓ send sequence: 0-49","info":"","x":340,"y":112,"wires":[]},{"id":"6421756f.6abd5c","type":"batch","z":"bf16276d.2f1758","name":"","mode":"count","count":"5","overlap":"1","interval":"5","allowEmptySequence":false,"topics":[],"x":370,"y":500,"wires":[["199cf232.743e7e"]]},{"id":"657b6a53.2a1fc4","type":"comment","z":"bf16276d.2f1758","name":"Group 5 consecutive messages with overlap of 1 msg","info":"","x":240,"y":328,"wires":[]},{"id":"199cf232.743e7e","type":"join","z":"bf16276d.2f1758","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":510,"y":500,"wires":[["91d29dda.d8a4f"]]},{"id":"91d29dda.d8a4f","type":"debug","z":"bf16276d.2f1758","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":670,"y":500,"wires":[]},{"id":"8bd4d407.aa2af8","type":"comment","z":"bf16276d.2f1758","name":"↑ create message sequence with 5 messages with overlap of 1 msg","info":"","x":560,"y":540,"wires":[]},{"id":"a49ea57d.8d2458","type":"comment","z":"bf16276d.2f1758","name":"↓ join sequence to array","info":"","x":560,"y":460,"wires":[]},{"id":"f689d1b3.90e4b","type":"inject","z":"bf16276d.2f1758","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":420,"wires":[["c021cf24.ad03e"]]},{"id":"c021cf24.ad03e","type":"function","z":"bf16276d.2f1758","name":"send: 0-49","func":"for(var x = 0; x < 50; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":420,"wires":[["6421756f.6abd5c"]]},{"id":"8da6e576.901a18","type":"comment","z":"bf16276d.2f1758","name":"↓ send sequence: 0-49","info":"","x":340,"y":380,"wires":[]}]

View File

@@ -0,0 +1 @@
[{"id":"82a01f29.86de","type":"tab","label":"Example: Time-based Group Mode","disabled":false,"info":"*Time-based Group mode* of batch node can be used to create new message sequences from incoming messages received within specified time range. \n"},{"id":"9a7f6539.6e36d8","type":"batch","z":"82a01f29.86de","name":"","mode":"interval","count":10,"overlap":0,"interval":"5","allowEmptySequence":false,"topics":[],"x":350,"y":260,"wires":[["e54a3b57.3677f8"]]},{"id":"71dc607e.98aab","type":"comment","z":"82a01f29.86de","name":"Group messages received within 5s","info":"","x":180,"y":80,"wires":[]},{"id":"e54a3b57.3677f8","type":"join","z":"82a01f29.86de","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":490,"y":260,"wires":[["1e263cac.6641f3"]]},{"id":"1e263cac.6641f3","type":"debug","z":"82a01f29.86de","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":650,"y":260,"wires":[]},{"id":"324c7b93.0db734","type":"comment","z":"82a01f29.86de","name":"↑ create message sequence received within 5s","info":"","x":480,"y":300,"wires":[]},{"id":"fb112bae.fbe428","type":"comment","z":"82a01f29.86de","name":"↓ join sequence to array","info":"","x":540,"y":220,"wires":[]},{"id":"34f8dda5.2864c2","type":"inject","z":"82a01f29.86de","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":160,"wires":[["c9e23ee4.ce535"]]},{"id":"c9e23ee4.ce535","type":"function","z":"82a01f29.86de","name":"send: 0-49","func":"for(var x = 0; x < 100; x++) {\n node.send({payload: x});\n}","outputs":1,"noerr":0,"x":310,"y":160,"wires":[["7026e0cc.4e3c3"]]},{"id":"7026e0cc.4e3c3","type":"delay","z":"82a01f29.86de","name":"","pauseType":"rate","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":490,"y":160,"wires":[["9a7f6539.6e36d8"]]},{"id":"40f8c766.6ed198","type":"comment","z":"82a01f29.86de","name":"↓ send sequence: 0-49","info":"","x":340,"y":120,"wires":[]}]

View File

@@ -0,0 +1 @@
[{"id":"845b226d.a4b18","type":"tab","label":"Example: Concatenate Mode","disabled":false,"info":"*Concatenate mode* of batch node can be used to combine input message sequences to create a new message sequence. Order of the sequences can be specified using message topic assigned to each message in a sequence. Message sequence can be specified multiple times.\n"},{"id":"72afe7b0.38b9d8","type":"inject","z":"845b226d.a4b18","name":"","topic":"SEQ","payload":"[1,2,3,4,5]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":100,"wires":[["6dea90dd.c442c"]]},{"id":"6dea90dd.c442c","type":"split","z":"845b226d.a4b18","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":330,"y":100,"wires":[["3ac93a4b.ddbbc6"]]},{"id":"3ac93a4b.ddbbc6","type":"batch","z":"845b226d.a4b18","name":"","mode":"concat","count":10,"overlap":0,"interval":10,"allowEmptySequence":false,"topics":[{"topic":"SEQ"},{"topic":"SEQ"}],"x":470,"y":100,"wires":[["48ec7040.56f5f"]]},{"id":"48ec7040.56f5f","type":"join","z":"845b226d.a4b18","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":610,"y":100,"wires":[["902613c4.769b5"]]},{"id":"902613c4.769b5","type":"debug","z":"845b226d.a4b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":100,"wires":[]},{"id":"a84cf2e1.65adc","type":"comment","z":"845b226d.a4b18","name":"Duplicate","info":"","x":100,"y":60,"wires":[]},{"id":"3256f015.45c36","type":"inject","z":"845b226d.a4b18","name":"","topic":"SEQ","payload":"[1,-6,-8,7,2,-3]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":160,"y":220,"wires":[["c308dcb2.621da"]]},{"id":"c308dcb2.621da","type":"split","z":"845b226d.a4b18","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":330,"y":220,"wires":[["2222098b.7fd036"]]},{"id":"247a5fab.239cc","type":"comment","z":"845b226d.a4b18","name":"Filter & Concat","info":"","x":120,"y":180,"wires":[]},{"id":"2222098b.7fd036","type":"switch","z":"845b226d.a4b18","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"},{"t":"else"}],"checkall":"true","repair":true,"outputs":2,"x":390,"y":280,"wires":[["56e3a974.2bfde8"],["86a1b43a.ff4cb8"]]},{"id":"cd00a796.e4e478","type":"comment","z":"845b226d.a4b18","name":"↑ Duplicate SEQ","info":"","x":500,"y":140,"wires":[]},{"id":"56e3a974.2bfde8","type":"change","z":"845b226d.a4b18","name":"Topic←POS","rules":[{"t":"set","p":"topic","pt":"msg","to":"POS","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":240,"wires":[["fe90d65d.a6b548"]]},{"id":"86a1b43a.ff4cb8","type":"change","z":"845b226d.a4b18","name":"Topic←NEG","rules":[{"t":"set","p":"topic","pt":"msg","to":"NEG","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":300,"wires":[["fe90d65d.a6b548"]]},{"id":"fe90d65d.a6b548","type":"batch","z":"845b226d.a4b18","name":"","mode":"concat","count":10,"overlap":0,"interval":10,"allowEmptySequence":false,"topics":[{"topic":"NEG"},{"topic":"POS"}],"x":710,"y":280,"wires":[["5b089f16.62b96"]]},{"id":"2f46c0af.bff71","type":"debug","z":"845b226d.a4b18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":930,"y":220,"wires":[]},{"id":"5b089f16.62b96","type":"join","z":"845b226d.a4b18","name":"","mode":"auto","build":"string","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":770,"y":220,"wires":[["2f46c0af.bff71"]]},{"id":"e069eb28.6eb358","type":"comment","z":"845b226d.a4b18","name":"↑ Order sequence: negative→positive","info":"","x":810,"y":320,"wires":[]},{"id":"aeae162b.efd118","type":"comment","z":"845b226d.a4b18","name":"Filter pos/neg and make separate sequence↑ (but not a simple sort) ","info":"","x":320,"y":340,"wires":[]}]

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="inject">
<script type="text/html" data-help-name="inject">
<p> Injiziert eine Nachricht manuell oder in regelmäßigen Intervallen in einen Nachrichtenflow.
Bei den Nutzdaten kann es sich um eine Vielzahl von Typen handeln, einschließlich Zeichenfolgen, JavaScript-Objekte oder die aktuelle Zeit. </p>
<h3> Ausgaben </h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="debug">
<script type="text/html" data-help-name="debug">
<p> Zeigt die ausgewählten Nachrichteneigenschaften auf der Registerkarte "Debug" und
optional im Laufzeitprotokoll an. Standardmäßig wird <code>msg.payload</code> angezeigt. </p>
<h3> Details </h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="catch">
<script type="text/html" data-help-name="catch">
<p> Fängt Fehler von Nodes auf derselben Registerkarte ab. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="status">
<script type="text/html" data-help-name="status">
<p> Berichtet Statusnachrichten von anderen Node auf derselben Registerkarte. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="link in">
<script type="text/html" data-help-name="link in">
<p> Erstellt virtuelle Verbindungen zwischen Flows. </p>
<h3> Details </h3>
<p> Der Node kann mit jedem beliebigen <code>Link-out</code> Node auf einer beliebigen Registerkarte verbunden werden.
@@ -25,7 +25,7 @@
<p> <b> Hinweis: </b> Links können nicht in einem Subflow erstellt oder aus einem Subflow heraus erstellt werden. </p>
</script>
<script type="text/x-red" data-help-name="link out">
<script type="text/html" data-help-name="link out">
<p> Erstellt virtuelle Verbindungen zwischen Flows. </p>
<h3> Details </h3>
<p> Der Node kann mit jedem beliebigen <code>Link-out</code> Node auf einer beliebigen Registerkarte verbunden werden.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="comment">
<script type="text/html" data-help-name="comment">
<p>Ein Node, der zur Kommentierung der Flows verwendet werden kann.</p>
<h3>Details</h3>
<p>Das Textfeld des Node akzeptiert die Markdown Syntax. Der eingegebene Text wird dann in diesem Informations Panel angezeigt.

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="unknown">
<script type="text/html" data-help-name="unknown">
<p>Dieser Node ist von einem Typ, der in der aktuellen Node-RED Installation unbekannt ist.</p>
<h3>Details</h3>
<p><i>Wenn der Flow im aktuellen Zustand eingesetzt wird, bleibt die Konfiguration erhalten

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="function">
<script type="text/html" data-help-name="function">
<p> Ein JavaScript-Funktionsblock, der für die Nachrichten ausgeführt werden soll, die vom Node empfangen werden. </p>
<p> Die Nachrichten werden als JavaScript-Objekt mit dem Namen <code>msg</code> übergeben. </p>
<p> Er erwartet eine Eigenschaft <code> msg.payload </code> , die den Hauptteil der Nachricht enthält. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="switch">
<script type="text/html" data-help-name="switch">
<p>Weiterleitung von Nachrichten basierend auf den Werten ihrer Eigenschaften oder der Position der Sequenz.</p>
<h3>Details</h3>
<p>Wenn eine Nachricht ankommt, wertet der Node jede der definierten Regeln aus

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="change">
<script type="text/html" data-help-name="change">
<p>Setzen, Ändern, Löschen oder Verschieben von Eigenschaften einer Nachricht, eines Flow-Kontextes oder eines globalen Kontextes.</p>
<p>Der Node kann mehrere Regeln angeben, die in der Reihenfolge ihrer Definition angewendet werden.</p>
<h3>Details</h3>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="range">
<script type="text/html" data-help-name="range">
<p>Ordnet einen numerischen Wert einem anderen Bereich zu..</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="template">
<script type="text/html" data-help-name="template">
<p> Legt eine Eigenschaft fest, die auf der bereitgestellten Vorlage basiert. </p>
<h3> Eingaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="delay">
<script type="text/html" data-help-name="delay">
<p> Verzögert jede Nachricht, die den Node durchläuft oder begrenzt die Geschwindigkeit, mit der sie übergeben werden können. </p>
<h3> Eingaben </h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="trigger">
<script type="text/html" data-help-name="trigger">
<p> Wenn der Trigger ausgelöst wird, kann eine Nachricht gesendet werden und dann optional eine weitere Nachricht ,
sofern der Trigger nicht verlängert oder zurückgesetzt wird. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="exec">
<script type="text/html" data-help-name="exec">
<p> Führt einen Systembefehl aus und gibt seine Ausgabe zurück. </p>
<p> Der Node kann so konfiguriert werden, dass er entweder wartet, bis der Befehl abgeschlossen ist,
oder die Ausgabe so sendet wie der Befehl sie generiert. </p>

View File

@@ -14,6 +14,6 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="tls-config">
<script type="text/html" data-help-name="tls-config">
<p>Konfigurationsoptionen für TLS Verbindungen.</p>
</script>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http proxy">
<script type="text/html" data-help-name="http proxy">
<p>Konfigurationsoptionen für den HTTP Proxy.</p>
<h3>Details</h3>

View File

@@ -11,7 +11,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="mqtt in">
<script type="text/html" data-help-name="mqtt in">
<p> Stellt eine Verbindung zu einem MQTT-Broker her und subskribiert Nachrichten zu dem angegebenen Topic. </p>
<h3> Ausgaben </h3>
<dl class="message-properties">
@@ -31,7 +31,7 @@
<p> Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Brokerverbindung nutzen. </p>
</script>
<script type="text/x-red" data-help-name="mqtt out">
<script type="text/html" data-help-name="mqtt out">
<p>Stellt eine Verbindung zu einem MQTT-Broker her und publiziert Nachrichten.</p>
<h3>Eingaben</h3>
<dl class="message-properties">
@@ -61,7 +61,7 @@
<p>Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Brokerverbindung nutzen.</p>
</script>
<script type="text/x-red" data-help-name="mqtt-broker">
<script type="text/html" data-help-name="mqtt-broker">
<p> Konfiguration für eine Verbindung zu einem MQTT-Broker. </p>
<p> Diese Konfiguration erstellt eine Verbindung zu einem Broker, die anschließend von den
Nodes <code>MQTT In</code> und <code>MQTT Out</code> verwendet werden. </p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http in">
<script type="text/html" data-help-name="http in">
<p>Erstellt einen HTTP Endpunkt zur Erzeugung von Web Services.</p>
<h3>Outputs</h3>
<dl class="message-properties">
@@ -53,7 +53,7 @@
muss einen code>HTTP Response</code> Node enthalten, um die Anforderung abzuschließen. </p>
</script>
<script type="text/x-red" data-help-name="http response">
<script type="text/html" data-help-name="http response">
<p>Sendet Antworten auf Anforderungen, die von einem code>HTTP In</code> Node empfangen wurden. </p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="http request">
<script type="text/html" data-help-name="http request">
<p>Sendet HTTP-Anforderungen und gibt die Antwort zurück.</p>
<h3>Eingaben</h3>

View File

@@ -14,29 +14,29 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="websocket in">
<script type="text/html" data-help-name="websocket in">
<p>WebSocket Eingangs-Node.</p>
<p>Standardmäßig befinden sich die vom WebSocket empfangenen Daten in <code>msg.payload</code>.
Der Socket kann konfiguriert werden, um einen korrekt gebildeten JSON-String zu erwarten,
in diesem Fall wird er das JSON analysieren und das resultierende Objekt als gesamte Nachricht senden.</p>
</script>
<script type="text/x-red" data-help-name="websocket out">
<script type="text/html" data-help-name="websocket out">
<p>WebSocket Ausgabe-Node.</p>
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
<p>Standardmäßig wird <code>msg.payload</code> über den WebSocket gesendet.
Der Socket kann so konfiguriert werden, dass er das gesamte <code>msg</code> Objekt als JSON-String kodiert und über den WebSocket sendet.</p>
<p>Wenn die an diesem Node ankommende Nachricht an einem WebSocket-Eingangs-Node begann,
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
wird die Nachricht an den Client zurückgesendet, der den Flow ausgelöst hat.
Andernfalls wird die Nachricht an alle verbundenen Clients gesendet..</p>
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
<p>Wenn eine Nachricht, die an einem WebSocket-Eingangsnoten gestartet wurde, an alle verbunden Clients gesendet werden soll,
muss die Eigenschaft <code>msg._session</code> innerhalb des Flow gelöscht werden.</p>
</script>
<script type="text/x-red" data-help-name="websocket-listener">
<script type="text/html" data-help-name="websocket-listener">
<p>Dieser Konfigurations-Node erstellt einen WebSocket Server-Endpunkt unter Verwendung des angegebenen Pfades.</p>
</script>
<script type="text/x-red" data-help-name="websocket-client">
<script type="text/html" data-help-name="websocket-client">
<p>Dieser Konfigurations-Node verbindet einen WebSocket-Client mit der angegebenen URL.</p>
</script>

View File

@@ -14,13 +14,13 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="tcp in">
<script type="text/html" data-help-name="tcp in">
<p>Bietet eine Auswahl an TCP-Eingängen. Kann sich entweder mit einem entfernten TCP-Port verbinden oder eingehende Verbindungen akzeptieren.</p>
<p><b>Note: </b>Auf einigen Systemen benötigen Sie möglicherweise Root- oder Administratorzugriff, um
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="tcp out">
<script type="text/html" data-help-name="tcp out">
<p>Bietet eine Auswahl an TCP-Ausgängen. Kann sich entweder mit einem entfernten TCP-Port verbinden,
eingehende Verbindungen akzeptieren oder auf Nachrichten antworten, die von einem TCP-In-Node empfangen werden.</p>
<p>Nur der Inhalt von <code>msg.payload</code> wird gesendet.</p>
@@ -32,7 +32,7 @@
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="tcp request">
<script type="text/html" data-help-name="tcp request">
<p>Ein einfacher TCP-Anforderungs-Node - sendet die <code>msg.payload</code> an einen Server-TCP-Port und erwartet eine Antwort.</p>
<p>Verbindet sich, sendet die "Anforderung" und liest die "Antwort". Der Node wartet entweder auf eine vorgegebene Anzahl von
Zeichen in einen festen Buffer, auf ein bestimmtes Zeichen oder einen festen Timeout ab der ersten Antwort,

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="udp in">
<script type="text/html" data-help-name="udp in">
<p>Ein UDP-Eingangs-Node, der eine <code>msg.payload</code> erzeugt, die einen
Buffer, Strings oder base64-kodierter String enthält. Multicast wird unterstützt.</p>
<p>Über die Eigenschaften <code>msg.ip</code> und <code>msg.port</code> kann auf die Werte der
@@ -23,7 +23,7 @@
Ports unter 1024 und/oder Broadcast nutzen zu können.</p>
</script>
<script type="text/x-red" data-help-name="udp out">
<script type="text/html" data-help-name="udp out">
<p>Dieser Node sendet <code>msg.payload</code> an den angegebenen UDP-Host und Port. Multicast wird unterstützt.</p>
<p>Sie können <code>msg.ip</code> und <code>msg.port</code> verwenden, um die Zielwerte festzulegen,
aber die statisch im Node konfigurierten Werte haben Vorrang.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="csv">
<script type="text/html" data-help-name="csv">
<p>Konvertiert zwischen einem CSV-formatierten String und ihrer JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="html">
<script type="text/html" data-help-name="html">
<p>Extrahiert unter Verwendung eines CSS-Selektors Elemente aus einem HTML-Dokument, das sich in <code>msg.payload</code> befindet.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="json">
<script type="text/html" data-help-name="json">
<p>Konvertiert zwischen einem JSON-String und seiner JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="xml">
<script type="text/html" data-help-name="xml">
<p>Konvertiert zwischen einem XML-String und seiner JavaScript-Objektdarstellung - in beiden Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="yaml">
<script type="text/html" data-help-name="yaml">
<p>Konvertiert zwischen einer YAML-formatierten String und ihrer JavaScript-Objektdarstellung in beide Richtungen.</p>
<h3>Eingaben</h3>
<dl class="message-properties">

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="split">
<script type="text/html" data-help-name="split">
<p>Teilt eine Nachricht in eine Folge von Nachrichten auf.</p>
<h3>Eingaben</h3>
@@ -62,7 +62,7 @@
Das bedeutet, dass er nicht mit dem <b>join</b> Node im Automatikmodus verwendet werden kann.</p>
</script>
<script type="text/x-red" data-help-name="join">
<script type="text/html" data-help-name="join">
<p>Verbindet Sequenzen von Nachrichten zu einer einzigen Nachricht.</p>
<p>Es sind drei Modi verfügbar:</p>
<dl>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="sort">
<script type="text/html" data-help-name="sort">
<p>Eine Funktion, die die Nachrichteneigenschaft oder eine Folge von Nachrichten sortiert.</p>
<p>Wenn der Node konfiguriert ist, um die Nachrichteneigenschaft zu sortieren,
sortiert er Array-Daten, auf die von der angegebenen Nachrichteneigenschaft verwiesen wird.</p>

View File

@@ -14,7 +14,7 @@
limitations under the License.
-->
<script type="text/x-red" data-help-name="batch">
<script type="text/html" data-help-name="batch">
<p>Erstellt Sequenzen von Nachrichten nach verschiedenen Regeln.</p>
<h3>Details</h3>
<p>Es gibt drei Modi für die Erstellung von Nachrichtensequenzen:</p>

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