mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
62 Commits
0.20.0-bet
...
0.20.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f309a9d537 | ||
|
|
a786b37cb9 | ||
|
|
6a519a30a2 | ||
|
|
81ae552e69 | ||
|
|
0ec04a3624 | ||
|
|
81d5b47fce | ||
|
|
ed31a0cf15 | ||
|
|
201d1926bc | ||
|
|
9ee6655bfa | ||
|
|
c4beab6b0d | ||
|
|
34b6643913 | ||
|
|
98e391b867 | ||
|
|
19eb8e9a6d | ||
|
|
43b7aa40c3 | ||
|
|
747af44fc1 | ||
|
|
d5ef428edd | ||
|
|
5fa4d227b8 | ||
|
|
cc7e3b0c26 | ||
|
|
473a2ae275 | ||
|
|
7f5d47f39d | ||
|
|
6031f146aa | ||
|
|
020a469f3b | ||
|
|
091de3aa66 | ||
|
|
b837f7608c | ||
|
|
afe9367bac | ||
|
|
9bd9023cb6 | ||
|
|
8502cf8498 | ||
|
|
33dade0584 | ||
|
|
84cc2ad0fa | ||
|
|
dc2d3bc7c0 | ||
|
|
64df557423 | ||
|
|
715cc77e76 | ||
|
|
b80d1af3d7 | ||
|
|
f05f534fd2 | ||
|
|
c0837ead0e | ||
|
|
a1f135bd66 | ||
|
|
978f4ecc58 | ||
|
|
46a8d96997 | ||
|
|
c283224000 | ||
|
|
a6ef755139 | ||
|
|
29a257d17a | ||
|
|
368b76a183 | ||
|
|
8bb861124d | ||
|
|
2f884ec778 | ||
|
|
8c561e92c8 | ||
|
|
633b9180d7 | ||
|
|
0e2d0e1b6f | ||
|
|
ea4d65ceee | ||
|
|
d47ac84d2e | ||
|
|
a97759aa35 | ||
|
|
3fcfd4abdd | ||
|
|
6d771da9a9 | ||
|
|
6201247875 | ||
|
|
8c367bcc53 | ||
|
|
8198132ca7 | ||
|
|
cf3b4e9e63 | ||
|
|
987dbf8a92 | ||
|
|
7b80ae42e1 | ||
|
|
3c4f4d27d6 | ||
|
|
06a1f30350 | ||
|
|
2f93bb969b | ||
|
|
e094ea3d2a |
43
CHANGELOG.md
43
CHANGELOG.md
@@ -1,3 +1,46 @@
|
||||
#### 0.20.0-beta.3: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Update palette manager view properly when module updated
|
||||
- Add TreeList common widget
|
||||
- Fix visual jump when opening Comment editor on Safari Part of #2008
|
||||
- Fix vertical align of markdown editor in Safari Fixes #2008
|
||||
- Avoid marking node as changed if label state is default Fixes #2009
|
||||
- Highlight port on node hover while joining
|
||||
- Support drag-wiring of link nodes
|
||||
- Allow TypeSearch to include a filter option
|
||||
- Improve diff colouring
|
||||
- Allow sections to toggle in 2-element stack
|
||||
- Add support for ${} env var syntax when skipping validation Closes #1980
|
||||
- i18 support for markdown editor tooltip
|
||||
- Add RED.editor.registerTypeEditor for custom type editors
|
||||
- Tidy up markdown toolbar handling across all editors
|
||||
- Added validation while export into library
|
||||
- Reuse notification boxes rather than stack multiple of the same type
|
||||
- Make ssh key dialog accessible when opened from new proj dialog
|
||||
|
||||
Runtime
|
||||
|
||||
- Bump JSONata to 1.6.4 Fixes #2023
|
||||
- Add audit logging to admin api
|
||||
- Fix failure of RED.require #2010
|
||||
- Allow oauth strategy callback method to be customised Closes #1998
|
||||
- Ensure fs context cache is flushed on close Fixes #2001
|
||||
- Fix library Buffer( to Buffer.alloc( for node 10
|
||||
- Catch file-not-found on startup when non-existant flow file specified
|
||||
- Actively expire login sesssions and notify user
|
||||
- Add quotation marks for basic auth challenge #1976
|
||||
|
||||
Nodes
|
||||
|
||||
- Change: remove promises to improve performance
|
||||
- Debug: add ability to apply JSONata expression to message
|
||||
- Join: remove promises to improve performance
|
||||
- JSON: delete msg.schema before sending msg to avoid conflicts
|
||||
- Link: update UI to use common TreeList widget
|
||||
- Switch: remove promises to improve performance
|
||||
|
||||
#### 0.20.0-beta.2: Beta Release
|
||||
|
||||
- Split Node-RED internals into multiple sub-modules
|
||||
|
||||
11
Gruntfile.js
11
Gruntfile.js
@@ -135,6 +135,7 @@ module.exports = function(grunt) {
|
||||
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js",
|
||||
@@ -445,7 +446,17 @@ module.exports = function(grunt) {
|
||||
destination: 'docs',
|
||||
configure: './jsdoc.json'
|
||||
}
|
||||
},
|
||||
editor: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js'
|
||||
],
|
||||
options: {
|
||||
destination: 'packages/node_modules/@node-red/editor-client/docs',
|
||||
configure: './jsdoc.json'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
jsdoc2md: {
|
||||
runtimeAPI: {
|
||||
|
||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"description": "A visual tool for wiring the Internet of Things",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "6.6.1",
|
||||
"ajv": "6.6.2",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.18.3",
|
||||
@@ -33,7 +33,7 @@
|
||||
"cookie": "0.3.1",
|
||||
"cookie-parser": "1.4.3",
|
||||
"cors": "2.8.5",
|
||||
"cron": "1.5.1",
|
||||
"cron": "1.6.0",
|
||||
"denque": "1.4.0",
|
||||
"express": "4.16.4",
|
||||
"express-session": "1.15.6",
|
||||
@@ -41,11 +41,11 @@
|
||||
"fs.notify": "0.0.4",
|
||||
"hash-sum": "1.0.2",
|
||||
"https-proxy-agent": "2.2.1",
|
||||
"i18next": "12.1.0",
|
||||
"i18next": "13.1.0",
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.12.0",
|
||||
"js-yaml": "3.12.1",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.5.4",
|
||||
"jsonata": "1.6.3",
|
||||
"media-typer": "1.0.1",
|
||||
"memorystore": "1.6.0",
|
||||
"mime": "2.4.0",
|
||||
@@ -70,14 +70,14 @@
|
||||
"sentiment": "2.1.0",
|
||||
"uglify-js": "3.4.9",
|
||||
"when": "3.7.8",
|
||||
"ws": "1.1.5",
|
||||
"ws": "6.1.2",
|
||||
"xml2js": "0.4.19"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bcrypt": "~2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chromedriver": "2.43.1",
|
||||
"chromedriver": "2.45.0",
|
||||
"grunt": "~1.0.3",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-cli": "~1.3.2",
|
||||
|
||||
@@ -182,7 +182,12 @@ function genericStrategy(adminApp,strategy) {
|
||||
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
|
||||
completeGenerateStrategyAuth
|
||||
);
|
||||
adminApp.get('/auth/strategy/callback',
|
||||
|
||||
var callbackMethodFunc = adminApp.get;
|
||||
if (/^post$/i.test(options.callbackMethod)) {
|
||||
callbackMethodFunc = adminApp.post;
|
||||
}
|
||||
callbackMethodFunc('/auth/strategy/callback',
|
||||
passport.authenticate(strategy.name, {session:false, failureRedirect: settings.httpAdminRoot }),
|
||||
completeGenerateStrategyAuth
|
||||
);
|
||||
|
||||
@@ -25,27 +25,39 @@ function generateToken(length) {
|
||||
|
||||
|
||||
var storage;
|
||||
|
||||
var sessionExpiryTime
|
||||
|
||||
var sessions = {};
|
||||
|
||||
var loadedSessions = null;
|
||||
|
||||
var apiAccessTokens;
|
||||
var sessionExpiryListeners = [];
|
||||
var expiryTimeout;
|
||||
|
||||
function expireSessions() {
|
||||
if (expiryTimeout) {
|
||||
clearTimeout(expiryTimeout);
|
||||
expiryTimeout = null;
|
||||
}
|
||||
var nextExpiry = Number.MAX_SAFE_INTEGER;
|
||||
var now = Date.now();
|
||||
var modified = false;
|
||||
for (var t in sessions) {
|
||||
if (sessions.hasOwnProperty(t)) {
|
||||
var session = sessions[t];
|
||||
if (!session.hasOwnProperty("expires") || session.expires < now) {
|
||||
sessionExpiryListeners.forEach(listener => { listener(session) })
|
||||
delete sessions[t];
|
||||
modified = true;
|
||||
} else {
|
||||
if (session.expires < nextExpiry) {
|
||||
nextExpiry = session.expires;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextExpiry < Number.MAX_SAFE_INTEGER) {
|
||||
// Allow 5 seconds grace
|
||||
expiryTimeout = setTimeout(expireSessions,(nextExpiry - Date.now()) + 5000)
|
||||
}
|
||||
if (modified) {
|
||||
return storage.saveSessions(sessions);
|
||||
} else {
|
||||
@@ -65,6 +77,9 @@ function loadSessions() {
|
||||
module.exports = {
|
||||
init: function(adminAuthSettings, _storage) {
|
||||
storage = _storage;
|
||||
|
||||
sessionExpiryListeners = [];
|
||||
|
||||
sessionExpiryTime = adminAuthSettings.sessionExpiryTime || 604800; // 1 week in seconds
|
||||
// At this point, storage will not have been initialised, so defer loading
|
||||
// the sessions until there's a request for them.
|
||||
@@ -112,6 +127,11 @@ module.exports = {
|
||||
expires: accessTokenExpiresAt
|
||||
};
|
||||
sessions[accessToken] = session;
|
||||
|
||||
if (!expiryTimeout) {
|
||||
expiryTimeout = setTimeout(expireSessions,(accessTokenExpiresAt - Date.now()) + 5000)
|
||||
}
|
||||
|
||||
return storage.saveSessions(sessions).then(function() {
|
||||
return {
|
||||
accessToken: accessToken,
|
||||
@@ -125,5 +145,8 @@ module.exports = {
|
||||
delete sessions[token];
|
||||
return storage.saveSessions(sessions);
|
||||
});
|
||||
},
|
||||
onSessionExpiry: function(callback) {
|
||||
sessionExpiryListeners.push(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
**/
|
||||
|
||||
var ws = require("ws");
|
||||
var url = require("url");
|
||||
|
||||
var log = require("@node-red/util").log; // TODO: separate module
|
||||
var Tokens;
|
||||
@@ -40,11 +41,19 @@ function init(_server,_settings,_runtimeAPI) {
|
||||
settings = _settings;
|
||||
runtimeAPI = _runtimeAPI;
|
||||
Tokens = require("../auth/tokens");
|
||||
Tokens.onSessionExpiry(handleSessionExpiry);
|
||||
Users = require("../auth/users");
|
||||
Permissions = require("../auth/permissions");
|
||||
|
||||
}
|
||||
|
||||
function handleSessionExpiry(session) {
|
||||
activeConnections.forEach(connection => {
|
||||
if (connection.token === session.accessToken) {
|
||||
connection.ws.send(JSON.stringify({auth:"fail"}));
|
||||
connection.ws.close();
|
||||
}
|
||||
})
|
||||
}
|
||||
function generateSession(length) {
|
||||
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
|
||||
var token = [];
|
||||
@@ -88,7 +97,7 @@ function CommsConnection(ws) {
|
||||
// handleRemoteSubscription(ws,msg.subscribe);
|
||||
}
|
||||
} else {
|
||||
var completeConnection = function(userScope,sendAck) {
|
||||
var completeConnection = function(userScope,session,sendAck) {
|
||||
try {
|
||||
if (!userScope || !Permissions.hasPermission(userScope,"status.read")) {
|
||||
ws.send(JSON.stringify({auth:"fail"}));
|
||||
@@ -96,6 +105,7 @@ function CommsConnection(ws) {
|
||||
} else {
|
||||
pendingAuth = false;
|
||||
addActiveConnection(self);
|
||||
self.token = msg.auth;
|
||||
if (sendAck) {
|
||||
ws.send(JSON.stringify({auth:"ok"}));
|
||||
}
|
||||
@@ -113,29 +123,29 @@ function CommsConnection(ws) {
|
||||
if (user) {
|
||||
self.user = user;
|
||||
log.audit({event: "comms.auth",user:self.user});
|
||||
completeConnection(client.scope,true);
|
||||
completeConnection(client.scope,msg.auth,true);
|
||||
} else {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
completeConnection(null,false);
|
||||
completeConnection(null,null,false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
completeConnection(null,false);
|
||||
completeConnection(null,null,false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (anonymousUser) {
|
||||
log.audit({event: "comms.auth",user:anonymousUser});
|
||||
self.user = anonymousUser;
|
||||
completeConnection(anonymousUser.permissions,false);
|
||||
completeConnection(anonymousUser.permissions,null,false);
|
||||
//TODO: duplicated code - pull non-auth message handling out
|
||||
if (msg.subscribe) {
|
||||
self.subscribe(msg.subscribe);
|
||||
}
|
||||
} else {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
completeConnection(null,false);
|
||||
completeConnection(null,null,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,27 +188,27 @@ function start() {
|
||||
Users.default().then(function(_anonymousUser) {
|
||||
anonymousUser = _anonymousUser;
|
||||
var webSocketKeepAliveTime = settings.webSocketKeepAliveTime || 15000;
|
||||
var path = settings.httpAdminRoot || "/";
|
||||
path = (path.slice(0,1) != "/" ? "/":"") + path + (path.slice(-1) == "/" ? "":"/") + "comms";
|
||||
wsServer = new ws.Server({
|
||||
server:server,
|
||||
path:path,
|
||||
// Disable the deflate option due to this issue
|
||||
// https://github.com/websockets/ws/pull/632
|
||||
// that is fixed in the 1.x release of the ws module
|
||||
// that we cannot currently pickup as it drops node 0.10 support
|
||||
//perMessageDeflate: false
|
||||
});
|
||||
|
||||
var commsPath = settings.httpAdminRoot || "/";
|
||||
commsPath = (commsPath.slice(0,1) != "/" ? "/":"") + commsPath + (commsPath.slice(-1) == "/" ? "":"/") + "comms";
|
||||
wsServer = new ws.Server({ noServer: true });
|
||||
wsServer.on('connection',function(ws) {
|
||||
var commsConnection = new CommsConnection(ws);
|
||||
});
|
||||
|
||||
|
||||
wsServer.on('error', function(err) {
|
||||
log.warn(log._("comms.error-server",{message:err.toString()}));
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const pathname = url.parse(request.url).pathname;
|
||||
if (pathname === commsPath) {
|
||||
wsServer.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wsServer.emit('connection', ws, request);
|
||||
});
|
||||
}
|
||||
// Don't destroy the socket as other listeners may want to handle the
|
||||
// event.
|
||||
});
|
||||
|
||||
lastSentTime = Date.now();
|
||||
|
||||
heartbeatTimer = setInterval(function() {
|
||||
|
||||
@@ -21,6 +21,8 @@ var i18n = require("@node-red/util").i18n; // TODO: separate module
|
||||
|
||||
module.exports = {
|
||||
errorHandler: function(err,req,res,next) {
|
||||
//TODO: why this when rejectHandler also?!
|
||||
|
||||
if (err.message === "request entity too large") {
|
||||
log.error(err);
|
||||
} else {
|
||||
@@ -39,7 +41,9 @@ module.exports = {
|
||||
return lang;
|
||||
},
|
||||
rejectHandler: function(req,res,err) {
|
||||
res.status(err.status||500).json({
|
||||
//TODO: why this when errorHandler also?!
|
||||
log.audit({event: "api.error",error:err.code||"unexpected_error",message:err.message||err.toString()},req);
|
||||
res.status(err.status||400).json({
|
||||
code: err.code||"unexpected_error",
|
||||
message: err.message||err.toString()
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "0.20.0-beta.2",
|
||||
"@node-red/editor-client": "0.20.0-beta.2",
|
||||
"@node-red/util": "0.20.0-beta.3",
|
||||
"@node-red/editor-client": "0.20.0-beta.3",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.18.3",
|
||||
"clone": "2.1.2",
|
||||
@@ -32,6 +32,6 @@
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"passport": "0.4.0",
|
||||
"when": "3.7.8",
|
||||
"ws": "1.1.5"
|
||||
"ws": "6.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
22
packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
vendored
Normal file → Executable file
22
packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
vendored
Normal file → Executable file
@@ -27,8 +27,7 @@
|
||||
"status": "Status",
|
||||
"enabled": "Enabled",
|
||||
"disabled":"Disabled",
|
||||
"info": "Description",
|
||||
"tip": "Description accepts Markdown and will appear in the Info tab."
|
||||
"info": "Description"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
@@ -119,7 +118,6 @@
|
||||
"project_not_found": "<p>Project '__project__' not found.</p>",
|
||||
"git_merge_conflict": "<p>Automatic merging of changes failed.</p><p>Fix the unmerged conflicts then commit the results.</p>"
|
||||
},
|
||||
|
||||
"error": "<strong>Error</strong>: __message__",
|
||||
"errors": {
|
||||
"lostConnection": "Lost connection to server, reconnecting...",
|
||||
@@ -155,7 +153,6 @@
|
||||
"node_plural": "__count__ nodes",
|
||||
"configNode": "__count__ configuration node",
|
||||
"configNode_plural": "__count__ configuration nodes",
|
||||
"node_plural": "__count__ nodes",
|
||||
"flow": "__count__ flow",
|
||||
"flow_plural": "__count__ flows",
|
||||
"subflow": "__count__ subflow",
|
||||
@@ -279,7 +276,6 @@
|
||||
"deleteSubflow": "delete subflow",
|
||||
"info": "Description",
|
||||
"category": "Category",
|
||||
"format":"markdown format",
|
||||
"errors": {
|
||||
"noNodesSelected": "<strong>Cannot create subflow</strong>: no nodes selected",
|
||||
"multipleInputsToSelection": "<strong>Cannot create subflow</strong>: multiple inputs to selection"
|
||||
@@ -464,7 +460,6 @@
|
||||
"update": "Update"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
@@ -719,7 +714,20 @@
|
||||
"format": "format JSON"
|
||||
},
|
||||
"markdownEditor": {
|
||||
"title": "Markdown editor"
|
||||
"title": "Markdown editor",
|
||||
"format": "Formatted with markdown",
|
||||
"heading1": "Heading 1",
|
||||
"heading2": "Heading 2",
|
||||
"heading3": "Heading 3",
|
||||
"bold": "Bold",
|
||||
"italic": "Italic",
|
||||
"code": "Code",
|
||||
"ordered-list": "Ordered list",
|
||||
"unordered-list": "Unordered list",
|
||||
"quote": "Quote",
|
||||
"link": "Link",
|
||||
"horizontal-rule": "Horizontal rule",
|
||||
"toggle-preview": "Toggle preview"
|
||||
},
|
||||
"bufferEditor": {
|
||||
"title": "Buffer editor",
|
||||
|
||||
14
packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json
vendored
Normal file → Executable file
14
packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json
vendored
Normal file → Executable file
@@ -115,7 +115,6 @@
|
||||
"args": "array",
|
||||
"desc": "Returns the mean value of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
|
||||
},
|
||||
|
||||
"$boolean": {
|
||||
"args": "arg",
|
||||
"desc": "Casts the argument to a Boolean using the following rules:\n\n - `Boolean` : unchanged\n - `string`: empty : `false`\n - `string`: non-empty : `true`\n - `number`: `0` : `false`\n - `number`: non-zero : `true`\n - `null` : `false`\n - `array`: empty : `false`\n - `array`: contains a member that casts to `true` : `true`\n - `array`: all members cast to `false` : `false`\n - `object`: empty : `false`\n - `object`: non-empty : `true`\n - `function` : `false`"
|
||||
@@ -219,5 +218,18 @@
|
||||
"$env": {
|
||||
"args": "arg",
|
||||
"desc": "Returns the value of an environment variable.\n\nThis is a Node-RED defined function."
|
||||
},
|
||||
"$eval": {
|
||||
"args": "expr [, context]",
|
||||
"desc": "Parses and evaluates the string `expr` which contains literal JSON or a JSONata expression using the current context as the context for evaluation."
|
||||
},
|
||||
"$formatInteger": {
|
||||
"args": "number, picture",
|
||||
"desc": "Casts the `number` to a string and formats it to an integer representation as specified by the `picture` string. The picture string parameter defines how the number is formatted and has the same syntax as `fn:format-integer` from the XPath F&O 3.1 specification."
|
||||
},
|
||||
"$parseInteger": {
|
||||
"args": "string, picture",
|
||||
"desc": "Parses the contents of the `string` parameter to an integer (as a JSON number) using the format specified by the `picture` string. The `picture` string parameter has the same format as `$formatInteger`."
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
20
packages/node_modules/@node-red/editor-client/locales/ja/editor.json
vendored
Normal file → Executable file
20
packages/node_modules/@node-red/editor-client/locales/ja/editor.json
vendored
Normal file → Executable file
@@ -27,8 +27,7 @@
|
||||
"status": "状態",
|
||||
"enabled": "有効",
|
||||
"disabled": "無効",
|
||||
"info": "詳細",
|
||||
"tip": "マークダウン形式で記述した「詳細」は「情報タブ」に表示されます。"
|
||||
"info": "詳細"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
@@ -154,7 +153,6 @@
|
||||
"node_plural": "__count__ 個のノード",
|
||||
"configNode": "__count__ 個の設定ノード",
|
||||
"configNode_plural": "__count__ 個の設定ノード",
|
||||
"node_plural": "__count__ 個のノード",
|
||||
"flow": "__count__ 個のフロー",
|
||||
"flow_plural": "__count__ 個のフロー",
|
||||
"subflow": "__count__ 個のサブフロー",
|
||||
@@ -278,7 +276,6 @@
|
||||
"deleteSubflow": "サブフローを削除",
|
||||
"info": "詳細",
|
||||
"category": "カテゴリ",
|
||||
"format": "マークダウン形式",
|
||||
"errors": {
|
||||
"noNodesSelected": "<strong>サブフローを作成できません</strong>: ノードが選択されていません",
|
||||
"multipleInputsToSelection": "<strong>サブフローを作成できません</strong>: 複数の入力が選択されています"
|
||||
@@ -717,7 +714,20 @@
|
||||
"format": "JSONフォーマット"
|
||||
},
|
||||
"markdownEditor": {
|
||||
"title": "マークダウンエディタ"
|
||||
"title": "マークダウンエディタ",
|
||||
"format": "マークダウン形式で記述",
|
||||
"heading1": "見出しレベル1",
|
||||
"heading2": "見出しレベル2",
|
||||
"heading3": "見出しレベル3",
|
||||
"bold": "太字",
|
||||
"italic": "斜体",
|
||||
"code": "コード",
|
||||
"ordered-list": "箇条書き(番号付き)",
|
||||
"unordered-list": "箇条書き",
|
||||
"quote": "引用",
|
||||
"link": "リンク",
|
||||
"horizontal-rule": "区切り線",
|
||||
"toggle-preview": "プレビュー表示切替え"
|
||||
},
|
||||
"bufferEditor": {
|
||||
"title": "バッファエディタ",
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
"status": "状态",
|
||||
"enabled": "有效",
|
||||
"disabled": "无效",
|
||||
"info": "详细描述",
|
||||
"tip": "详细描述支持Markdown轻量级标记语言,并将出现在信息标签中。"
|
||||
"info": "详细描述"
|
||||
},
|
||||
"menu": {
|
||||
"label": {
|
||||
@@ -191,7 +190,6 @@
|
||||
"output": "输出:",
|
||||
"deleteSubflow": "删除子流程",
|
||||
"info": "详细描述",
|
||||
"format": "标记格式",
|
||||
"errors": {
|
||||
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
|
||||
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -75,29 +75,39 @@ RED.comms = (function() {
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var message = JSON.parse(event.data);
|
||||
for (var m = 0; m < message.length; m++) {
|
||||
var msg = message[m];
|
||||
if (pendingAuth && msg.auth) {
|
||||
if (msg.auth === "ok") {
|
||||
if (message.auth) {
|
||||
if (pendingAuth) {
|
||||
if (message.auth === "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
} else if (msg.auth === "fail") {
|
||||
} else if (message.auth === "fail") {
|
||||
// anything else is an error...
|
||||
active = false;
|
||||
RED.user.login({updateMenu:true},function() {
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
} else if (message.auth === "fail") {
|
||||
// Our current session has expired
|
||||
active = false;
|
||||
RED.user.login({updateMenu:true},function() {
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
else if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
} else {
|
||||
// Otherwise, 'message' is an array of actual comms messages
|
||||
for (var m = 0; m < message.length; m++) {
|
||||
var msg = message[m];
|
||||
if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,10 +142,10 @@ RED.comms = (function() {
|
||||
connectWS();
|
||||
} else {
|
||||
var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>';
|
||||
errornotification.update(msg);
|
||||
errornotification.update(msg,{silent:true});
|
||||
$(errornotification).find("a").click(function(e) {
|
||||
e.preventDefault();
|
||||
errornotification.update(RED._("notification.errors.lostConnection"));
|
||||
errornotification.update(RED._("notification.errors.lostConnection"),{silent:true});
|
||||
clearInterval(connectCountdownTimer);
|
||||
connectWS();
|
||||
})
|
||||
|
||||
@@ -296,6 +296,7 @@ RED.history = (function() {
|
||||
});
|
||||
|
||||
RED.nodes.dirty(ev.dirty);
|
||||
RED.view.select(null);
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
|
||||
@@ -391,7 +391,7 @@ var RED = (function() {
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||
}
|
||||
} else if (topic == "node/upgraded") {
|
||||
} else if (topic == "notification/node/upgraded") {
|
||||
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
||||
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
||||
}
|
||||
|
||||
@@ -1242,7 +1242,7 @@ RED.text.format = (function() {
|
||||
element.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var range = selection.getRangeAt(0);
|
||||
var tempRange = range.cloneRange(), startNode, startOffset;
|
||||
startNode = range.startContainer;
|
||||
@@ -1304,7 +1304,7 @@ RED.text.format = (function() {
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
/*!
|
||||
* Returns the HTML representation of a given structured text
|
||||
* @param text - the structured text
|
||||
* @param type - could be one of filepath, url, email
|
||||
@@ -1315,7 +1315,7 @@ RED.text.format = (function() {
|
||||
getHtml: function (text, type, args, isRtl, locale) {
|
||||
return getHandler(type).format(text, args, isRtl, true, locale);
|
||||
},
|
||||
/**
|
||||
/*!
|
||||
* Handle Structured text correct display for a given HTML element.
|
||||
* @param element - the element : should be of type div contenteditable=true
|
||||
* @param type - could be one of filepath, url, email
|
||||
|
||||
@@ -72,7 +72,7 @@ RED.clipboard = (function() {
|
||||
$("#clipboard-export").select();
|
||||
document.execCommand("copy");
|
||||
document.getSelection().removeAllRanges();
|
||||
RED.notify(RED._("clipboard.nodesExported"));
|
||||
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
|
||||
@@ -38,7 +38,7 @@ RED.popover = (function() {
|
||||
var direction = options.direction || "right";
|
||||
var trigger = options.trigger;
|
||||
var content = options.content;
|
||||
var delay = options.delay;
|
||||
var delay = options.delay || { show: 750, hide: 50 };
|
||||
var autoClose = options.autoClose;
|
||||
var width = options.width||"auto";
|
||||
var size = options.size||"default";
|
||||
@@ -172,6 +172,18 @@ RED.popover = (function() {
|
||||
openPopup();
|
||||
}
|
||||
});
|
||||
if (autoClose) {
|
||||
target.on('mouseleave disabled', function(e) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
if (active) {
|
||||
active = false;
|
||||
setTimeout(closePopup,autoClose);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else if (trigger === 'modal') {
|
||||
$(document).on('mousedown.modal-popover-close', function (event) {
|
||||
var target = event.target;
|
||||
|
||||
@@ -66,6 +66,14 @@ RED.stack = (function() {
|
||||
}
|
||||
}
|
||||
entry.expand();
|
||||
} else if (entries.length === 2) {
|
||||
if (entries[0] === entry) {
|
||||
entries[0].collapse();
|
||||
entries[1].expand();
|
||||
} else {
|
||||
entries[1].collapse();
|
||||
entries[0].expand();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entry.toggle();
|
||||
|
||||
177
packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js
vendored
Normal file
177
packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* options:
|
||||
* - data : array - initial items to display in tree
|
||||
*
|
||||
* methods:
|
||||
* - data(items) - clears existing items and replaces with new data
|
||||
*
|
||||
* events:
|
||||
* - treelistselect : function(event, item) {}
|
||||
*
|
||||
*
|
||||
* data:
|
||||
* [
|
||||
* {
|
||||
* label: 'Local', // label for the item
|
||||
* icon: 'fa fa-rocket', // (optional) icon for the item
|
||||
* selected: true/false, // (optional) if present, display checkbox accordingly
|
||||
* children: [] | function(done) // (optional) an array of child items, or a function
|
||||
* // that will call the `done` callback with an array
|
||||
* // of child items
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
*
|
||||
*
|
||||
* var treeList = $("<div>").css({width: "100%", height: "100%"}).treeList({data:[...]})
|
||||
* treeList.on('treelistselect', function(e,item) { console.log(item)})
|
||||
* treeList.treeList('data',[ ... ] )
|
||||
*
|
||||
*/
|
||||
|
||||
$.widget( "nodered.treeList", {
|
||||
_create: function() {
|
||||
var that = this;
|
||||
|
||||
this.element.addClass('red-ui-treeList');
|
||||
var wrapper = $('<div>',{class:'red-ui-treeList-container'}).appendTo(this.element);
|
||||
|
||||
this._data = [];
|
||||
|
||||
this._topList = $('<ol>').css({
|
||||
position:'absolute',
|
||||
top: 0,
|
||||
left:0,
|
||||
right:0,
|
||||
bottom:0
|
||||
}).appendTo(wrapper).editableList({
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
height: '100%',
|
||||
addItem: function(container,i,item) {
|
||||
that._addSubtree(container,item,0);
|
||||
}
|
||||
});
|
||||
if (this.options.data) {
|
||||
this.data(this.options.data);
|
||||
}
|
||||
},
|
||||
_addChildren: function(container,children,depth) {
|
||||
var that = this;
|
||||
var subtree = $('<ol>').appendTo(container).editableList({
|
||||
addButton: false,
|
||||
scrollOnAdd: false,
|
||||
height: 'auto',
|
||||
addItem: function(container,i,item) {
|
||||
that._addSubtree(container,item,depth+1);
|
||||
}
|
||||
});
|
||||
for (var i=0;i<children.length;i++) {
|
||||
subtree.editableList('addItem',children[i])
|
||||
}
|
||||
},
|
||||
_addSubtree: function(container, item, depth) {
|
||||
var that = this;
|
||||
var labelNodeType = "<label>";
|
||||
if (item.children && item.hasOwnProperty('selected')) {
|
||||
labelNodeType = "<div>";
|
||||
}
|
||||
var label = $(labelNodeType,{tabindex:"0",class:"red-ui-treeList-label"}).appendTo(container);
|
||||
if (item.class) {
|
||||
label.addClass(item.class);
|
||||
}
|
||||
label.css({
|
||||
paddingLeft: (depth*15)+'px'
|
||||
})
|
||||
label.on('mouseover',function(e) { that._trigger('itemmouseover',e,item); })
|
||||
label.on('mouseout',function(e) { that._trigger('itemmouseout',e,item); })
|
||||
|
||||
if (item.children) {
|
||||
$('<span class="red-ui-treeList-icon"><i class="fa fa-angle-right" /></span>').appendTo(label);
|
||||
// $('<span class="red-ui-treeList-icon"><i class="fa fa-folder-o" /></span>').appendTo(label);
|
||||
label.click(function(e) {
|
||||
if (!container.hasClass("built") && typeof item.children === 'function') {
|
||||
container.addClass('built');
|
||||
var childrenAdded = false;
|
||||
var spinner;
|
||||
item.children(function(children) {
|
||||
childrenAdded = true;
|
||||
that._addChildren(container,children,depth);
|
||||
if (spinner) {
|
||||
spinner.remove();
|
||||
}
|
||||
});
|
||||
if (!childrenAdded) {
|
||||
spinner = $('<div class="red-ui-treeList-spinner">').css({
|
||||
"background-position": (35+depth*15)+'px 50%'
|
||||
}).appendTo(container);
|
||||
}
|
||||
|
||||
}
|
||||
container.toggleClass("expanded");
|
||||
})
|
||||
} else {
|
||||
$('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
||||
}
|
||||
if (item.hasOwnProperty('selected')) {
|
||||
var selectWrapper = $('<span class="red-ui-treeList-icon"></span>').appendTo(label);
|
||||
var cb = $('<input type="checkbox">').prop('checked',item.selected).appendTo(selectWrapper);
|
||||
cb.on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
cb.on('change', function(e) {
|
||||
item.selected = this.checked;
|
||||
that._trigger("select",e,item);
|
||||
})
|
||||
} else if (!item.children) {
|
||||
label.click(function(e) {
|
||||
that._trigger("select",e,item)
|
||||
})
|
||||
}
|
||||
if (item.icon) {
|
||||
$('<span class="red-ui-treeList-icon"><i class="'+item.icon+'" /></span>').appendTo(label);
|
||||
}
|
||||
$('<span class="red-ui-treeList-label-text"></span>').html(item.label).appendTo(label);
|
||||
if (item.children) {
|
||||
if (Array.isArray(item.children)) {
|
||||
that._addChildren(container,item.children,depth);
|
||||
}
|
||||
if (item.expanded) {
|
||||
label.click();
|
||||
}
|
||||
}
|
||||
},
|
||||
empty: function() {
|
||||
this._topList.editableList('empty');
|
||||
},
|
||||
data: function(items) {
|
||||
if (items !== undefined) {
|
||||
this._data = items;
|
||||
this._topList.editableList('empty');
|
||||
for (var i=0; i<items.length;i++) {
|
||||
this._topList.editableList('addItem',items[i]);
|
||||
}
|
||||
} else {
|
||||
return this._data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
@@ -340,7 +340,7 @@ RED.deploy = (function() {
|
||||
|
||||
var unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (node.users.length === 0 && (node._def.hasUsers !== false)) {
|
||||
if ((node._def.hasUsers !== false) && (node.users.length === 0)) {
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
/**
|
||||
* @namespace RED.editor
|
||||
*/
|
||||
RED.editor = (function() {
|
||||
|
||||
|
||||
@@ -21,6 +25,8 @@ RED.editor = (function() {
|
||||
var editing_config_node = null;
|
||||
var subflowEditor;
|
||||
|
||||
var customEditTypes = {};
|
||||
|
||||
var editTrayWidthCache = {};
|
||||
|
||||
function getCredentialsURL(nodeType, nodeID) {
|
||||
@@ -124,6 +130,9 @@ RED.editor = (function() {
|
||||
if (/^\$\([a-zA-Z_][a-zA-Z0-9_]*\)$/.test(value)) {
|
||||
return true;
|
||||
}
|
||||
if (/^\$\{[a-zA-Z_][a-zA-Z0-9_]*\}$/.test(value)) {
|
||||
return true;
|
||||
}
|
||||
if ("required" in definition[property] && definition[property].required) {
|
||||
valid = value !== "";
|
||||
}
|
||||
@@ -929,7 +938,7 @@ RED.editor = (function() {
|
||||
function buildDescriptionForm(container,node) {
|
||||
var dialogForm = $('<form class="dialog-form form-horizontal" autocomplete="off"></form>').appendTo(container);
|
||||
var toolbarRow = $('<div></div>').appendTo(dialogForm);
|
||||
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: calc(100% - 36px);"></div>').appendTo(dialogForm);
|
||||
var row = $('<div class="form-row node-text-editor-row" style="position:relative; padding-top: 4px; height: 100%"></div>').appendTo(dialogForm);
|
||||
$('<div style="height: 100%" class="node-text-editor" id="node-info-input-info-editor" ></div>').appendTo(row);
|
||||
var nodeInfoEditor = RED.editor.createEditor({
|
||||
id: "node-info-input-info-editor",
|
||||
@@ -939,26 +948,6 @@ RED.editor = (function() {
|
||||
if (node.info) {
|
||||
nodeInfoEditor.getSession().setValue(node.info, -1);
|
||||
}
|
||||
var toolbar = RED.editor.types._markdown.buildToolbar(toolbarRow,nodeInfoEditor);
|
||||
|
||||
$('<button id="node-info-input-info-expand" class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(toolbar);
|
||||
|
||||
$('#node-info-input-info-expand').click(function(e) {
|
||||
e.preventDefault();
|
||||
var value = nodeInfoEditor.getValue();
|
||||
RED.editor.editMarkdown({
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: nodeInfoEditor.getCursorPosition(),
|
||||
complete: function(v,cursor) {
|
||||
nodeInfoEditor.setValue(v, -1);
|
||||
nodeInfoEditor.gotoLine(cursor.row+1,cursor.column,false);
|
||||
setTimeout(function() {
|
||||
nodeInfoEditor.focus();
|
||||
},300);
|
||||
}
|
||||
})
|
||||
});
|
||||
return nodeInfoEditor;
|
||||
}
|
||||
|
||||
@@ -1220,7 +1209,7 @@ RED.editor = (function() {
|
||||
node.l = false;
|
||||
} else {
|
||||
// A link node - default state is false
|
||||
if (node.hasOwnProperty('l')) {
|
||||
if (node.hasOwnProperty('l') && node.l) {
|
||||
changes.l = node.l
|
||||
changed = true;
|
||||
}
|
||||
@@ -1230,7 +1219,7 @@ RED.editor = (function() {
|
||||
// Checked - show label
|
||||
if (!/^link (in|out)$/.test(node.type)) {
|
||||
// Not a link node - default state is true
|
||||
if (node.hasOwnProperty('l')) {
|
||||
if (node.hasOwnProperty('l') && !node.l) {
|
||||
changes.l = node.l
|
||||
changed = true;
|
||||
}
|
||||
@@ -2157,7 +2146,7 @@ RED.editor = (function() {
|
||||
}
|
||||
|
||||
function showTypeEditor(type, options) {
|
||||
if (RED.editor.types.hasOwnProperty(type)) {
|
||||
if (customEditTypes.hasOwnProperty(type)) {
|
||||
if (editStack.length > 0) {
|
||||
options.parent = editStack[editStack.length-1].id;
|
||||
}
|
||||
@@ -2166,12 +2155,99 @@ RED.editor = (function() {
|
||||
options.onclose = function() {
|
||||
editStack.pop();
|
||||
}
|
||||
RED.editor.types[type].show(options);
|
||||
customEditTypes[type].show(options);
|
||||
} else {
|
||||
console.log("Unknown type editor:",type);
|
||||
}
|
||||
}
|
||||
|
||||
function createEditor(options) {
|
||||
var el = options.element || $("#"+options.id)[0];
|
||||
var toolbarRow = $("<div>").appendTo(el);
|
||||
el = $("<div>").appendTo(el).addClass("node-text-editor-container")[0];
|
||||
var editor = ace.edit(el);
|
||||
editor.setTheme("ace/theme/tomorrow");
|
||||
var session = editor.getSession();
|
||||
session.on("changeAnnotation", function () {
|
||||
var annotations = session.getAnnotations() || [];
|
||||
var i = annotations.length;
|
||||
var len = annotations.length;
|
||||
while (i--) {
|
||||
if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
|
||||
else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
|
||||
}
|
||||
if (len > annotations.length) { session.setAnnotations(annotations); }
|
||||
});
|
||||
if (options.mode) {
|
||||
session.setMode(options.mode);
|
||||
}
|
||||
if (options.foldStyle) {
|
||||
session.setFoldStyle(options.foldStyle);
|
||||
} else {
|
||||
session.setFoldStyle('markbeginend');
|
||||
}
|
||||
if (options.options) {
|
||||
editor.setOptions(options.options);
|
||||
} else {
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion:true,
|
||||
enableSnippets:true,
|
||||
tooltipFollowsMouse: false
|
||||
});
|
||||
}
|
||||
if (options.readOnly) {
|
||||
editor.setOption('readOnly',options.readOnly);
|
||||
editor.container.classList.add("ace_read-only");
|
||||
}
|
||||
if (options.hasOwnProperty('lineNumbers')) {
|
||||
editor.renderer.setOption('showGutter',options.lineNumbers);
|
||||
}
|
||||
editor.$blockScrolling = Infinity;
|
||||
if (options.value) {
|
||||
session.setValue(options.value,-1);
|
||||
}
|
||||
if (options.globals) {
|
||||
setTimeout(function() {
|
||||
if (!!session.$worker) {
|
||||
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
|
||||
}
|
||||
},100);
|
||||
}
|
||||
if (options.mode === 'ace/mode/markdown') {
|
||||
$(el).addClass("node-text-editor-container-toolbar");
|
||||
editor.toolbar = customEditTypes['_markdown'].buildToolbar(toolbarRow,editor);
|
||||
if (options.expandable !== false) {
|
||||
var expandButton = $('<button class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(editor.toolbar);
|
||||
|
||||
expandButton.click(function(e) {
|
||||
e.preventDefault();
|
||||
var value = editor.getValue();
|
||||
RED.editor.editMarkdown({
|
||||
value: value,
|
||||
width: "Infinity",
|
||||
cursor: editor.getCursorPosition(),
|
||||
complete: function(v,cursor) {
|
||||
editor.setValue(v, -1);
|
||||
editor.gotoLine(cursor.row+1,cursor.column,false);
|
||||
setTimeout(function() {
|
||||
editor.focus();
|
||||
},300);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
var helpButton = $('<button class="node-text-editor-help editor-button editor-button-small"><i class="fa fa-question"></i></button>').appendTo($(el).parent());
|
||||
RED.popover.create({
|
||||
target: helpButton,
|
||||
trigger: 'click',
|
||||
size: "small",
|
||||
direction: "left",
|
||||
content: RED._("markdownEditor.format"),
|
||||
autoClose: 50
|
||||
});
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
@@ -2184,14 +2260,7 @@ RED.editor = (function() {
|
||||
$("#node-dialog-cancel").click();
|
||||
$("#node-config-dialog-cancel").click();
|
||||
});
|
||||
|
||||
for (var type in RED.editor.types) {
|
||||
if (RED.editor.types.hasOwnProperty(type)) {
|
||||
RED.editor.types[type].init();
|
||||
}
|
||||
}
|
||||
},
|
||||
types: {},
|
||||
edit: showEditDialog,
|
||||
editConfig: showEditConfigNodeDialog,
|
||||
editSubflow: showEditSubflowDialog,
|
||||
@@ -2204,57 +2273,32 @@ RED.editor = (function() {
|
||||
validateNode: validateNode,
|
||||
updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo
|
||||
|
||||
/**
|
||||
* Show a type editor.
|
||||
* @param {string} type - the type to display
|
||||
* @param {object} options - options for the editor
|
||||
* @function
|
||||
* @memberof RED.editor
|
||||
*/
|
||||
showTypeEditor: showTypeEditor,
|
||||
|
||||
createEditor: function(options) {
|
||||
var editor = ace.edit(options.id||options.element);
|
||||
editor.setTheme("ace/theme/tomorrow");
|
||||
var session = editor.getSession();
|
||||
session.on("changeAnnotation", function () {
|
||||
var annotations = session.getAnnotations() || [];
|
||||
var i = annotations.length;
|
||||
var len = annotations.length;
|
||||
while (i--) {
|
||||
if (/doctype first\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
|
||||
else if (/Unexpected End of file\. Expected/.test(annotations[i].text)) { annotations.splice(i, 1); }
|
||||
}
|
||||
if (len > annotations.length) { session.setAnnotations(annotations); }
|
||||
});
|
||||
if (options.mode) {
|
||||
session.setMode(options.mode);
|
||||
}
|
||||
if (options.foldStyle) {
|
||||
session.setFoldStyle(options.foldStyle);
|
||||
} else {
|
||||
session.setFoldStyle('markbeginend');
|
||||
}
|
||||
if (options.options) {
|
||||
editor.setOptions(options.options);
|
||||
} else {
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion:true,
|
||||
enableSnippets:true,
|
||||
tooltipFollowsMouse: false
|
||||
});
|
||||
}
|
||||
if (options.readOnly) {
|
||||
editor.setOption('readOnly',options.readOnly);
|
||||
editor.container.classList.add("ace_read-only");
|
||||
}
|
||||
if (options.hasOwnProperty('lineNumbers')) {
|
||||
editor.renderer.setOption('showGutter',options.lineNumbers);
|
||||
}
|
||||
editor.$blockScrolling = Infinity;
|
||||
if (options.value) {
|
||||
session.setValue(options.value,-1);
|
||||
}
|
||||
if (options.globals) {
|
||||
setTimeout(function() {
|
||||
if (!!session.$worker) {
|
||||
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
|
||||
}
|
||||
},100);
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
/**
|
||||
* Register a type editor.
|
||||
* @param {string} type - the type name
|
||||
* @param {object} options - the editor definition
|
||||
* @function
|
||||
* @memberof RED.editor
|
||||
*/
|
||||
registerTypeEditor: function(type, definition) {
|
||||
customEditTypes[type] = definition;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a editor ui component
|
||||
* @param {object} options - the editor options
|
||||
* @function
|
||||
* @memberof RED.editor
|
||||
*/
|
||||
createEditor: createEditor
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.editor.types._buffer = (function() {
|
||||
|
||||
(function() {
|
||||
|
||||
var template = '<script type="text/x-red" data-template-name="_buffer"><div id="node-input-buffer-panels"><div id="node-input-buffer-panel-str" class="red-ui-panel"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><span class="node-input-buffer-type"><i class="fa fa-exclamation-circle"></i> <span id="node-input-buffer-type-string" data-i18n="bufferEditor.modeString"></span><span id="node-input-buffer-type-array" data-i18n="bufferEditor.modeArray"></span></span></div><div class="form-row node-text-editor-row"><div class="node-text-editor" id="node-input-buffer-str"></div></div></div><div id="node-input-buffer-panel-bin" class="red-ui-panel"><div class="form-row node-text-editor-row" style="margin-top: 10px"><div class="node-text-editor" id="node-input-buffer-bin"></div></div></div></div></script>';
|
||||
|
||||
@@ -45,10 +44,7 @@ RED.editor.types._buffer = (function() {
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
$(template).appendTo(document.body);
|
||||
},
|
||||
var definition = {
|
||||
show: function(options) {
|
||||
var value = options.value;
|
||||
var onComplete = options.complete;
|
||||
@@ -206,4 +202,7 @@ RED.editor.types._buffer = (function() {
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
}
|
||||
$(template).appendTo(document.body);
|
||||
RED.editor.registerTypeEditor("_buffer", definition);
|
||||
|
||||
})();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.editor.types._expression = (function() {
|
||||
(function() {
|
||||
|
||||
|
||||
var template = '<script type="text/x-red" data-template-name="_expression">'+
|
||||
@@ -46,10 +46,7 @@ RED.editor.types._expression = (function() {
|
||||
'</script>';
|
||||
var expressionTestCache = {};
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
$(template).appendTo(document.body);
|
||||
},
|
||||
var definition = {
|
||||
show: function(options) {
|
||||
var expressionTestCacheId = options.parent||"_";
|
||||
var value = options.value;
|
||||
@@ -349,4 +346,6 @@ RED.editor.types._expression = (function() {
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
}
|
||||
$(template).appendTo(document.body);
|
||||
RED.editor.registerTypeEditor("_expression", definition);
|
||||
})();
|
||||
|
||||
@@ -13,15 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.editor.types._js = (function() {
|
||||
(function() {
|
||||
|
||||
|
||||
var template = '<script type="text/x-red" data-template-name="_js"><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-js"></div></div></script>';
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
$(template).appendTo(document.body);
|
||||
},
|
||||
var definition = {
|
||||
show: function(options) {
|
||||
var value = options.value;
|
||||
var onComplete = options.complete;
|
||||
@@ -99,4 +96,7 @@ RED.editor.types._js = (function() {
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
}
|
||||
$(template).appendTo(document.body);
|
||||
RED.editor.registerTypeEditor("_js", definition);
|
||||
|
||||
})();
|
||||
|
||||
@@ -13,15 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.editor.types._json = (function() {
|
||||
(function() {
|
||||
|
||||
|
||||
var template = '<script type="text/x-red" data-template-name="_json"><div class="form-row" style="margin-bottom: 3px; text-align: right;"><button id="node-input-json-reformat" class="editor-button editor-button-small"><span data-i18n="jsonEditor.format"></span></button></div><div class="form-row node-text-editor-row"><div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div></div></script>';
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
$(template).appendTo(document.body);
|
||||
},
|
||||
var definition = {
|
||||
show: function(options) {
|
||||
var value = options.value;
|
||||
var onComplete = options.complete;
|
||||
@@ -115,4 +112,6 @@ RED.editor.types._json = (function() {
|
||||
RED.tray.show(trayOptions);
|
||||
}
|
||||
}
|
||||
$(template).appendTo(document.body);
|
||||
RED.editor.registerTypeEditor("_json", definition);
|
||||
})();
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
RED.editor.types._markdown = (function() {
|
||||
|
||||
(function() {
|
||||
|
||||
var toolbarTemplate = '<div style="margin-bottom: 5px">'+
|
||||
'<span class="button-group">'+
|
||||
@@ -41,7 +40,7 @@ RED.editor.types._markdown = (function() {
|
||||
'<div id="node-input-markdown-panel-editor" class="red-ui-panel">'+
|
||||
'<div style="height: 100%; margin: auto; max-width: 1000px;">'+
|
||||
'<div id="node-input-markdown-toolbar"></div>'+
|
||||
'<div class="node-text-editor" style="height: calc(100% - 50px)" id="node-input-markdown"></div>'+
|
||||
'<div class="node-text-editor" style="height: 100%" id="node-input-markdown"></div>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'<div class="red-ui-panel">'+
|
||||
@@ -52,24 +51,7 @@ RED.editor.types._markdown = (function() {
|
||||
|
||||
var panels;
|
||||
|
||||
var styleActions = {
|
||||
'h1': { newline: true, before:"# ", tooltip:"Heading 1"},
|
||||
'h2': { newline: true, before:"## ", tooltip:"Heading 2"},
|
||||
'h3': { newline: true, before:"### ", tooltip:"Heading 3"},
|
||||
'b': { before:"**", after: "**", tooltip: "Bold" },
|
||||
'i': { before:"_", after: "_", tooltip: "Italic" },
|
||||
'code': { before:"`", after: "`", tooltip: "Code" },
|
||||
'ol': { before:" * ", newline: true, tooltip: "Ordered list" },
|
||||
'ul': { before:" - ", newline: true, tooltip: "Unordered list" },
|
||||
'bq': { before:"> ", newline: true, tooltip: "Quote" },
|
||||
'link': { before:"[", after: "]()", tooltip: "Link"},
|
||||
'hr': { before:"\n---\n\n", tooltip: "Horizontal rule" }
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
$(template).appendTo(document.body);
|
||||
},
|
||||
var definition = {
|
||||
show: function(options) {
|
||||
var value = options.value;
|
||||
var onComplete = options.complete;
|
||||
@@ -112,7 +94,8 @@ RED.editor.types._markdown = (function() {
|
||||
expressionEditor = RED.editor.createEditor({
|
||||
id: 'node-input-markdown',
|
||||
value: value,
|
||||
mode:"ace/mode/markdown"
|
||||
mode:"ace/mode/markdown",
|
||||
expandable: false
|
||||
});
|
||||
var changeTimer;
|
||||
expressionEditor.getSession().on("change", function() {
|
||||
@@ -138,11 +121,10 @@ RED.editor.types._markdown = (function() {
|
||||
}
|
||||
});
|
||||
panels.ratio(1);
|
||||
var toolbar = RED.editor.types._markdown.buildToolbar($("#node-input-markdown-toolbar"), expressionEditor);
|
||||
|
||||
$('<span class="button-group" style="float:right">'+
|
||||
'<button id="node-btn-markdown-preview" class="editor-button toggle single"><i class="fa fa-eye"></i></button>'+
|
||||
'</span>').appendTo(toolbar);
|
||||
'</span>').appendTo(expressionEditor.toolbar);
|
||||
|
||||
$("#node-btn-markdown-preview").click(function(e) {
|
||||
e.preventDefault();
|
||||
@@ -154,7 +136,7 @@ RED.editor.types._markdown = (function() {
|
||||
panels.ratio(0.5);
|
||||
}
|
||||
});
|
||||
RED.popover.tooltip($("#node-btn-markdown-preview"),"Toggle preview");
|
||||
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
|
||||
|
||||
if (options.cursor) {
|
||||
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
|
||||
@@ -174,6 +156,19 @@ RED.editor.types._markdown = (function() {
|
||||
},
|
||||
|
||||
buildToolbar: function(container, editor) {
|
||||
var styleActions = {
|
||||
'h1': { newline: true, before:"# ", tooltip:RED._("markdownEditor.heading1")},
|
||||
'h2': { newline: true, before:"## ", tooltip:RED._("markdownEditor.heading2")},
|
||||
'h3': { newline: true, before:"### ", tooltip:RED._("markdownEditor.heading3")},
|
||||
'b': { before:"**", after: "**", tooltip: RED._("markdownEditor.bold")},
|
||||
'i': { before:"_", after: "_", tooltip: RED._("markdownEditor.italic")},
|
||||
'code': { before:"`", after: "`", tooltip: RED._("markdownEditor.code")},
|
||||
'ol': { before:" * ", newline: true, tooltip: RED._("markdownEditor.ordered-list")},
|
||||
'ul': { before:" - ", newline: true, tooltip: RED._("markdownEditor.unordered-list")},
|
||||
'bq': { before:"> ", newline: true, tooltip: RED._("markdownEditor.quote")},
|
||||
'link': { before:"[", after: "]()", tooltip: RED._("markdownEditor.link")},
|
||||
'hr': { before:"\n---\n\n", tooltip: RED._("markdownEditor.horizontal-rule")}
|
||||
}
|
||||
var toolbar = $(toolbarTemplate).appendTo(container);
|
||||
toolbar.find('button[data-style]').each(function(el) {
|
||||
var style = styleActions[$(this).data('style')];
|
||||
@@ -212,4 +207,6 @@ RED.editor.types._markdown = (function() {
|
||||
return toolbar;
|
||||
}
|
||||
}
|
||||
$(template).appendTo(document.body);
|
||||
RED.editor.registerTypeEditor("_markdown", definition);
|
||||
})();
|
||||
|
||||
@@ -458,7 +458,10 @@ RED.library = (function() {
|
||||
click: function() {
|
||||
//TODO: move this to RED.library
|
||||
var flowName = $("#node-input-library-filename").val();
|
||||
if (!/^\s*$/.test(flowName)) {
|
||||
flowName = flowName.trim();
|
||||
if(flowName === "" || flowName.endsWith("/")) {
|
||||
RED.notify(RED._("library.invalidFilename"),"warning");
|
||||
} else {
|
||||
$.ajax({
|
||||
url:'library/flows/'+flowName,
|
||||
type: "POST",
|
||||
|
||||
@@ -56,6 +56,11 @@ RED.notifications = (function() {
|
||||
type = options.type;
|
||||
}
|
||||
|
||||
if (options.id && persistentNotifications.hasOwnProperty(options.id)) {
|
||||
persistentNotifications[options.id].update(msg,options);
|
||||
return persistentNotifications[options.id];
|
||||
}
|
||||
|
||||
if (options.modal) {
|
||||
$("#full-shade").show();
|
||||
}
|
||||
@@ -181,7 +186,9 @@ RED.notifications = (function() {
|
||||
if (typeof options === 'number') {
|
||||
timeout = options;
|
||||
} else if (options !== undefined) {
|
||||
timeout = options.timeout;
|
||||
if (!options.fixed) {
|
||||
timeout = options.timeout || 5000;
|
||||
}
|
||||
if (options.buttons) {
|
||||
var buttonSet = $('<div style="margin-top: 20px;" class="ui-dialog-buttonset"></div>').appendTo(nn)
|
||||
options.buttons.forEach(function(buttonDef) {
|
||||
@@ -203,6 +210,11 @@ RED.notifications = (function() {
|
||||
}
|
||||
if (nn.hidden) {
|
||||
nn.showNotification();
|
||||
} else if (!options || !options.silent){
|
||||
$(nn).addClass("notification-shake-horizontal");
|
||||
setTimeout(function() {
|
||||
$(nn).removeClass("notification-shake-horizontal");
|
||||
},300);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -221,7 +233,9 @@ RED.notifications = (function() {
|
||||
currentNotifications.push(n);
|
||||
if (options.id) {
|
||||
persistentNotifications[options.id] = n;
|
||||
notificationButtonWrapper.show();
|
||||
if (options.fixed) {
|
||||
notificationButtonWrapper.show();
|
||||
}
|
||||
}
|
||||
c+=1;
|
||||
return n;
|
||||
|
||||
@@ -289,7 +289,7 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
if (moduleInfo.pending_version) {
|
||||
nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
|
||||
nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').show();
|
||||
nodeEntry.updateButton.text(RED._('palette.editor.updated')).addClass('disabled').css('display', 'inline-block');
|
||||
} else if (loadedIndex.hasOwnProperty(module)) {
|
||||
if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
|
||||
nodeEntry.updateButton.show();
|
||||
|
||||
@@ -21,7 +21,7 @@ RED.projects = (function() {
|
||||
var activeProject;
|
||||
function reportUnexpectedError(error) {
|
||||
var notification;
|
||||
if (error.error === 'git_missing_user') {
|
||||
if (error.code === 'git_missing_user') {
|
||||
notification = RED.notify("<p>"+RED._("projects.errors.no-username-email")+"</p>",{
|
||||
fixed: true,
|
||||
type:'error',
|
||||
@@ -43,7 +43,7 @@ RED.projects = (function() {
|
||||
})
|
||||
} else {
|
||||
console.log(error);
|
||||
notification = RED.notify("<p>"+RED._("projects.errors.unexpected")+":</p><p>"+error.message+"</p><small>"+RED._("projects.errors.code")+": "+error.error+"</small>",{
|
||||
notification = RED.notify("<p>"+RED._("projects.errors.unexpected")+":</p><p>"+error.message+"</p><small>"+RED._("projects.errors.code")+": "+error.code+"</small>",{
|
||||
fixed: true,
|
||||
modal: true,
|
||||
type: 'error',
|
||||
@@ -546,7 +546,7 @@ RED.projects = (function() {
|
||||
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
|
||||
$('<button class="editor-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).click(function(e) {
|
||||
e.preventDefault();
|
||||
$('#projects-dialog-cancel').click();
|
||||
dialog.dialog( "close" );
|
||||
RED.userSettings.show('gitconfig');
|
||||
setTimeout(function() {
|
||||
$("#user-settings-gitconfig-add-key").click();
|
||||
@@ -1507,7 +1507,7 @@ RED.projects = (function() {
|
||||
return switchProject(selectedProject.name,function(err,data) {
|
||||
dialog.dialog( "close" );
|
||||
if (err) {
|
||||
if (err.error !== 'credentials_load_failed') {
|
||||
if (err.code !== 'credentials_load_failed') {
|
||||
console.log(RED._("projects.create.unexpected_error"),err)
|
||||
}
|
||||
}
|
||||
@@ -1893,7 +1893,6 @@ RED.projects = (function() {
|
||||
function sendRequest(options,body) {
|
||||
// dialogBody.hide();
|
||||
// console.log(options.url,body);
|
||||
|
||||
if (options.requireCleanWorkspace && RED.nodes.dirty()) {
|
||||
var thenCallback;
|
||||
var alwaysCallback;
|
||||
@@ -1952,7 +1951,7 @@ RED.projects = (function() {
|
||||
resultCallback = responses;
|
||||
resultCallbackArgs = {error:responses.statusText};
|
||||
return;
|
||||
} else if (options.handleAuthFail !== false && xhr.responseJSON.error === 'git_auth_failed') {
|
||||
} else if (options.handleAuthFail !== false && xhr.responseJSON.code === 'git_auth_failed') {
|
||||
var url = activeProject.git.remotes[xhr.responseJSON.remote||options.remote||'origin'].fetch;
|
||||
|
||||
var message = $('<div>'+
|
||||
@@ -2040,8 +2039,8 @@ RED.projects = (function() {
|
||||
]
|
||||
});
|
||||
return;
|
||||
} else if (responses[xhr.responseJSON.error]) {
|
||||
resultCallback = responses[xhr.responseJSON.error];
|
||||
} else if (responses[xhr.responseJSON.code]) {
|
||||
resultCallback = responses[xhr.responseJSON.code];
|
||||
resultCallbackArgs = xhr.responseJSON;
|
||||
return;
|
||||
} else if (responses['*']) {
|
||||
@@ -2050,10 +2049,12 @@ RED.projects = (function() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
console.log(responses)
|
||||
console.log(RED._("projects.send-req.unhandled")+":");
|
||||
console.log(xhr);
|
||||
console.log(textStatus);
|
||||
console.log(err);
|
||||
console.log(stack);
|
||||
}).always(function() {
|
||||
var delta = Date.now() - start;
|
||||
delta = Math.max(0,500-delta);
|
||||
|
||||
@@ -140,11 +140,12 @@ RED.tray = (function() {
|
||||
|
||||
// tray.body.parent().width(Math.min($("#editor-stack").position().left-8,tray.width));
|
||||
|
||||
|
||||
$("#main-container").scrollLeft(0);
|
||||
el.css({
|
||||
right: -(el.width()+10)+"px",
|
||||
transition: "right 0.25s ease"
|
||||
});
|
||||
$("#workspace").scrollLeft(0);
|
||||
handleWindowResize();
|
||||
openingTray = true;
|
||||
setTimeout(function() {
|
||||
|
||||
@@ -200,7 +200,7 @@ RED.typeSearch = (function() {
|
||||
dialog.hide();
|
||||
searchResultsDiv.hide();
|
||||
}
|
||||
refreshTypeList();
|
||||
refreshTypeList(opts);
|
||||
addCallback = opts.add;
|
||||
closeCallback = opts.close;
|
||||
RED.events.emit("type-search:open");
|
||||
@@ -254,21 +254,29 @@ RED.typeSearch = (function() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
function refreshTypeList() {
|
||||
function applyFilter(filter,type,def) {
|
||||
return !filter ||
|
||||
(
|
||||
(!filter.type || type === filter.type) &&
|
||||
(!filter.input || def.inputs > 0) &&
|
||||
(!filter.output || def.outputs > 0)
|
||||
)
|
||||
}
|
||||
function refreshTypeList(opts) {
|
||||
var i;
|
||||
searchResults.editableList('empty');
|
||||
searchInput.searchBox('value','');
|
||||
selected = -1;
|
||||
var common = [
|
||||
'inject','debug','function','change','switch'
|
||||
];
|
||||
].filter(function(t) { return applyFilter(opts.filter,t,RED.nodes.getType(t)); });
|
||||
|
||||
var recentlyUsed = Object.keys(typesUsed);
|
||||
recentlyUsed.sort(function(a,b) {
|
||||
return typesUsed[b]-typesUsed[a];
|
||||
});
|
||||
recentlyUsed = recentlyUsed.filter(function(t) {
|
||||
return common.indexOf(t) === -1;
|
||||
return applyFilter(opts.filter,t,RED.nodes.getType(t)) && common.indexOf(t) === -1;
|
||||
});
|
||||
|
||||
var items = [];
|
||||
@@ -313,8 +321,10 @@ RED.typeSearch = (function() {
|
||||
searchResults.editableList('addItem', item);
|
||||
}
|
||||
for (i=0;i<items.length;i++) {
|
||||
items[i].i = index++;
|
||||
searchResults.editableList('addItem', items[i]);
|
||||
if (applyFilter(opts.filter,items[i].type,items[i].def)) {
|
||||
items[i].i = index++;
|
||||
searchResults.editableList('addItem', items[i]);
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
selected = 0;
|
||||
|
||||
@@ -42,6 +42,7 @@ RED.view = (function() {
|
||||
var activeNodes = [];
|
||||
var activeLinks = [];
|
||||
var activeFlowLinks = [];
|
||||
var activeLinkNodes = {};
|
||||
|
||||
var selected_link = null,
|
||||
mousedown_link = null,
|
||||
@@ -61,7 +62,8 @@ RED.view = (function() {
|
||||
clickElapsed = 0,
|
||||
scroll_position = [],
|
||||
quickAddActive = false,
|
||||
quickAddLink = null;
|
||||
quickAddLink = null,
|
||||
showAllLinkPorts = -1;
|
||||
|
||||
var clipboard = "";
|
||||
|
||||
@@ -263,19 +265,41 @@ RED.view = (function() {
|
||||
"stroke-width" : "1px"
|
||||
});
|
||||
}
|
||||
|
||||
var linkLayer = vis.append("g");
|
||||
var dragGroup = vis.append("g");
|
||||
var nodeLayer = vis.append("g");
|
||||
var drag_lines = [];
|
||||
|
||||
function showDragLines(nodes) {
|
||||
showAllLinkPorts = -1;
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
node.el = dragGroup.append("svg:path").attr("class", "drag_line");
|
||||
if ((node.node.type === "link out" && node.portType === PORT_TYPE_OUTPUT) ||
|
||||
(node.node.type === "link in" && node.portType === PORT_TYPE_INPUT)) {
|
||||
node.el.attr("class","link_link drag_line");
|
||||
node.virtualLink = true;
|
||||
showAllLinkPorts = (node.portType === PORT_TYPE_OUTPUT)?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT;
|
||||
}
|
||||
drag_lines.push(node);
|
||||
}
|
||||
|
||||
if (showAllLinkPorts !== -1) {
|
||||
activeNodes.forEach(function(n) {
|
||||
if (n.type === "link in" || n.type === "link out") {
|
||||
n.dirty = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
function hideDragLines() {
|
||||
if (showAllLinkPorts !== -1) {
|
||||
activeNodes.forEach(function(n) {
|
||||
if (n.type === "link in" || n.type === "link out") {
|
||||
n.dirty = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
showAllLinkPorts = -1;
|
||||
while(drag_lines.length) {
|
||||
var line = drag_lines.pop();
|
||||
if (line.el) {
|
||||
@@ -657,10 +681,21 @@ RED.view = (function() {
|
||||
mouse_mode = RED.state.QUICK_JOINING;
|
||||
$(window).on('keyup',disableQuickJoinEventHandler);
|
||||
}
|
||||
var filter = undefined;
|
||||
if (drag_lines.length > 0) {
|
||||
if (drag_lines[0].virtualLink) {
|
||||
filter = {type:drag_lines[0].node.type === 'link in'?'link out':'link in'}
|
||||
} else if (drag_lines[0].portType === PORT_TYPE_OUTPUT) {
|
||||
filter = {input:true}
|
||||
} else {
|
||||
filter = {output:true}
|
||||
}
|
||||
}
|
||||
quickAddActive = true;
|
||||
RED.typeSearch.show({
|
||||
x:d3.event.clientX-mainPos.left-node_width/2,
|
||||
y:d3.event.clientY-mainPos.top-node_height/2,
|
||||
filter: filter,
|
||||
cancel: function() {
|
||||
quickAddActive = false;
|
||||
resetMouseVars();
|
||||
@@ -685,19 +720,54 @@ RED.view = (function() {
|
||||
if (quickAddLink || drag_lines.length > 0) {
|
||||
var drag_line = quickAddLink||drag_lines[0];
|
||||
var src = null,dst,src_port;
|
||||
if (drag_line.portType === PORT_TYPE_OUTPUT && nn.inputs > 0) {
|
||||
if (drag_line.portType === PORT_TYPE_OUTPUT && (nn.inputs > 0 || drag_line.virtualLink) ) {
|
||||
src = drag_line.node;
|
||||
src_port = drag_line.port;
|
||||
dst = nn;
|
||||
} else if (drag_line.portType === PORT_TYPE_INPUT && nn.outputs > 0) {
|
||||
} else if (drag_line.portType === PORT_TYPE_INPUT && (nn.outputs > 0 || drag_line.virtualLink)) {
|
||||
src = nn;
|
||||
dst = drag_line.node;
|
||||
src_port = 0;
|
||||
}
|
||||
|
||||
if (src !== null) {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
historyEvent.links = [link];
|
||||
if (drag_line.virtualLink) {
|
||||
historyEvent = {
|
||||
t:'multi',
|
||||
events: [historyEvent]
|
||||
}
|
||||
var oldSrcLinks = $.extend(true,{},{v:src.links}).v
|
||||
var oldDstLinks = $.extend(true,{},{v:dst.links}).v
|
||||
src.links.push(dst.id);
|
||||
dst.links.push(src.id);
|
||||
src.dirty = true;
|
||||
dst.dirty = true;
|
||||
|
||||
historyEvent.events.push({
|
||||
t:'edit',
|
||||
node: src,
|
||||
dirty: RED.nodes.dirty(),
|
||||
changed: src.changed,
|
||||
changes: {
|
||||
links:oldSrcLinks
|
||||
}
|
||||
});
|
||||
historyEvent.events.push({
|
||||
t:'edit',
|
||||
node: dst,
|
||||
dirty: RED.nodes.dirty(),
|
||||
changed: dst.changed,
|
||||
changes: {
|
||||
links:oldDstLinks
|
||||
}
|
||||
});
|
||||
src.changed = true;
|
||||
dst.changed = true;
|
||||
} else {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
historyEvent.links = [link];
|
||||
}
|
||||
hideDragLines();
|
||||
if (!quickAddLink && drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) {
|
||||
showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]);
|
||||
@@ -1216,9 +1286,15 @@ RED.view = (function() {
|
||||
var currentLinks = activeLinks;
|
||||
var addedLinkLinks = {};
|
||||
activeFlowLinks = [];
|
||||
var activeLinkNodeIds = Object.keys(activeLinkNodes);
|
||||
activeLinkNodeIds.forEach(function(n) {
|
||||
activeLinkNodes[n].dirty = true;
|
||||
})
|
||||
activeLinkNodes = {};
|
||||
for (var i=0;i<moving_set.length;i++) {
|
||||
if (moving_set[i].n.type === "link out" || moving_set[i].n.type === "link in") {
|
||||
var linkNode = moving_set[i].n;
|
||||
activeLinkNodes[linkNode.id] = linkNode;
|
||||
var offFlowLinks = {};
|
||||
linkNode.links.forEach(function(id) {
|
||||
var target = RED.nodes.node(id);
|
||||
@@ -1233,6 +1309,9 @@ RED.view = (function() {
|
||||
link: true
|
||||
});
|
||||
addedLinkLinks[linkNode.id+":"+target.id] = true;
|
||||
activeLinkNodes[target.id] = target;
|
||||
target.dirty = true;
|
||||
|
||||
}
|
||||
} else {
|
||||
offFlowLinks[target.z] = offFlowLinks[target.z]||[];
|
||||
@@ -1248,6 +1327,8 @@ RED.view = (function() {
|
||||
link: true
|
||||
});
|
||||
addedLinkLinks[target.id+":"+linkNode.id] = true;
|
||||
activeLinkNodes[target.id] = target;
|
||||
target.dirty = true;
|
||||
}
|
||||
} else {
|
||||
offFlowLinks[target.z] = offFlowLinks[target.z]||[];
|
||||
@@ -1269,6 +1350,13 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeFlowLinks.length === 0 && selected_link !== null && selected_link.link) {
|
||||
activeLinks.push(selected_link);
|
||||
activeLinkNodes[selected_link.source.id] = selected_link.source;
|
||||
selected_link.source.dirty = true;
|
||||
activeLinkNodes[selected_link.target.id] = selected_link.target;
|
||||
selected_link.target.dirty = true;
|
||||
}
|
||||
} else {
|
||||
selection.flows = workspaceSelection;
|
||||
}
|
||||
@@ -1445,22 +1533,63 @@ RED.view = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
}
|
||||
if (selected_link) {
|
||||
RED.nodes.removeLink(selected_link);
|
||||
removedLinks.push(selected_link);
|
||||
var historyEvent;
|
||||
|
||||
if (selected_link && selected_link.link) {
|
||||
var sourceId = selected_link.source.id;
|
||||
var targetId = selected_link.target.id;
|
||||
var sourceIdIndex = selected_link.target.links.indexOf(sourceId);
|
||||
var targetIdIndex = selected_link.source.links.indexOf(targetId);
|
||||
|
||||
historyEvent = {
|
||||
t:"multi",
|
||||
events: [
|
||||
{
|
||||
t: "edit",
|
||||
node: selected_link.source,
|
||||
changed: selected_link.source.changed,
|
||||
changes: {
|
||||
links: $.extend(true,{},{v:selected_link.source.links}).v
|
||||
}
|
||||
},
|
||||
{
|
||||
t: "edit",
|
||||
node: selected_link.target,
|
||||
changed: selected_link.target.changed,
|
||||
changes: {
|
||||
links: $.extend(true,{},{v:selected_link.target.links}).v
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
dirty:RED.nodes.dirty()
|
||||
}
|
||||
|
||||
selected_link.source.changed = true;
|
||||
selected_link.target.changed = true;
|
||||
selected_link.target.links.splice(sourceIdIndex,1);
|
||||
selected_link.source.links.splice(targetIdIndex,1);
|
||||
selected_link.source.dirty = true;
|
||||
selected_link.target.dirty = true;
|
||||
|
||||
} else {
|
||||
if (selected_link) {
|
||||
RED.nodes.removeLink(selected_link);
|
||||
removedLinks.push(selected_link);
|
||||
}
|
||||
RED.nodes.dirty(true);
|
||||
historyEvent = {
|
||||
t:"delete",
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
}
|
||||
var historyEvent = {
|
||||
t:"delete",
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflowOutputs:removedSubflowOutputs,
|
||||
subflowInputs:removedSubflowInputs,
|
||||
subflow: {
|
||||
instances: subflowInstances
|
||||
},
|
||||
dirty:startDirty
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
|
||||
selected_link = null;
|
||||
@@ -1507,7 +1636,7 @@ RED.view = (function() {
|
||||
}
|
||||
}
|
||||
clipboard = JSON.stringify(nns);
|
||||
RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}));
|
||||
RED.notify(RED._("clipboard.nodeCopied",{count:nns.length}),{id:"clipboard"});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1574,6 +1703,7 @@ RED.view = (function() {
|
||||
if (d3.event.ctrlKey || d3.event.metaKey) {
|
||||
mouse_mode = RED.state.QUICK_JOINING;
|
||||
showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]);
|
||||
quickAddLink = null;
|
||||
$(window).on('keyup',disableQuickJoinEventHandler);
|
||||
}
|
||||
}
|
||||
@@ -1584,7 +1714,16 @@ RED.view = (function() {
|
||||
function portMouseUp(d,portType,portIndex) {
|
||||
var i;
|
||||
if (mouse_mode === RED.state.QUICK_JOINING && drag_lines.length > 0) {
|
||||
if (drag_lines[0].node===d) {
|
||||
if (drag_lines[0].node === d) {
|
||||
// Cannot quick-join to self
|
||||
return
|
||||
}
|
||||
if (drag_lines[0].virtualLink &&
|
||||
(
|
||||
(drag_lines[0].node.type === 'link in' && d.type !== 'link out') ||
|
||||
(drag_lines[0].node.type === 'link out' && d.type !== 'link in')
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1608,12 +1747,17 @@ RED.view = (function() {
|
||||
}
|
||||
var addedLinks = [];
|
||||
var removedLinks = [];
|
||||
var modifiedNodes = []; // joining link nodes
|
||||
|
||||
var select_link = null;
|
||||
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
if (drag_lines[i].link) {
|
||||
removedLinks.push(drag_lines[i].link)
|
||||
}
|
||||
}
|
||||
var linkEditEvents = [];
|
||||
|
||||
for (i=0;i<drag_lines.length;i++) {
|
||||
if (portType != drag_lines[i].portType && mouseup_node !== drag_lines[i].node) {
|
||||
var drag_line = drag_lines[i];
|
||||
@@ -1627,21 +1771,75 @@ RED.view = (function() {
|
||||
dst = drag_line.node;
|
||||
src_port = portIndex;
|
||||
}
|
||||
var existingLink = RED.nodes.filterLinks({source:src,target:dst,sourcePort: src_port}).length !== 0;
|
||||
if (!existingLink) {
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
RED.nodes.addLink(link);
|
||||
addedLinks.push(link);
|
||||
var link = {source: src, sourcePort:src_port, target: dst};
|
||||
if (drag_line.virtualLink) {
|
||||
if (/^link (in|out)$/.test(src.type) && /^link (in|out)$/.test(dst.type)) {
|
||||
if (src.links.indexOf(dst.id) === -1 && dst.links.indexOf(src.id) === -1) {
|
||||
var oldSrcLinks = $.extend(true,{},{v:src.links}).v
|
||||
var oldDstLinks = $.extend(true,{},{v:dst.links}).v
|
||||
src.links.push(dst.id);
|
||||
dst.links.push(src.id);
|
||||
src.dirty = true;
|
||||
dst.dirty = true;
|
||||
modifiedNodes.push(src);
|
||||
modifiedNodes.push(dst);
|
||||
|
||||
link.link = true;
|
||||
activeLinks.push(link);
|
||||
activeLinkNodes[src.id] = src;
|
||||
activeLinkNodes[dst.id] = dst;
|
||||
select_link = link;
|
||||
|
||||
linkEditEvents.push({
|
||||
t:'edit',
|
||||
node: src,
|
||||
dirty: RED.nodes.dirty(),
|
||||
changed: src.changed,
|
||||
changes: {
|
||||
links:oldSrcLinks
|
||||
}
|
||||
});
|
||||
linkEditEvents.push({
|
||||
t:'edit',
|
||||
node: dst,
|
||||
dirty: RED.nodes.dirty(),
|
||||
changed: dst.changed,
|
||||
changes: {
|
||||
links:oldDstLinks
|
||||
}
|
||||
});
|
||||
src.changed = true;
|
||||
dst.changed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var existingLink = RED.nodes.filterLinks({source:src,target:dst,sourcePort: src_port}).length !== 0;
|
||||
if (!existingLink) {
|
||||
RED.nodes.addLink(link);
|
||||
addedLinks.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addedLinks.length > 0 || removedLinks.length > 0) {
|
||||
var historyEvent = {
|
||||
t:"add",
|
||||
links:addedLinks,
|
||||
removedLinks: removedLinks,
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
if (addedLinks.length > 0 || removedLinks.length > 0 || modifiedNodes.length > 0) {
|
||||
// console.log(addedLinks);
|
||||
// console.log(removedLinks);
|
||||
// console.log(modifiedNodes);
|
||||
var historyEvent;
|
||||
if (modifiedNodes.length > 0) {
|
||||
historyEvent = {
|
||||
t:"multi",
|
||||
events: linkEditEvents,
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
} else {
|
||||
historyEvent = {
|
||||
t:"add",
|
||||
links:addedLinks,
|
||||
removedLinks: removedLinks,
|
||||
dirty:RED.nodes.dirty()
|
||||
};
|
||||
}
|
||||
if (activeSubflow) {
|
||||
var subflowRefresh = RED.subflow.refresh(true);
|
||||
if (subflowRefresh) {
|
||||
@@ -1657,7 +1855,7 @@ RED.view = (function() {
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
if (mouse_mode === RED.state.QUICK_JOINING) {
|
||||
if (addedLinks.length > 0) {
|
||||
if (addedLinks.length > 0 || modifiedNodes.length > 0) {
|
||||
hideDragLines();
|
||||
if (portType === PORT_TYPE_INPUT && d.outputs > 0) {
|
||||
showDragLines([{node:d,port:0,portType:PORT_TYPE_OUTPUT}]);
|
||||
@@ -1666,6 +1864,11 @@ RED.view = (function() {
|
||||
} else {
|
||||
resetMouseVars();
|
||||
}
|
||||
selected_link = select_link;
|
||||
mousedown_link = select_link;
|
||||
if (select_link) {
|
||||
updateSelection();
|
||||
}
|
||||
}
|
||||
redraw();
|
||||
return;
|
||||
@@ -1673,7 +1876,11 @@ RED.view = (function() {
|
||||
|
||||
resetMouseVars();
|
||||
hideDragLines();
|
||||
selected_link = null;
|
||||
selected_link = select_link;
|
||||
mousedown_link = select_link;
|
||||
if (select_link) {
|
||||
updateSelection();
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
}
|
||||
@@ -1772,9 +1979,20 @@ RED.view = (function() {
|
||||
});
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
function portMouseOver(port,d,portType,portIndex) {
|
||||
clearTimeout(portLabelHoverTimeout);
|
||||
var active = (mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== portType));
|
||||
var active = (mouse_mode!=RED.state.JOINING && mouse_mode != RED.state.QUICK_JOINING) || // Not currently joining - all ports active
|
||||
(
|
||||
drag_lines.length > 0 && // Currently joining
|
||||
drag_lines[0].portType !== portType && // INPUT->OUTPUT OUTPUT->INPUT
|
||||
(
|
||||
!drag_lines[0].virtualLink || // Not a link wire
|
||||
(drag_lines[0].node.type === 'link in' && d.type === 'link out') ||
|
||||
(drag_lines[0].node.type === 'link out' && d.type === 'link in')
|
||||
)
|
||||
)
|
||||
|
||||
if (active && ((portType === PORT_TYPE_INPUT && ((d._def && d._def.inputLabels)||d.inputLabels)) || (portType === PORT_TYPE_OUTPUT && ((d._def && d._def.outputLabels)||d.outputLabels)))) {
|
||||
portLabelHoverTimeout = setTimeout(function() {
|
||||
var tooltip = getPortLabel(d,portType,portIndex);
|
||||
@@ -1815,7 +2033,24 @@ RED.view = (function() {
|
||||
return;
|
||||
}
|
||||
var direction = d._def? (d.inputs > 0 ? 1: 0) : (d.direction == "in" ? 0: 1)
|
||||
var wasJoining = false;
|
||||
if (mouse_mode === RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) {
|
||||
wasJoining = true;
|
||||
if (drag_lines.length > 0) {
|
||||
if (drag_lines[0].virtualLink) {
|
||||
if (d.type === 'link in') {
|
||||
direction = 1;
|
||||
} else if (d.type === 'link out') {
|
||||
direction = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
portMouseUp(d, direction, 0);
|
||||
if (wasJoining) {
|
||||
d3.selectAll(".port_hovered").classed("port_hovered",false);
|
||||
}
|
||||
}
|
||||
|
||||
function nodeMouseDown(d) {
|
||||
@@ -2033,12 +2268,12 @@ RED.view = (function() {
|
||||
outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor);
|
||||
|
||||
// Don't bother redrawing nodes if we're drawing links
|
||||
if (mouse_mode != RED.state.JOINING) {
|
||||
if (showAllLinkPorts !== -1 || mouse_mode != RED.state.JOINING) {
|
||||
|
||||
var dirtyNodes = {};
|
||||
|
||||
if (activeSubflow) {
|
||||
var subflowOutputs = vis.selectAll(".subflowoutput").data(activeSubflow.out,function(d,i){ return d.id;});
|
||||
var subflowOutputs = nodeLayer.selectAll(".subflowoutput").data(activeSubflow.out,function(d,i){ return d.id;});
|
||||
subflowOutputs.exit().remove();
|
||||
var outGroup = subflowOutputs.enter().insert("svg:g").attr("class","node subflowoutput").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"});
|
||||
outGroup.each(function(d,i) {
|
||||
@@ -2081,7 +2316,7 @@ RED.view = (function() {
|
||||
outGroup.append("svg:text").attr("class","port_label").attr("x",20).attr("y",8).style("font-size","10px").text("output");
|
||||
outGroup.append("svg:text").attr("class","port_label port_index").attr("x",20).attr("y",24).text(function(d,i){ return i+1});
|
||||
|
||||
var subflowInputs = vis.selectAll(".subflowinput").data(activeSubflow.in,function(d,i){ return d.id;});
|
||||
var subflowInputs = nodeLayer.selectAll(".subflowinput").data(activeSubflow.in,function(d,i){ return d.id;});
|
||||
subflowInputs.exit().remove();
|
||||
var inGroup = subflowInputs.enter().insert("svg:g").attr("class","node subflowinput").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"});
|
||||
inGroup.each(function(d,i) {
|
||||
@@ -2143,11 +2378,11 @@ RED.view = (function() {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
vis.selectAll(".subflowoutput").remove();
|
||||
vis.selectAll(".subflowinput").remove();
|
||||
nodeLayer.selectAll(".subflowoutput").remove();
|
||||
nodeLayer.selectAll(".subflowinput").remove();
|
||||
}
|
||||
|
||||
var node = vis.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
|
||||
var node = nodeLayer.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
|
||||
node.exit().remove();
|
||||
|
||||
var nodeEnter = node.enter().insert("svg:g")
|
||||
@@ -2157,10 +2392,11 @@ RED.view = (function() {
|
||||
|
||||
nodeEnter.each(function(d,i) {
|
||||
var node = d3.select(this);
|
||||
var isLink = d.hasOwnProperty('l')?!d.l : (d.type === "link in" || d.type === "link out")
|
||||
var isLink = (d.type === "link in" || d.type === "link out")
|
||||
var hideLabel = d.hasOwnProperty('l')?!d.l : isLink;
|
||||
node.attr("id",d.id);
|
||||
var l = RED.utils.getNodeLabel(d);
|
||||
if (isLink) {
|
||||
if (hideLabel) {
|
||||
d.w = node_height;
|
||||
} else {
|
||||
d.w = Math.max(node_width,20*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/20)) );
|
||||
@@ -2268,6 +2504,19 @@ RED.view = (function() {
|
||||
}
|
||||
},500);
|
||||
}
|
||||
} else if (mouse_mode === RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) {
|
||||
if (drag_lines.length > 0) {
|
||||
var selectClass;
|
||||
var portType;
|
||||
if ((drag_lines[0].virtualLink && drag_lines[0].portType === PORT_TYPE_INPUT) || drag_lines[0].portType === PORT_TYPE_OUTPUT) {
|
||||
selectClass = ".port_input .port";
|
||||
portType = PORT_TYPE_INPUT;
|
||||
} else {
|
||||
selectClass = ".port_output .port";
|
||||
portType = PORT_TYPE_OUTPUT;
|
||||
}
|
||||
portMouseOver(d3.select(this.parentNode).selectAll(selectClass),d,portType,0);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on("mouseout",function(d) {
|
||||
@@ -2278,6 +2527,20 @@ RED.view = (function() {
|
||||
portLabelHover.remove();
|
||||
portLabelHover = null;
|
||||
}
|
||||
if (mouse_mode === RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) {
|
||||
if (drag_lines.length > 0) {
|
||||
var selectClass;
|
||||
var portType;
|
||||
if ((drag_lines[0].virtualLink && drag_lines[0].portType === PORT_TYPE_INPUT) || drag_lines[0].portType === PORT_TYPE_OUTPUT) {
|
||||
selectClass = ".port_input .port";
|
||||
portType = PORT_TYPE_INPUT;
|
||||
} else {
|
||||
selectClass = ".port_output .port";
|
||||
portType = PORT_TYPE_OUTPUT;
|
||||
}
|
||||
portMouseOut(d3.select(this.parentNode).selectAll(selectClass),d,portType,0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//node.append("rect").attr("class", "node-gradient-top").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-top)").style("pointer-events","none");
|
||||
@@ -2332,7 +2595,7 @@ RED.view = (function() {
|
||||
.attr("x", 38)
|
||||
.attr("dy", ".35em")
|
||||
.attr("text-anchor","start")
|
||||
.classed("hidden",isLink);
|
||||
.classed("hidden",hideLabel);
|
||||
|
||||
if (d._def.align) {
|
||||
text.attr("class","node_label node_label_"+d._def.align);
|
||||
@@ -2360,13 +2623,14 @@ RED.view = (function() {
|
||||
|
||||
node.each(function(d,i) {
|
||||
if (d.dirty) {
|
||||
var isLink = d.hasOwnProperty('l')?!d.l : (d.type === "link in" || d.type === "link out")
|
||||
var isLink = (d.type === "link in" || d.type === "link out")
|
||||
var hideLabel = d.hasOwnProperty('l')?!d.l : isLink;
|
||||
dirtyNodes[d.id] = d;
|
||||
//if (d.x < -50) deleteSelection(); // Delete nodes if dragged back to palette
|
||||
if (/*!isLink &&*/ d.resize) {
|
||||
if (d.resize) {
|
||||
var l = RED.utils.getNodeLabel(d);
|
||||
var ow = d.w;
|
||||
if (isLink) {
|
||||
if (hideLabel) {
|
||||
d.w = node_height;
|
||||
} else {
|
||||
d.w = Math.max(node_width,20*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/20)) );
|
||||
@@ -2396,13 +2660,24 @@ RED.view = (function() {
|
||||
//thisNode.selectAll(".node_icon_shade_border_right").attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2)});
|
||||
|
||||
var inputPorts = thisNode.selectAll(".port_input");
|
||||
if (d.inputs === 0 && !inputPorts.empty()) {
|
||||
if (isLink && showAllLinkPorts === -1 && !activeLinkNodes[d.id] && d.inputs === 0 && !inputPorts.empty()) {
|
||||
inputPorts.remove();
|
||||
//nodeLabel.attr("x",30);
|
||||
} else if (d.inputs === 1 && inputPorts.empty()) {
|
||||
} else if (((isLink && (showAllLinkPorts===PORT_TYPE_INPUT||activeLinkNodes[d.id]))|| d.inputs === 1) && inputPorts.empty()) {
|
||||
var inputGroup = thisNode.append("g").attr("class","port_input");
|
||||
inputGroup.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
|
||||
var inputGroupPorts;
|
||||
|
||||
if (d.type === "link in") {
|
||||
inputGroupPorts = inputGroup.append("circle")
|
||||
.attr("cx",-1).attr("cy",5)
|
||||
.attr("r",5)
|
||||
.attr("class","port link_port")
|
||||
// inputGroupPorts = inputGroup.append("path")
|
||||
// .attr("d","M 4 -1 h -3 a 6 6 0 1 0 0 12 h 3")
|
||||
// .attr("class","port link_port")
|
||||
} else {
|
||||
inputGroupPorts = inputGroup.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
}
|
||||
inputGroupPorts.on("mousedown",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
|
||||
.on("touchstart",function(d){portMouseDown(d,PORT_TYPE_INPUT,0);})
|
||||
.on("mouseup",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
.on("touchend",function(d){portMouseUp(d,PORT_TYPE_INPUT,0);} )
|
||||
@@ -2411,13 +2686,38 @@ RED.view = (function() {
|
||||
}
|
||||
|
||||
var numOutputs = d.outputs;
|
||||
if (isLink && d.type === "link out") {
|
||||
if (showAllLinkPorts===PORT_TYPE_OUTPUT || activeLinkNodes[d.id]) {
|
||||
d.ports = [0];
|
||||
numOutputs = 1;
|
||||
} else {
|
||||
d.ports = [];
|
||||
numOutputs = 0;
|
||||
}
|
||||
}
|
||||
var y = (d.h/2)-((numOutputs-1)/2)*13;
|
||||
d.ports = d.ports || d3.range(numOutputs);
|
||||
d._ports = thisNode.selectAll(".port_output").data(d.ports);
|
||||
var output_group = d._ports.enter().append("g").attr("class","port_output");
|
||||
var output_group_ports;
|
||||
|
||||
output_group.append("rect").attr("class","port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10)
|
||||
.on("mousedown",(function(){var node = d; return function(d,i){portMouseDown(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
if (d.type === "link out") {
|
||||
output_group_ports = output_group.append("circle")
|
||||
.attr("cx",11).attr("cy",5)
|
||||
.attr("r",5)
|
||||
.attr("class","port link_port")
|
||||
// output_group_ports = output_group.append("path")
|
||||
// .attr("d","M 6 -1 h 3 a 6 6 0 1 1 0 12 h -3")
|
||||
// .attr("class","port link_port")
|
||||
} else {
|
||||
output_group_ports = output_group.append("rect")
|
||||
.attr("class","port")
|
||||
.attr("rx",3).attr("ry",3)
|
||||
.attr("width",10)
|
||||
.attr("height",10)
|
||||
}
|
||||
|
||||
output_group_ports.on("mousedown",(function(){var node = d; return function(d,i){portMouseDown(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("touchstart",(function(){var node = d; return function(d,i){portMouseDown(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("mouseup",(function(){var node = d; return function(d,i){portMouseUp(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
.on("touchend",(function(){var node = d; return function(d,i){portMouseUp(node,PORT_TYPE_OUTPUT,i);}})() )
|
||||
@@ -2464,7 +2764,7 @@ RED.view = (function() {
|
||||
}
|
||||
return "node_label"+
|
||||
(d._def.align?" node_label_"+d._def.align:"")+s;
|
||||
}).classed("hidden",isLink);
|
||||
}).classed("hidden",hideLabel);
|
||||
if (d._def.icon) {
|
||||
var icon = thisNode.select(".node_icon");
|
||||
var faIcon = thisNode.select(".fa-lg");
|
||||
@@ -2592,7 +2892,7 @@ RED.view = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
var link = vis.selectAll(".link").data(
|
||||
var link = linkLayer.selectAll(".link").data(
|
||||
activeLinks,
|
||||
function(d) {
|
||||
return d.source.id+":"+d.sourcePort+":"+d.target.id+":"+d.target.i;
|
||||
@@ -2638,7 +2938,7 @@ RED.view = (function() {
|
||||
});
|
||||
|
||||
link.exit().remove();
|
||||
var links = vis.selectAll(".link_path");
|
||||
var links = linkLayer.selectAll(".link_path");
|
||||
links.each(function(d) {
|
||||
var link = d3.select(this);
|
||||
if (d.added || d===selected_link || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) {
|
||||
@@ -2666,7 +2966,7 @@ RED.view = (function() {
|
||||
delete d.added;
|
||||
return d.target.type == "unknown" || d.source.type == "unknown"
|
||||
});
|
||||
var offLinks = vis.selectAll(".link_flow_link_g").data(
|
||||
var offLinks = linkLayer.selectAll(".link_flow_link_g").data(
|
||||
activeFlowLinks,
|
||||
function(d) {
|
||||
return d.node.id+":"+d.refresh
|
||||
@@ -2697,10 +2997,13 @@ RED.view = (function() {
|
||||
var y = -(flows.length-1)*h/2;
|
||||
var linkGroups = g.selectAll(".link_group").data(flows);
|
||||
var enterLinkGroups = linkGroups.enter().append("g").attr("class","link_group")
|
||||
.on('mouseover', function() { d3.select(this).classed('link_group_active',true)})
|
||||
.on('mouseout', function() { d3.select(this).classed('link_group_active',false)})
|
||||
.on('mouseover', function() { if (mouse_mode !== 0) { return } d3.select(this).classed('link_group_active',true)})
|
||||
.on('mouseout', function() {if (mouse_mode !== 0) { return } d3.select(this).classed('link_group_active',false)})
|
||||
.on('mousedown', function() { d3.event.preventDefault(); d3.event.stopPropagation(); })
|
||||
.on('mouseup', function(f) {
|
||||
if (mouse_mode !== 0) {
|
||||
return
|
||||
}
|
||||
d3.event.stopPropagation();
|
||||
var targets = d.links[f];
|
||||
RED.workspaces.show(f);
|
||||
@@ -2772,7 +3075,7 @@ RED.view = (function() {
|
||||
linkGroups.exit().remove();
|
||||
});
|
||||
offLinks.exit().remove();
|
||||
offLinks = vis.selectAll(".link_flow_link_g");
|
||||
offLinks = linkLayer.selectAll(".link_flow_link_g");
|
||||
offLinks.each(function(d) {
|
||||
var s = 1;
|
||||
if (d.node.type === "link in") {
|
||||
@@ -2785,7 +3088,7 @@ RED.view = (function() {
|
||||
|
||||
} else {
|
||||
// JOINING - unselect any selected links
|
||||
vis.selectAll(".link_selected").data(
|
||||
linkLayer.selectAll(".link_selected").data(
|
||||
activeLinks,
|
||||
function(d) {
|
||||
return d.source.id+":"+d.sourcePort+":"+d.target.id+":"+d.target.i;
|
||||
@@ -2952,7 +3255,7 @@ RED.view = (function() {
|
||||
}
|
||||
if (counts.length > 0) {
|
||||
var countList = "<ul><li>"+counts.join("</li><li>")+"</li></ul>";
|
||||
RED.notify("<p>"+RED._("clipboard.nodesImported")+"</p>"+countList);
|
||||
RED.notify("<p>"+RED._("clipboard.nodesImported")+"</p>"+countList,{id:"clipboard"});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,6 @@ RED.workspaces = (function() {
|
||||
height -= $(rows[i]).outerHeight(true);
|
||||
}
|
||||
height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom")));
|
||||
height -= 28;
|
||||
$(".node-text-editor").css("height",height+"px");
|
||||
tabflowEditor.resize();
|
||||
},
|
||||
@@ -166,7 +165,6 @@ RED.workspaces = (function() {
|
||||
|
||||
var row = $('<div class="form-row node-text-editor-row">'+
|
||||
'<label for="node-input-info" data-i18n="editor:workspace.info" style="width:300px;"></label>'+
|
||||
'<div class="node-text-editor-toolbar"></div>'+
|
||||
'<div style="min-height:250px;" class="node-text-editor" id="node-input-info"></div>'+
|
||||
'</div>').appendTo(dialogForm);
|
||||
tabflowEditor = RED.editor.createEditor({
|
||||
@@ -175,10 +173,6 @@ RED.workspaces = (function() {
|
||||
value: ""
|
||||
});
|
||||
|
||||
var toolbar = RED.editor.types._markdown.buildToolbar(row.find(".node-text-editor-toolbar"),tabflowEditor);
|
||||
|
||||
$('<button id="node-info-input-info-expand" class="editor-button" style="float: right;"><i class="fa fa-expand"></i></button>').appendTo(toolbar);
|
||||
|
||||
$('#node-info-input-info-expand').click(function(e) {
|
||||
e.preventDefault();
|
||||
var value = tabflowEditor.getValue();
|
||||
|
||||
@@ -567,9 +567,15 @@ ul.node-dialog-configm-deploy-list {
|
||||
td.lineno {
|
||||
font-family: monospace;
|
||||
text-align: right;
|
||||
color: #aaa;
|
||||
color: #999;
|
||||
background: #f6f6f6;
|
||||
padding: 1px 5px;
|
||||
&.added {
|
||||
background: #c0f6c0;
|
||||
}
|
||||
&.removed {
|
||||
background: #ffcccc;
|
||||
}
|
||||
}
|
||||
td.lineno:nth-child(3) {
|
||||
border-left: 1px solid $secondary-border-color;
|
||||
@@ -578,12 +584,20 @@ ul.node-dialog-configm-deploy-list {
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
padding: 1px 5px;
|
||||
border-left: 1px solid #ccc;
|
||||
span.prefix {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.added {
|
||||
border-left-color: #aaeeaa
|
||||
}
|
||||
&.removed {
|
||||
border-left-color: #eebbbb
|
||||
}
|
||||
}
|
||||
td.blank {
|
||||
background: #f6f6f6;
|
||||
@@ -592,7 +606,7 @@ ul.node-dialog-configm-deploy-list {
|
||||
background: #eefaee;
|
||||
}
|
||||
td.removed {
|
||||
background: #fadddd;
|
||||
background: #ffecec;
|
||||
}
|
||||
tr.mergeHeader td {
|
||||
color: #800080;
|
||||
|
||||
@@ -209,11 +209,28 @@
|
||||
}
|
||||
|
||||
.node-text-editor {
|
||||
position: relative;
|
||||
.node-text-editor-help {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
right: 1px;
|
||||
border-bottom-right-radius: 5px;
|
||||
z-Index: 8;
|
||||
border-bottom: none;
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.node-text-editor-container {
|
||||
border:1px solid #ccc;
|
||||
border-radius:5px;
|
||||
overflow: hidden;
|
||||
font-size: 14px !important;
|
||||
font-family: Menlo, Consolas, 'DejaVu Sans Mono', Courier, monospace !important;
|
||||
height: 100%;
|
||||
|
||||
&.node-text-editor-container-toolbar {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-button {
|
||||
@@ -333,7 +350,7 @@
|
||||
padding: 10px;
|
||||
border:1px solid #ccc;
|
||||
border-radius:5px;
|
||||
height: calc(100% - 31px);
|
||||
height: calc(100% - 21px);
|
||||
overflow-y: scroll;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -194,8 +194,8 @@
|
||||
}
|
||||
|
||||
.port_hovered {
|
||||
stroke: $port-selected-color;
|
||||
fill: $port-selected-color;
|
||||
stroke: $port-selected-color !important;
|
||||
fill: $port-selected-color !important;
|
||||
}
|
||||
|
||||
.port_quick_link {
|
||||
@@ -211,7 +211,7 @@
|
||||
}
|
||||
|
||||
.drag_line {
|
||||
stroke: $node-selected-color;
|
||||
stroke: $node-selected-color !important;
|
||||
stroke-width: 3;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
@@ -236,10 +236,10 @@
|
||||
stroke: $link-link-color;
|
||||
fill: none;
|
||||
stroke-dasharray: 15,2;
|
||||
pointer-events: none;
|
||||
// pointer-events: none;
|
||||
}
|
||||
.link_port {
|
||||
fill: #fff;
|
||||
fill: #eee;
|
||||
stroke: $link-link-color;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
@@ -54,3 +54,65 @@
|
||||
.notification-error {
|
||||
border-color: #AD1625;
|
||||
}
|
||||
|
||||
.notification-shake-horizontal {
|
||||
-webkit-animation: notification-shake-horizontal 0.3s steps(2, end) both;
|
||||
animation: notification-shake-horizontal 0.3s steps(2, end) both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes notification-shake-horizontal {
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
10%,
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
-webkit-transform: translateX(-1px);
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
20%,
|
||||
40%,
|
||||
60% {
|
||||
-webkit-transform: translateX(1px);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
// 80% {
|
||||
// -webkit-transform: translateX(1px);
|
||||
// transform: translateX(1px);
|
||||
// }
|
||||
// 90% {
|
||||
// -webkit-transform: translateX(-1px);
|
||||
// transform: translateX(-1px);
|
||||
// }
|
||||
}
|
||||
@keyframes notification-shake-horizontal {
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
10%,
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
-webkit-transform: translateX(-1px);
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
20%,
|
||||
40%,
|
||||
60% {
|
||||
-webkit-transform: translateX(1px);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
// 80% {
|
||||
// -webkit-transform: translateX(1px);
|
||||
// transform: translateX(1px);
|
||||
// }
|
||||
// 90% {
|
||||
// -webkit-transform: translateX(-1px);
|
||||
// transform: translateX(-1px);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@
|
||||
@include component-footer-button;
|
||||
}
|
||||
|
||||
|
||||
.palette-category {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
@@ -101,6 +100,9 @@
|
||||
padding-left: 30px;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: $palette-header-background !important;
|
||||
}
|
||||
}
|
||||
.palette-header > i {
|
||||
position: absolute;
|
||||
|
||||
@@ -42,11 +42,13 @@
|
||||
.red-ui-panels.red-ui-panels-horizontal {
|
||||
height: 100%;
|
||||
.red-ui-panel {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: calc(50% - 4px);
|
||||
}
|
||||
.red-ui-panels-separator {
|
||||
vertical-align: top;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
border-left: 1px solid $secondary-border-color;
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
@import "ui/common/nodeList";
|
||||
@import "ui/common/checkboxSet";
|
||||
@import "ui/common/stack";
|
||||
@import "ui/common/treeList";
|
||||
|
||||
@import "dragdrop";
|
||||
|
||||
|
||||
108
packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss
vendored
Normal file
108
packages/node_modules/@node-red/editor-client/src/sass/ui/common/treeList.scss
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
.red-ui-treeList {
|
||||
|
||||
}
|
||||
|
||||
.red-ui-treeList-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: #f9f9f9;
|
||||
|
||||
border: 1px solid $form-input-border-color;
|
||||
border-radius: 4px;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
.red-ui-editableList-border {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.red-ui-editableList-container {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.red-ui-editableList-container li {
|
||||
padding: 0;
|
||||
border-bottom: none;
|
||||
.red-ui-editableList-container {
|
||||
// margin-left: 15px;
|
||||
}
|
||||
}
|
||||
.red-ui-editableList-item-content {
|
||||
& > .red-ui-treeList-label .fa-angle-right {
|
||||
transition: transform 0.1s ease-in-out;
|
||||
}
|
||||
.red-ui-editableList {
|
||||
display: none;
|
||||
}
|
||||
&.expanded {
|
||||
& > .red-ui-treeList-label .fa-angle-right {
|
||||
transform: rotate(90deg)
|
||||
}
|
||||
& > .red-ui-editableList {
|
||||
display: block
|
||||
}
|
||||
& > .red-ui-treeList-spinner {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
label.red-ui-treeList-label {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
.red-ui-treeList-label {
|
||||
@include disable-selection;
|
||||
padding: 6px 0;
|
||||
display: block;
|
||||
color: $form-text-color;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
|
||||
&:hover {
|
||||
background: #f9f9f9;
|
||||
color: $form-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
&:focus {
|
||||
outline: none;
|
||||
color: $form-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.red-ui-treeList-label-text {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.red-ui-treeList-icon {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.red-ui-treeList-spinner {
|
||||
display: none;
|
||||
height: 32px;
|
||||
background: url(images/spin.svg) 50% 50% no-repeat;
|
||||
background-size: auto 20px;
|
||||
}
|
||||
@@ -118,11 +118,13 @@
|
||||
'$count':{ args:[ 'array' ]},
|
||||
'$each':{ args:[ 'object', 'function' ]},
|
||||
'$env': { args:[ 'arg' ]},
|
||||
'$eval': { args: ['expr', 'context']},
|
||||
'$exists':{ args:[ 'arg' ]},
|
||||
'$filter':{ args:[ 'array', 'function' ]},
|
||||
'$floor':{ args:[ 'number' ]},
|
||||
'$flowContext': {args:['string']},
|
||||
'$formatBase': {args:['number','radix']},
|
||||
'$formatInteger': {args:['number', 'picture']},
|
||||
'$formatNumber': {args:['number', 'picture', 'options']},
|
||||
'$fromMillis': {args:['number']},
|
||||
'$globalContext': {args:['string']},
|
||||
@@ -141,6 +143,7 @@
|
||||
'$now':{ args:[ ]},
|
||||
'$number':{ args:[ 'arg' ]},
|
||||
'$pad': {args:['str', 'width','char']},
|
||||
'$parseInteger': {args:['string', 'picture']},
|
||||
'$power':{ args:[ 'base', 'exponent' ]},
|
||||
'$random':{ args:[ ]},
|
||||
'$reduce':{ args:[ 'array', 'function' , 'init' ]},
|
||||
|
||||
@@ -483,20 +483,17 @@
|
||||
var key = RED.utils.parseContextKey(payload);
|
||||
payload = this.payloadType+"."+key.key;
|
||||
}
|
||||
var label = (this.name||payload);
|
||||
var label = this._def.label.call(this);
|
||||
if (label.length > 30) {
|
||||
label = label.substring(0,50)+"...";
|
||||
}
|
||||
label = label.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
|
||||
if (this.payloadType === "date") { label = this._("inject.timestamp"); }
|
||||
if (this.payloadType === "none") { label = this._("inject.blank"); }
|
||||
var node = this;
|
||||
$.ajax({
|
||||
url: "inject/"+this.id,
|
||||
type:"POST",
|
||||
success: function(resp) {
|
||||
RED.notify(node._("inject.success",{label:label}),"success");
|
||||
RED.notify(node._("inject.success",{label:label}),{type:"success",id:"inject"});
|
||||
},
|
||||
error: function(jqXHR,textStatus,errorThrown) {
|
||||
if (jqXHR.status == 404) {
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<label for="node-input-typed-complete"><i class="fa fa-list"></i> <span data-i18n="debug.output"></span></label>
|
||||
<input id="node-input-typed-complete" type="text" style="width: 70%">
|
||||
<input id="node-input-complete" type="hidden">
|
||||
<input id="node-input-targetType" type="hidden">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-tosidebar"><i class="fa fa-random"></i> <span data-i18n="debug.to"></span></label>
|
||||
<label for="node-input-tosidebar" style="width:70%">
|
||||
@@ -42,11 +44,15 @@
|
||||
tosidebar: {value:true},
|
||||
console: {value:false},
|
||||
tostatus: {value:false},
|
||||
complete: {value:"false", required:true}
|
||||
complete: {value:"false", required:true},
|
||||
targetType: {value:undefined}
|
||||
},
|
||||
label: function() {
|
||||
var suffix = "";
|
||||
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
|
||||
if (this.targetType === "jsonata") {
|
||||
return (this.name || "JSONata") + suffix;
|
||||
}
|
||||
if (this.complete === true || this.complete === "true") {
|
||||
return (this.name||"msg") + suffix;
|
||||
} else {
|
||||
@@ -245,6 +251,11 @@
|
||||
delete RED._debug;
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var none = {
|
||||
value: "none",
|
||||
label: RED._("node-red:debug.none"),
|
||||
hasValue: false
|
||||
};
|
||||
if (this.tosidebar === undefined) {
|
||||
this.tosidebar = true;
|
||||
$("#node-input-tosidebar").prop('checked', true);
|
||||
@@ -254,8 +265,21 @@
|
||||
$("#node-input-console").prop('checked', this.console);
|
||||
$("#node-input-tosidebar").prop('checked', true);
|
||||
}
|
||||
$("#node-input-typed-complete").typedInput({types:['msg', {value:"full",label:RED._("node-red:debug.msgobj"),hasValue:false}]});
|
||||
if (this.complete === "true" || this.complete === true) {
|
||||
var fullType = {
|
||||
value: "full",
|
||||
label: RED._("node-red:debug.msgobj"),
|
||||
hasValue: false
|
||||
};
|
||||
$("#node-input-typed-complete").typedInput({
|
||||
default: "msg",
|
||||
types:['msg', fullType, "jsonata"],
|
||||
typeField: $("#node-input-targetType")
|
||||
});
|
||||
if (this.targetType === "jsonata") {
|
||||
var property = this.complete || "";
|
||||
$("#node-input-typed-complete").typedInput('type','jsonata');
|
||||
$("#node-input-typed-complete").typedInput('value',property);
|
||||
} else if ((this.targetType === "full") || this.complete === "true" || this.complete === true) {
|
||||
// show complete message object
|
||||
$("#node-input-typed-complete").typedInput('type','full');
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
var util = require("util");
|
||||
@@ -9,9 +8,11 @@ module.exports = function(RED) {
|
||||
util.inspect.styles.boolean = "red";
|
||||
|
||||
function DebugNode(n) {
|
||||
var is_edit = (n.targetType === "jsonata");
|
||||
var edit_exp = is_edit ? n.complete : null;
|
||||
RED.nodes.createNode(this,n);
|
||||
this.name = n.name;
|
||||
this.complete = (n.complete||"payload").toString();
|
||||
this.complete = is_edit ? null : (n.complete||"payload").toString();
|
||||
if (this.complete === "false") { this.complete = "payload"; }
|
||||
this.console = ""+(n.console || false);
|
||||
this.tostatus = n.tostatus || false;
|
||||
@@ -43,8 +44,48 @@ module.exports = function(RED) {
|
||||
"50": "green",
|
||||
"60": "blue"
|
||||
};
|
||||
var edit = null;
|
||||
if (edit_exp) {
|
||||
try {
|
||||
edit = RED.util.prepareJSONataExpression(edit_exp, this);
|
||||
}
|
||||
catch (e) {
|
||||
node.error(RED._("debug.invalid-exp", {error: edit_exp}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function editValue(exp, val) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (exp) {
|
||||
RED.util.evaluateJSONataExpression(exp, val, (err, value) => {
|
||||
if (err) {
|
||||
reject(RED._("debug.invalid-exp", {error: edit_exp}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
resolve(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.on("input",function(msg) {
|
||||
function output_e(msg) {
|
||||
editValue(edit, msg).then(val => {
|
||||
if (this.console === "true") {
|
||||
node.log("\n"+util.inspect(val, {colors:useColors, depth:10}));
|
||||
}
|
||||
if (this.active && this.tosidebar) {
|
||||
sendDebug({id:node.id, name:node.name, topic:val.topic, msg:val, _path:val._path});
|
||||
}
|
||||
}).catch(err => {
|
||||
node.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
function output(msg) {
|
||||
if (this.complete === "true") {
|
||||
// debug complete msg object
|
||||
if (this.console === "true") {
|
||||
@@ -87,7 +128,9 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.on("input", (edit_exp ? output_e : output));
|
||||
}
|
||||
|
||||
RED.nodes.registerType("debug",DebugNode, {
|
||||
|
||||
@@ -14,164 +14,81 @@
|
||||
<div class="form-row node-input-link-row"></div>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#node-input-link-container {
|
||||
position: relative;
|
||||
}
|
||||
#node-input-link-container li {
|
||||
padding: 2px 5px;
|
||||
background: none;
|
||||
font-size: 0.8em;
|
||||
margin:0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#node-input-link-container li label {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#node-input-link-container li label input {
|
||||
vertical-align: top;
|
||||
width:15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
#node-input-link-container li:hover,
|
||||
#node-input-link-container li:hover .node-input-target-node-sublabel {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
.node-input-link-node-sublabel {
|
||||
position:absolute;
|
||||
right: 0px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
|
||||
function sortNodeList(nodeList,sortOn,sortOnSecond) {
|
||||
var currentSort = nodeList.data('currentSort');
|
||||
var currentSortOrder = nodeList.data('currentSortOrder');
|
||||
var treeList;
|
||||
|
||||
if (!currentSort) {
|
||||
currentSort = sortOn;
|
||||
currentSortOrder = 'a';
|
||||
} else {
|
||||
if (currentSort === sortOn) {
|
||||
currentSortOrder = (currentSortOrder === 'a'?'d':'a');
|
||||
} else {
|
||||
currentSortOrder = 'a';
|
||||
}
|
||||
currentSort = sortOn;
|
||||
}
|
||||
nodeList.data('currentSort',currentSort);
|
||||
nodeList.data('currentSortOrder',currentSortOrder);
|
||||
|
||||
$("#node-input-link-container-div .fa").hide();
|
||||
$(".node-input-link-sort-"+currentSort+"-"+currentSortOrder).show();
|
||||
|
||||
|
||||
var items = nodeList.find("li").get();
|
||||
items.sort(function(a,b) {
|
||||
var labelA = $(a).find(".node-input-link-node-"+currentSort).text().toLowerCase();
|
||||
var labelB = $(b).find(".node-input-link-node-"+currentSort).text().toLowerCase();
|
||||
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
|
||||
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
|
||||
|
||||
if (sortOnSecond) {
|
||||
labelA = $(a).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
|
||||
labelB = $(b).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
|
||||
if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
|
||||
if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
$.each(items, function(i, li) {
|
||||
nodeList.append(li);
|
||||
});
|
||||
}
|
||||
function onEditPrepare(node,targetType) {
|
||||
if (!node.links) {
|
||||
node.links = [];
|
||||
}
|
||||
node.oldLinks = [];
|
||||
|
||||
$('<div id="node-input-link-container-div" style="min-height: 100px;position: relative; box-sizing: border-box; border-radius: 2px; height: 180px; border: 1px solid #ccc;overflow:hidden; ">'+
|
||||
' <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
|
||||
' <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]node-red:link.label.sortByLabel"><span data-i18n="node-red:link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
|
||||
' <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]node-red:link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="node-red:link.label.type">flow</span></a></div>'+
|
||||
' </div>'+
|
||||
' <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
|
||||
' <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
|
||||
' </div>'+
|
||||
'</div>').appendTo('.node-input-link-row');
|
||||
var activeSubflow = RED.nodes.subflow(node.z);
|
||||
|
||||
var nodeList = $("#node-input-link-container");
|
||||
treeList = $("<div>")
|
||||
.css({width: "100%", height: "100%"})
|
||||
.appendTo(".node-input-link-row")
|
||||
.treeList({})
|
||||
.on('treelistitemmouseover',function(e,item) {
|
||||
if (item.node) {
|
||||
item.node.highlighted = true;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
})
|
||||
.on('treelistitemmouseout',function(e,item) {
|
||||
if (item.node) {
|
||||
item.node.highlighted = false;
|
||||
item.node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
var candidateNodes = RED.nodes.filterNodes({type:targetType});
|
||||
var inSubflow = !!RED.nodes.subflow(node.z);
|
||||
|
||||
var flows = [];
|
||||
var flowMap = {};
|
||||
|
||||
if (activeSubflow) {
|
||||
flowMap[activeSubflow.id] = {
|
||||
id: activeSubflow.id,
|
||||
class: 'palette-header',
|
||||
label: "Subflow : "+(activeSubflow.name || activeSubflow.id),
|
||||
expanded: true,
|
||||
children: []
|
||||
};
|
||||
flows.push(flowMap[activeSubflow.id])
|
||||
} else {
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
flowMap[ws.id] = {
|
||||
id: ws.id,
|
||||
class: 'palette-header',
|
||||
label: (ws.label || ws.id),
|
||||
expanded: ws.id === node.z,
|
||||
children: []
|
||||
}
|
||||
flows.push(flowMap[ws.id])
|
||||
})
|
||||
}
|
||||
|
||||
candidateNodes.forEach(function(n) {
|
||||
if (inSubflow) {
|
||||
if (n.z !== node.z) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!!RED.nodes.subflow(n.z)) {
|
||||
return;
|
||||
if (flowMap[n.z]) {
|
||||
var isChecked = false;
|
||||
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
|
||||
if (isChecked) {
|
||||
node.oldLinks.push(n.id);
|
||||
}
|
||||
flowMap[n.z].children.push({
|
||||
id: n.id,
|
||||
node: n,
|
||||
label: n.name||n.id,
|
||||
selected: isChecked
|
||||
})
|
||||
}
|
||||
var isChecked = false;
|
||||
|
||||
isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
|
||||
|
||||
if (isChecked) {
|
||||
node.oldLinks.push(n.id);
|
||||
}
|
||||
|
||||
var container = $('<li/>',{class:"node-input-link-node"});
|
||||
var row = $('<label/>',{for:"node-input-link-node-"+n.id}).appendTo(container);
|
||||
$('<input>',{type:"checkbox",class:"node-input-link-node-checkbox",id:"node-input-link-node-"+n.id})
|
||||
.data('node-id',n.id)
|
||||
.prop('checked', isChecked)
|
||||
.appendTo(row);
|
||||
container.on('mouseover',function(e) {
|
||||
n.highlighted = true;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
container.on('mouseout',function(e) {
|
||||
n.highlighted = false;
|
||||
n.dirty = true;
|
||||
RED.view.redraw();
|
||||
});
|
||||
var labelSpan = $('<span>');
|
||||
var label = n.name||n.id;
|
||||
var sublabel;
|
||||
var tab = RED.nodes.workspace(n.z);
|
||||
if (tab) {
|
||||
sublabel = tab.label||tab.id;
|
||||
} else {
|
||||
tab = RED.nodes.subflow(n.z);
|
||||
sublabel = "subflow : "+tab.name;
|
||||
}
|
||||
$('<span>',{class:"node-input-link-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
|
||||
if (sublabel) {
|
||||
$('<span>',{class:"node-input-link-node-sublabel"}).text(sublabel).appendTo(row);
|
||||
}
|
||||
container.appendTo(nodeList);
|
||||
});
|
||||
|
||||
sortNodeList(nodeList,'sublabel','label');
|
||||
|
||||
$("#node-input-link-sort-label").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList(nodeList,'label');
|
||||
});
|
||||
|
||||
$("#node-input-link-sort-type").click(function(e) {
|
||||
e.preventDefault();
|
||||
sortNodeList(nodeList,'sublabel');
|
||||
});
|
||||
flows = flows.filter(function(f) { return f.children.length > 0 })
|
||||
treeList.treeList('data',flows)
|
||||
}
|
||||
|
||||
function resizeNodeList() {
|
||||
@@ -182,16 +99,19 @@
|
||||
}
|
||||
var editorRow = $("#dialog-form>div.node-input-link-row");
|
||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||
$("#node-input-link-container-div").css("height",height+"px");
|
||||
$(".node-input-link-row").css("height",height+"px");
|
||||
}
|
||||
|
||||
function onEditSave(node) {
|
||||
var flows = treeList.treeList('data');
|
||||
node.links = [];
|
||||
$(".node-input-link-node-checkbox").each(function(n) {
|
||||
if ($(this).prop("checked")) {
|
||||
node.links.push($(this).data('node-id'));
|
||||
}
|
||||
});
|
||||
flows.forEach(function(f) {
|
||||
f.children.forEach(function(n) {
|
||||
if (n.selected) {
|
||||
node.links.push(n.id);
|
||||
}
|
||||
})
|
||||
})
|
||||
node.oldLinks.sort();
|
||||
node.links.sort();
|
||||
var nodeMap = {};
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
|
||||
<script type="text/x-red" data-template-name="comment">
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-comment"></i> <span data-i18n="comment.label.title"></span></label>
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
<input type="text" id="node-input-name">
|
||||
</div>
|
||||
<div class="form-row" style="margin-bottom: 0px;">
|
||||
<label for="node-input-info" style="width: 100% !important;"><i class="fa fa-comments"></i> <span data-i18n="comment.label.body"></span></label>
|
||||
<input type="hidden" id="node-input-info" autofocus="autofocus">
|
||||
</div>
|
||||
<div class="form-row node-text-editor-row">
|
||||
<input type="hidden" id="node-input-info" autofocus="autofocus">
|
||||
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-info-editor"></div>
|
||||
</div>
|
||||
<div class="form-tips" data-i18n="[html]comment.tip"></div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -32,7 +28,7 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
info: function() {
|
||||
return (this.name?"# "+this.name+"\n":"")+(this.info||"");
|
||||
return this.name?"# "+this.name+"\n\n---\n\n":"";
|
||||
},
|
||||
oneditprepare: function() {
|
||||
var that = this;
|
||||
|
||||
@@ -18,6 +18,23 @@ module.exports = function(RED) {
|
||||
"use strict";
|
||||
var ws = require("ws");
|
||||
var inspect = require("util").inspect;
|
||||
var url = require("url");
|
||||
|
||||
var serverUpgradeAdded = false;
|
||||
function handleServerUpgrade(request, socket, head) {
|
||||
const pathname = url.parse(request.url).pathname;
|
||||
if (listenerNodes.hasOwnProperty(pathname)) {
|
||||
listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done(ws) {
|
||||
listenerNodes[pathname].server.emit('connection', ws, request);
|
||||
});
|
||||
} else {
|
||||
// Don't destroy the socket as other listeners may want to handle the
|
||||
// event.
|
||||
}
|
||||
}
|
||||
var listenerNodes = {};
|
||||
var activeListenerNodes = 0;
|
||||
|
||||
|
||||
// A node red node that sets up a local websocket server
|
||||
function WebSocketListenerNode(n) {
|
||||
@@ -53,13 +70,22 @@ module.exports = function(RED) {
|
||||
|
||||
function handleConnection(/*socket*/socket) {
|
||||
var id = (1+Math.random()*4294967295).toString(16);
|
||||
if (node.isServer) { node._clients[id] = socket; node.emit('opened',Object.keys(node._clients).length); }
|
||||
if (node.isServer) {
|
||||
node._clients[id] = socket;
|
||||
node.emit('opened',Object.keys(node._clients).length);
|
||||
}
|
||||
socket.on('open',function() {
|
||||
if (!node.isServer) { node.emit('opened',''); }
|
||||
if (!node.isServer) {
|
||||
node.emit('opened','');
|
||||
}
|
||||
});
|
||||
socket.on('close',function() {
|
||||
if (node.isServer) { delete node._clients[id]; node.emit('closed',Object.keys(node._clients).length); }
|
||||
else { node.emit('closed'); }
|
||||
if (node.isServer) {
|
||||
delete node._clients[id];
|
||||
node.emit('closed',Object.keys(node._clients).length);
|
||||
} else {
|
||||
node.emit('closed');
|
||||
}
|
||||
if (!node.closing && !node.isServer) {
|
||||
clearTimeout(node.tout);
|
||||
node.tout = setTimeout(function() { startconn(); }, 3000); // try to reconnect every 3 secs... bit fast ?
|
||||
@@ -78,34 +104,29 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
if (node.isServer) {
|
||||
var path = RED.settings.httpNodeRoot || "/";
|
||||
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
|
||||
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Listen for 'newListener' events from RED.server
|
||||
node._serverListeners = {};
|
||||
|
||||
var storeListener = function(/*String*/event,/*function*/listener) {
|
||||
if (event == "error" || event == "upgrade" || event == "listening") {
|
||||
node._serverListeners[event] = listener;
|
||||
}
|
||||
activeListenerNodes++;
|
||||
if (!serverUpgradeAdded) {
|
||||
RED.server.on('upgrade', handleServerUpgrade);
|
||||
serverUpgradeAdded = true
|
||||
}
|
||||
|
||||
RED.server.addListener('newListener',storeListener);
|
||||
var path = RED.settings.httpNodeRoot || "/";
|
||||
path = path + (path.slice(-1) == "/" ? "":"/") + (node.path.charAt(0) == "/" ? node.path.substring(1) : node.path);
|
||||
node.fullPath = path;
|
||||
|
||||
if (listenerNodes.hasOwnProperty(path)) {
|
||||
node.error(RED._("websocket.errors.duplicate-path",{path: node.path}));
|
||||
return;
|
||||
}
|
||||
listenerNodes[node.fullPath] = node;
|
||||
var serverOptions = {
|
||||
server:RED.server,
|
||||
path:path
|
||||
noServer: true
|
||||
}
|
||||
if (RED.settings.webSocketNodeVerifyClient) {
|
||||
serverOptions.verifyClient = RED.settings.webSocketNodeVerifyClient;
|
||||
}
|
||||
// Create a WebSocket Server
|
||||
node.server = new ws.Server(serverOptions);
|
||||
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Stop listening for new listener events
|
||||
RED.server.removeListener('newListener',storeListener);
|
||||
node.server.setMaxListeners(0);
|
||||
node.server.on('connection', handleConnection);
|
||||
}
|
||||
@@ -115,21 +136,17 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
node.on("close", function() {
|
||||
// Workaround https://github.com/einaros/ws/pull/253
|
||||
// Remove listeners from RED.server
|
||||
if (node.isServer) {
|
||||
var listener = null;
|
||||
for (var event in node._serverListeners) {
|
||||
if (node._serverListeners.hasOwnProperty(event)) {
|
||||
listener = node._serverListeners[event];
|
||||
if (typeof listener === "function") {
|
||||
RED.server.removeListener(event,listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
node._serverListeners = {};
|
||||
delete listenerNodes[node.fullPath];
|
||||
node.server.close();
|
||||
node._inputNodes = [];
|
||||
activeListenerNodes--;
|
||||
if (activeListenerNodes === 0 && serverUpgradeAdded) {
|
||||
RED.server.removeListener('upgrade', handleServerUpgrade);
|
||||
serverUpgradeAdded = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else {
|
||||
node.closing = true;
|
||||
@@ -177,11 +194,12 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
WebSocketListenerNode.prototype.broadcast = function(data) {
|
||||
var i;
|
||||
try {
|
||||
if (this.isServer) {
|
||||
for (i = 0; i < this.server.clients.length; i++) {
|
||||
this.server.clients[i].send(data);
|
||||
for (let client in this._clients) {
|
||||
if (this._clients.hasOwnProperty(client)) {
|
||||
this._clients[client].send(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -215,8 +233,11 @@ module.exports = function(RED) {
|
||||
this.serverConfig.on('opened', function(n) { node.status({fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:n})}); });
|
||||
this.serverConfig.on('erro', function() { node.status({fill:"red",shape:"ring",text:"common.status.error"}); });
|
||||
this.serverConfig.on('closed', function(n) {
|
||||
if (n > 0) { node.status({fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:n})}); }
|
||||
else { node.status({fill:"red",shape:"ring",text:"common.status.disconnected"}); }
|
||||
if (n > 0) {
|
||||
node.status({fill:"green",shape:"dot",text:RED._("websocket.status.connected",{count:n})});
|
||||
} else {
|
||||
node.status({fill:"red",shape:"ring",text:"common.status.disconnected"});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.error(RED._("websocket.errors.missing-conf"));
|
||||
|
||||
@@ -91,206 +91,117 @@ module.exports = function(RED) {
|
||||
return _maxKeptCount;
|
||||
}
|
||||
|
||||
function getProperty(node,msg) {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (node.propertyType === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
function getProperty(node,msg,done) {
|
||||
if (node.propertyType === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(node.property,msg,(err,value) => {
|
||||
if (err) {
|
||||
done(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
done(undefined,value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (node.propertyType === 'jsonata') {
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(node.property,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg,(err,value) => {
|
||||
if (err) {
|
||||
done(undefined,undefined);
|
||||
} else {
|
||||
done(undefined,value);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getV1(node,msg,rule,hasParts) {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise( (resolve,reject) => {
|
||||
if (rule.vt === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
} else if (rule.vt === 'jsonata') {
|
||||
var exp = rule.v;
|
||||
if (rule.t === 'jsonata_exp') {
|
||||
if (hasParts) {
|
||||
exp.assign("I", msg.parts.index);
|
||||
exp.assign("N", msg.parts.count);
|
||||
}
|
||||
}
|
||||
RED.util.evaluateJSONataExpression(exp,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else if (rule.vt === 'json') {
|
||||
resolve("json"); // TODO: ?! invalid case
|
||||
} else if (rule.vt === 'null') {
|
||||
resolve("null");
|
||||
function getV1(node,msg,rule,hasParts,done) {
|
||||
if (rule.vt === 'prev') {
|
||||
return done(undefined,node.previousValue);
|
||||
} else if (rule.vt === 'jsonata') {
|
||||
var exp = rule.v;
|
||||
if (rule.t === 'jsonata_exp') {
|
||||
if (hasParts) {
|
||||
exp.assign("I", msg.parts.index);
|
||||
exp.assign("N", msg.parts.count);
|
||||
}
|
||||
}
|
||||
RED.util.evaluateJSONataExpression(exp,msg,(err,value) => {
|
||||
if (err) {
|
||||
done(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
done(undefined, value);
|
||||
}
|
||||
});
|
||||
} else if (rule.vt === 'json') {
|
||||
done(undefined,"json"); // TODO: ?! invalid case
|
||||
} else if (rule.vt === 'null') {
|
||||
done(undefined,"null");
|
||||
} else {
|
||||
RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
done(undefined, undefined);
|
||||
} else {
|
||||
done(undefined, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getV2(node,msg,rule,done) {
|
||||
var v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
return done(undefined,node.previousValue);
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => {
|
||||
if (err) {
|
||||
done(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
done(undefined,value);
|
||||
}
|
||||
});
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
done(undefined,undefined);
|
||||
} else {
|
||||
done(undefined,value);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (rule.vt === 'prev') {
|
||||
return node.previousValue;
|
||||
} else if (rule.vt === 'jsonata') {
|
||||
var exp = rule.v;
|
||||
if (rule.t === 'jsonata_exp') {
|
||||
if (hasParts) {
|
||||
exp.assign("I", msg.parts.index);
|
||||
exp.assign("N", msg.parts.count);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(exp,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
}
|
||||
} else if (rule.vt === 'json') {
|
||||
return "json"; // TODO: ?! invalid case
|
||||
} else if (rule.vt === 'null') {
|
||||
return "null";
|
||||
} else {
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
done(undefined,v2);
|
||||
}
|
||||
}
|
||||
|
||||
function getV2(node,msg,rule) {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
var v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
resolve(node.previousValue);
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
RED.util.evaluateJSONataExpression(rule.v2,msg,(err,value) => {
|
||||
if (err) {
|
||||
reject(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg, function(err,value) {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
function applyRule(node, msg, property, state, done) {
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1,v2;
|
||||
|
||||
getV1(node,msg,rule,state.hasParts, (err,value) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
v1 = value;
|
||||
getV2(node,msg,rule, (err,value) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
v2 = value;
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return done(undefined,false);
|
||||
}
|
||||
} else {
|
||||
resolve(v2);
|
||||
state.onward.push(null);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var v2 = rule.v2;
|
||||
if (rule.v2t === 'prev') {
|
||||
return node.previousValue;
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
try {
|
||||
return RED.util.evaluateJSONataExpression(rule.v2,msg);
|
||||
} catch(err) {
|
||||
throw new Error(RED._("switch.errors.invalid-expr",{error:err.message}))
|
||||
}
|
||||
} else if (typeof v2 !== 'undefined') {
|
||||
try {
|
||||
return RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg);
|
||||
} catch(err) {
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return v2;
|
||||
}
|
||||
}
|
||||
done(undefined, state.currentRule < node.rules.length - 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function applyRule(node, msg, property, state) {
|
||||
if (node.useAsyncRules) {
|
||||
return new Promise((resolve,reject) => {
|
||||
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1,v2;
|
||||
|
||||
getV1(node,msg,rule,state.hasParts).then(value => {
|
||||
v1 = value;
|
||||
}).then(()=>getV2(node,msg,rule)).then(value => {
|
||||
v2 = value;
|
||||
}).then(() => {
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return resolve(false);
|
||||
}
|
||||
} else {
|
||||
state.onward.push(null);
|
||||
}
|
||||
resolve(state.currentRule < node.rules.length - 1);
|
||||
});
|
||||
})
|
||||
} else {
|
||||
var rule = node.rules[state.currentRule];
|
||||
var v1 = getV1(node,msg,rule,state.hasParts);
|
||||
var v2 = getV2(node,msg,rule);
|
||||
if (rule.t == "else") {
|
||||
property = state.elseflag;
|
||||
state.elseflag = true;
|
||||
}
|
||||
if (operators[rule.t](property,v1,v2,rule.case,msg.parts)) {
|
||||
state.onward.push(msg);
|
||||
state.elseflag = false;
|
||||
if (node.checkall == "false") {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
state.onward.push(null);
|
||||
}
|
||||
return state.currentRule < node.rules.length - 1
|
||||
}
|
||||
}
|
||||
|
||||
function applyRules(node, msg, property,state) {
|
||||
function applyRules(node, msg, property,state,done) {
|
||||
if (!state) {
|
||||
state = {
|
||||
currentRule: 0,
|
||||
@@ -301,26 +212,18 @@ module.exports = function(RED) {
|
||||
msg.parts.hasOwnProperty("index")
|
||||
}
|
||||
}
|
||||
if (node.useAsyncRules) {
|
||||
return applyRule(node,msg,property,state).then(hasMore => {
|
||||
if (hasMore) {
|
||||
state.currentRule++;
|
||||
return applyRules(node,msg,property,state);
|
||||
} else {
|
||||
node.previousValue = property;
|
||||
return state.onward;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var hasMore = applyRule(node,msg,property,state);
|
||||
applyRule(node,msg,property,state,(err,hasMore) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (hasMore) {
|
||||
state.currentRule++;
|
||||
return applyRules(node,msg,property,state);
|
||||
applyRules(node,msg,property,state,done);
|
||||
} else {
|
||||
node.previousValue = property;
|
||||
return state.onward;
|
||||
done(undefined,state.onward);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -345,13 +248,6 @@ module.exports = function(RED) {
|
||||
var valid = true;
|
||||
var repair = n.repair;
|
||||
var needsCount = repair;
|
||||
this.useAsyncRules = (
|
||||
this.propertyType === 'flow' ||
|
||||
this.propertyType === 'global' || (
|
||||
this.propertyType === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(this.property)
|
||||
)
|
||||
);
|
||||
|
||||
for (var i=0; i<this.rules.length; i+=1) {
|
||||
var rule = this.rules[i];
|
||||
@@ -363,13 +259,6 @@ module.exports = function(RED) {
|
||||
rule.vt = 'str';
|
||||
}
|
||||
}
|
||||
this.useAsyncRules = this.useAsyncRules || (
|
||||
rule.vt === 'flow' ||
|
||||
rule.vt === 'global' || (
|
||||
rule.vt === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(rule.v)
|
||||
)
|
||||
);
|
||||
if (rule.vt === 'num') {
|
||||
if (!isNaN(Number(rule.v))) {
|
||||
rule.v = Number(rule.v);
|
||||
@@ -382,9 +271,6 @@ module.exports = function(RED) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (rule.vt === 'flow' || rule.vt === 'global' || rule.vt === 'jsonata') {
|
||||
this.useAsyncRules = true;
|
||||
}
|
||||
if (typeof rule.v2 !== 'undefined') {
|
||||
if (!rule.v2t) {
|
||||
if (!isNaN(Number(rule.v2))) {
|
||||
@@ -393,13 +279,6 @@ module.exports = function(RED) {
|
||||
rule.v2t = 'str';
|
||||
}
|
||||
}
|
||||
this.useAsyncRules = this.useAsyncRules || (
|
||||
rule.v2t === 'flow' ||
|
||||
rule.v2t === 'global' || (
|
||||
rule.v2t === 'jsonata' &&
|
||||
/\$(flow|global)Context/.test(rule.v2)
|
||||
)
|
||||
);
|
||||
if (rule.v2t === 'num') {
|
||||
rule.v2 = Number(rule.v2);
|
||||
} else if (rule.v2t === 'jsonata') {
|
||||
@@ -444,26 +323,38 @@ module.exports = function(RED) {
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
function addMessageToPending(msg) {
|
||||
function drainMessageGroup(msgs,count,done) {
|
||||
var msg = msgs.shift();
|
||||
msg.parts.count = count;
|
||||
processMessage(msg,false, err => {
|
||||
if (err) {
|
||||
done(err);
|
||||
} else {
|
||||
if (msgs.length === 0) {
|
||||
done()
|
||||
} else {
|
||||
drainMessageGroup(msgs,count,done);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function addMessageToPending(msg,done) {
|
||||
var parts = msg.parts;
|
||||
// We've already checked the msg.parts has the require bits
|
||||
var group = addMessageToGroup(parts.id, msg, parts);
|
||||
var msgs = group.msgs;
|
||||
var count = group.count;
|
||||
if (count === msgs.length) {
|
||||
var msgsCount = msgs.length;
|
||||
if (count === msgsCount) {
|
||||
// We have a complete group - send the individual parts
|
||||
return msgs.reduce((promise, msg) => {
|
||||
return promise.then((result) => {
|
||||
msg.parts.count = count;
|
||||
return processMessage(msg, false);
|
||||
})
|
||||
}, Promise.resolve()).then( () => {
|
||||
pendingCount -= group.msgs.length;
|
||||
drainMessageGroup(msgs,count,err => {
|
||||
pendingCount -= msgsCount;
|
||||
delete pendingIn[parts.id];
|
||||
});
|
||||
done();
|
||||
})
|
||||
return;
|
||||
}
|
||||
return Promise.resolve();
|
||||
done();
|
||||
}
|
||||
|
||||
function sendGroup(onwards, port_count) {
|
||||
@@ -529,43 +420,33 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function processMessage(msg, checkParts) {
|
||||
function processMessage(msg, checkParts, done) {
|
||||
var hasParts = msg.hasOwnProperty("parts") &&
|
||||
msg.parts.hasOwnProperty("id") &&
|
||||
msg.parts.hasOwnProperty("index");
|
||||
|
||||
if (needsCount && checkParts && hasParts) {
|
||||
return addMessageToPending(msg);
|
||||
}
|
||||
if (node.useAsyncRules) {
|
||||
return getProperty(node,msg)
|
||||
.then(property => applyRules(node,msg,property))
|
||||
.then(onward => {
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
}
|
||||
else {
|
||||
sendGroupMessages(onward, msg);
|
||||
}
|
||||
}).catch(err => {
|
||||
node.warn(err);
|
||||
});
|
||||
addMessageToPending(msg,done);
|
||||
} else {
|
||||
try {
|
||||
var property = getProperty(node,msg);
|
||||
var onward = applyRules(node,msg,property);
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
getProperty(node,msg,(err,property) => {
|
||||
if (err) {
|
||||
node.warn(err);
|
||||
done();
|
||||
} else {
|
||||
sendGroupMessages(onward, msg);
|
||||
applyRules(node,msg,property,undefined,(err,onward) => {
|
||||
if (err) {
|
||||
node.warn(err);
|
||||
} else {
|
||||
if (!repair || !hasParts) {
|
||||
node.send(onward);
|
||||
} else {
|
||||
sendGroupMessages(onward, msg);
|
||||
}
|
||||
}
|
||||
done();
|
||||
});
|
||||
}
|
||||
} catch(err) {
|
||||
node.warn(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,12 +459,13 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
var pendingMessages = [];
|
||||
var activeMessagePromise = null;
|
||||
var handlingMessage = false;
|
||||
var processMessageQueue = function(msg) {
|
||||
if (msg) {
|
||||
|
||||
// A new message has arrived - add it to the message queue
|
||||
pendingMessages.push(msg);
|
||||
if (activeMessagePromise !== null) {
|
||||
if (handlingMessage) {
|
||||
// The node is currently processing a message, so do nothing
|
||||
// more with this message
|
||||
return;
|
||||
@@ -592,27 +474,24 @@ module.exports = function(RED) {
|
||||
if (pendingMessages.length === 0) {
|
||||
// There are no more messages to process, clear the active flag
|
||||
// and return
|
||||
activeMessagePromise = null;
|
||||
handlingMessage = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// There are more messages to process. Get the next message and
|
||||
// start processing it. Recurse back in to check for any more
|
||||
var nextMsg = pendingMessages.shift();
|
||||
activeMessagePromise = processMessage(nextMsg,true)
|
||||
.then(processMessageQueue)
|
||||
.catch((err) => {
|
||||
handlingMessage = true;
|
||||
processMessage(nextMsg,true,err => {
|
||||
if (err) {
|
||||
node.error(err,nextMsg);
|
||||
return processMessageQueue();
|
||||
});
|
||||
}
|
||||
processMessageQueue()
|
||||
});
|
||||
}
|
||||
|
||||
this.on('input', function(msg) {
|
||||
if (node.useAsyncRules) {
|
||||
processMessageQueue(msg);
|
||||
} else {
|
||||
processMessage(msg,true);
|
||||
}
|
||||
processMessageQueue(msg);
|
||||
});
|
||||
|
||||
this.on('close', function() {
|
||||
|
||||
@@ -98,7 +98,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
}
|
||||
|
||||
function getToValue(msg,rule) {
|
||||
function getToValue(msg,rule,done) {
|
||||
var value = rule.to;
|
||||
if (rule.tot === 'json') {
|
||||
value = JSON.parse(rule.to);
|
||||
@@ -107,222 +107,235 @@ module.exports = function(RED) {
|
||||
}
|
||||
if (rule.tot === "msg") {
|
||||
value = RED.util.getMessageProperty(msg,rule.to);
|
||||
} else if ((rule.tot === 'flow') ||
|
||||
(rule.tot === 'global')) {
|
||||
return new Promise((resolve,reject) => {
|
||||
RED.util.evaluateNodeProperty(rule.to, rule.tot, node, msg, (err,value) => {
|
||||
if (err) {
|
||||
resolve(undefined);
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
} else if ((rule.tot === 'flow') || (rule.tot === 'global')) {
|
||||
RED.util.evaluateNodeProperty(rule.to, rule.tot, node, msg, (err,value) => {
|
||||
if (err) {
|
||||
done(undefined,undefined);
|
||||
} else {
|
||||
done(undefined,value);
|
||||
}
|
||||
});
|
||||
return
|
||||
} else if (rule.tot === 'date') {
|
||||
value = Date.now();
|
||||
} else if (rule.tot === 'jsonata') {
|
||||
return new Promise((resolve,reject) => {
|
||||
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
|
||||
if (err) {
|
||||
reject(RED._("change.errors.invalid-expr",{error:err.message}))
|
||||
} else {
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
|
||||
if (err) {
|
||||
done(RED._("change.errors.invalid-expr",{error:err.message}))
|
||||
} else {
|
||||
done(undefined, value);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
return Promise.resolve(value);
|
||||
done(undefined,value);
|
||||
}
|
||||
function getFromValue(msg,rule) {
|
||||
|
||||
function getFromValueType(fromValue, done) {
|
||||
var fromType;
|
||||
var fromRE;
|
||||
if (typeof fromValue === 'number' || fromValue instanceof Number) {
|
||||
fromType = 'num';
|
||||
} else if (typeof fromValue === 'boolean') {
|
||||
fromType = 'bool'
|
||||
} else if (fromValue instanceof RegExp) {
|
||||
fromType = 're';
|
||||
fromRE = fromValue;
|
||||
} else if (typeof fromValue === 'string') {
|
||||
fromType = 'str';
|
||||
fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
try {
|
||||
fromRE = new RegExp(fromRE, "g");
|
||||
} catch (e) {
|
||||
done(new Error(RED._("change.errors.invalid-from",{error:e.message})));
|
||||
}
|
||||
} else {
|
||||
done(new Error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)})));
|
||||
}
|
||||
done(undefined,{
|
||||
fromType,
|
||||
fromValue,
|
||||
fromRE
|
||||
});
|
||||
}
|
||||
function getFromValue(msg,rule, done) {
|
||||
var fromValue;
|
||||
var fromType;
|
||||
var fromRE;
|
||||
if (rule.t === 'change') {
|
||||
if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') {
|
||||
return new Promise((resolve,reject) => {
|
||||
if (rule.fromt === "msg") {
|
||||
resolve(RED.util.getMessageProperty(msg,rule.from));
|
||||
} else if (rule.fromt === 'flow' || rule.fromt === 'global') {
|
||||
var contextKey = RED.util.parseContextStore(rule.from);
|
||||
node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(fromValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(fromValue => {
|
||||
if (typeof fromValue === 'number' || fromValue instanceof Number) {
|
||||
fromType = 'num';
|
||||
} else if (typeof fromValue === 'boolean') {
|
||||
fromType = 'bool'
|
||||
} else if (fromValue instanceof RegExp) {
|
||||
fromType = 're';
|
||||
fromRE = fromValue;
|
||||
} else if (typeof fromValue === 'string') {
|
||||
fromType = 'str';
|
||||
fromRE = fromValue.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
try {
|
||||
fromRE = new RegExp(fromRE, "g");
|
||||
} catch (e) {
|
||||
throw new Error(RED._("change.errors.invalid-from",{error:e.message}));
|
||||
if (rule.fromt === "msg") {
|
||||
return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done);
|
||||
} else if (rule.fromt === 'flow' || rule.fromt === 'global') {
|
||||
var contextKey = RED.util.parseContextStore(rule.from);
|
||||
node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => {
|
||||
if (err) {
|
||||
done(err)
|
||||
} else {
|
||||
getFromValueType(fromValue,done);
|
||||
}
|
||||
} else {
|
||||
throw new Error(RED._("change.errors.invalid-from",{error:"unsupported type: "+(typeof fromValue)}));
|
||||
}
|
||||
return {
|
||||
fromType,
|
||||
fromValue,
|
||||
fromRE
|
||||
}
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
fromType = rule.fromt;
|
||||
fromValue = rule.from;
|
||||
fromRE = rule.fromRE;
|
||||
}
|
||||
}
|
||||
return Promise.resolve({
|
||||
done(undefined, {
|
||||
fromType,
|
||||
fromValue,
|
||||
fromRE
|
||||
});
|
||||
}
|
||||
function applyRule(msg,rule) {
|
||||
var property = rule.p;
|
||||
var current;
|
||||
var fromValue;
|
||||
var fromType;
|
||||
var fromRE;
|
||||
try {
|
||||
return getToValue(msg,rule).then(value => {
|
||||
return getFromValue(msg,rule).then(fromParts => {
|
||||
fromValue = fromParts.fromValue;
|
||||
fromType = fromParts.fromType;
|
||||
fromRE = fromParts.fromRE;
|
||||
if (rule.pt === 'msg') {
|
||||
try {
|
||||
if (rule.t === 'delete') {
|
||||
RED.util.setMessageProperty(msg,property,undefined);
|
||||
} else if (rule.t === 'set') {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
} else if (rule.t === 'change') {
|
||||
current = RED.util.getMessageProperty(msg,property);
|
||||
if (typeof current === 'string') {
|
||||
if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
|
||||
// str representation of exact from number/boolean
|
||||
// only replace if they match exactly
|
||||
function applyRule(msg,rule,done) {
|
||||
var property = rule.p;
|
||||
var current;
|
||||
var fromValue;
|
||||
var fromType;
|
||||
var fromRE;
|
||||
|
||||
try {
|
||||
getToValue(msg,rule,(err,value) => {
|
||||
if (err) {
|
||||
node.error(err, msg);
|
||||
return done(undefined,null);
|
||||
} else {
|
||||
getFromValue(msg,rule,(err,fromParts) => {
|
||||
if (err) {
|
||||
node.error(err, msg);
|
||||
return done(undefined,null);
|
||||
} else {
|
||||
fromValue = fromParts.fromValue;
|
||||
fromType = fromParts.fromType;
|
||||
fromRE = fromParts.fromRE;
|
||||
if (rule.pt === 'msg') {
|
||||
try {
|
||||
if (rule.t === 'delete') {
|
||||
RED.util.setMessageProperty(msg,property,undefined);
|
||||
} else if (rule.t === 'set') {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
} else {
|
||||
current = current.replace(fromRE,value);
|
||||
RED.util.setMessageProperty(msg,property,current);
|
||||
} else if (rule.t === 'change') {
|
||||
current = RED.util.getMessageProperty(msg,property);
|
||||
if (typeof current === 'string') {
|
||||
if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
|
||||
// str representation of exact from number/boolean
|
||||
// only replace if they match exactly
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
} else {
|
||||
current = current.replace(fromRE,value);
|
||||
RED.util.setMessageProperty(msg,property,current);
|
||||
}
|
||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||
if (current == Number(fromValue)) {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
}
|
||||
} else if (typeof current === 'boolean' && fromType === 'bool') {
|
||||
if (current.toString() === fromValue) {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||
if (current == Number(fromValue)) {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
}
|
||||
} else if (typeof current === 'boolean' && fromType === 'bool') {
|
||||
if (current.toString() === fromValue) {
|
||||
RED.util.setMessageProperty(msg,property,value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(err) {}
|
||||
return msg;
|
||||
} else if (rule.pt === 'flow' || rule.pt === 'global') {
|
||||
var contextKey = RED.util.parseContextStore(property);
|
||||
return new Promise((resolve,reject) => {
|
||||
var target = node.context()[rule.pt];
|
||||
var callback = err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(msg);
|
||||
}
|
||||
}
|
||||
if (rule.t === 'delete') {
|
||||
target.set(contextKey.key,undefined,contextKey.store,callback);
|
||||
} else if (rule.t === 'set') {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
} else if (rule.t === 'change') {
|
||||
target.get(contextKey.key,contextKey.store,(err,current) => {
|
||||
} catch(err) {}
|
||||
return done(undefined,msg);
|
||||
} else if (rule.pt === 'flow' || rule.pt === 'global') {
|
||||
var contextKey = RED.util.parseContextStore(property);
|
||||
var target = node.context()[rule.pt];
|
||||
var callback = err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
node.error(err, msg);
|
||||
return done(undefined,null);
|
||||
} else {
|
||||
done(undefined,msg);
|
||||
}
|
||||
if (typeof current === 'string') {
|
||||
if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
|
||||
// str representation of exact from number/boolean
|
||||
// only replace if they match exactly
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
} else {
|
||||
current = current.replace(fromRE,value);
|
||||
target.set(contextKey.key,current,contextKey.store,callback);
|
||||
}
|
||||
if (rule.t === 'delete') {
|
||||
target.set(contextKey.key,undefined,contextKey.store,callback);
|
||||
} else if (rule.t === 'set') {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
} else if (rule.t === 'change') {
|
||||
target.get(contextKey.key,contextKey.store,(err,current) => {
|
||||
if (err) {
|
||||
node.error(err, msg);
|
||||
return done(undefined,null);
|
||||
}
|
||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||
if (current == Number(fromValue)) {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
if (typeof current === 'string') {
|
||||
if ((fromType === 'num' || fromType === 'bool' || fromType === 'str') && current === fromValue) {
|
||||
// str representation of exact from number/boolean
|
||||
// only replace if they match exactly
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
} else {
|
||||
current = current.replace(fromRE,value);
|
||||
target.set(contextKey.key,current,contextKey.store,callback);
|
||||
}
|
||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||
if (current == Number(fromValue)) {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
}
|
||||
} else if (typeof current === 'boolean' && fromType === 'bool') {
|
||||
if (current.toString() === fromValue) {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
}
|
||||
}
|
||||
} else if (typeof current === 'boolean' && fromType === 'bool') {
|
||||
if (current.toString() === fromValue) {
|
||||
target.set(contextKey.key,value,contextKey.store,callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}).catch(err => {
|
||||
node.error(err, msg);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
return Promise.resolve(msg);
|
||||
// This is an okay error
|
||||
done(undefined,msg);
|
||||
}
|
||||
}
|
||||
function applyRules(msg, currentRule) {
|
||||
function completeApplyingRules(msg,currentRule,done) {
|
||||
if (!msg) {
|
||||
return done();
|
||||
} else if (currentRule === node.rules.length - 1) {
|
||||
return done(undefined, msg);
|
||||
} else {
|
||||
applyRules(msg, currentRule+1,done);
|
||||
}
|
||||
}
|
||||
function applyRules(msg, currentRule, done) {
|
||||
if (currentRule >= node.rules.length) {
|
||||
return Promise.resolve(msg);
|
||||
return done(undefined,msg);
|
||||
}
|
||||
var r = node.rules[currentRule];
|
||||
var rulePromise;
|
||||
if (r.t === "move") {
|
||||
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) {
|
||||
rulePromise = applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt}).then(
|
||||
msg => applyRule(msg,{t:"delete", p:r.p, pt:r.pt})
|
||||
);
|
||||
}
|
||||
else { // 2 step move if we are moving from a child
|
||||
rulePromise = applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt}).then(
|
||||
msg => applyRule(msg,{t:"delete", p:r.p, pt:r.pt})
|
||||
).then(
|
||||
msg => applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt})
|
||||
).then(
|
||||
msg => applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt})
|
||||
)
|
||||
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => {
|
||||
applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => {
|
||||
completeApplyingRules(msg,currentRule,done);
|
||||
})
|
||||
});
|
||||
} else { // 2 step move if we are moving from a child
|
||||
applyRule(msg,{t:"set", p:"_temp_move", pt:r.tot, to:r.p, tot:r.pt},(err,msg)=> {
|
||||
applyRule(msg,{t:"delete", p:r.p, pt:r.pt},(err,msg)=> {
|
||||
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:"_temp_move", tot:r.pt},(err,msg)=> {
|
||||
applyRule(msg,{t:"delete", p:"_temp_move", pt:r.pt},(err,msg)=> {
|
||||
completeApplyingRules(msg,currentRule,done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
rulePromise = applyRule(msg,r);
|
||||
applyRule(msg,r,(err,msg)=> { completeApplyingRules(msg,currentRule,done); });
|
||||
}
|
||||
return rulePromise.then(
|
||||
msg => {
|
||||
if (!msg) {
|
||||
return
|
||||
} else if (currentRule === node.rules.length - 1) {
|
||||
return msg;
|
||||
} else {
|
||||
return applyRules(msg, currentRule+1);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
this.on('input', function(msg) {
|
||||
applyRules(msg, 0)
|
||||
.then( msg => { if (msg) { node.send(msg) }} )
|
||||
.catch( err => node.error(err, msg))
|
||||
applyRules(msg, 0, (err,msg) => {
|
||||
if (err) {
|
||||
node.error(err,msg);
|
||||
} else if (msg) {
|
||||
node.send(msg);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,48 +233,26 @@ module.exports = function(RED) {
|
||||
RED.nodes.registerType("split",SplitNode);
|
||||
|
||||
|
||||
var _max_kept_msgs_count;
|
||||
var _maxKeptMsgsCount;
|
||||
|
||||
function max_kept_msgs_count(node) {
|
||||
if (_max_kept_msgs_count === undefined) {
|
||||
function maxKeptMsgsCount(node) {
|
||||
if (_maxKeptMsgsCount === undefined) {
|
||||
var name = "nodeMessageBufferMaxLength";
|
||||
if (RED.settings.hasOwnProperty(name)) {
|
||||
_max_kept_msgs_count = RED.settings[name];
|
||||
_maxKeptMsgsCount = RED.settings[name];
|
||||
}
|
||||
else {
|
||||
_max_kept_msgs_count = 0;
|
||||
_maxKeptMsgsCount = 0;
|
||||
}
|
||||
}
|
||||
return _max_kept_msgs_count;
|
||||
return _maxKeptMsgsCount;
|
||||
}
|
||||
|
||||
function apply_r(exp, accum, msg, index, count) {
|
||||
function applyReduce(exp, accum, msg, index, count, done) {
|
||||
exp.assign("I", index);
|
||||
exp.assign("N", count);
|
||||
exp.assign("A", accum);
|
||||
return new Promise((resolve,reject) => {
|
||||
RED.util.evaluateJSONataExpression(exp, msg, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function apply_f(exp, accum, count) {
|
||||
exp.assign("N", count);
|
||||
exp.assign("A", accum);
|
||||
return new Promise((resolve,reject) => {
|
||||
return RED.util.evaluateJSONataExpression(exp, {}, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
RED.util.evaluateJSONataExpression(exp, msg, done);
|
||||
}
|
||||
|
||||
function exp_or_undefined(exp) {
|
||||
@@ -285,39 +263,68 @@ module.exports = function(RED) {
|
||||
return exp
|
||||
}
|
||||
|
||||
function reduceAndSendGroup(node, group) {
|
||||
|
||||
function reduceMessageGroup(node,msgs,exp,fixup,count,accumulator,done) {
|
||||
var msg = msgs.shift();
|
||||
exp.assign("I", msg.parts.index);
|
||||
exp.assign("N", count);
|
||||
exp.assign("A", accumulator);
|
||||
RED.util.evaluateJSONataExpression(exp, msg, (err,result) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (msgs.length === 0) {
|
||||
if (fixup) {
|
||||
fixup.assign("N", count);
|
||||
fixup.assign("A", result);
|
||||
RED.util.evaluateJSONataExpression(fixup, {}, (err, result) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
node.send({payload: result});
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
node.send({payload: result});
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
reduceMessageGroup(node,msgs,exp,fixup,count,result,done);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
function reduceAndSendGroup(node, group, done) {
|
||||
var is_right = node.reduce_right;
|
||||
var flag = is_right ? -1 : 1;
|
||||
var msgs = group.msgs;
|
||||
return getInitialReduceValue(node, node.exp_init, node.exp_init_type).then(accum => {
|
||||
var reduce_exp = node.reduce_exp;
|
||||
var reduce_fixup = node.reduce_fixup;
|
||||
var count = group.count;
|
||||
msgs.sort(function(x,y) {
|
||||
var ix = x.parts.index;
|
||||
var iy = y.parts.index;
|
||||
if (ix < iy) {return -flag;}
|
||||
if (ix > iy) {return flag;}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return msgs.reduce((promise, msg) => promise.then(accum => apply_r(reduce_exp, accum, msg, msg.parts.index, count)), Promise.resolve(accum))
|
||||
.then(accum => {
|
||||
if(reduce_fixup !== undefined) {
|
||||
return apply_f(reduce_fixup, accum, count).then(accum => {
|
||||
node.send({payload: accum});
|
||||
});
|
||||
} else {
|
||||
node.send({payload: accum});
|
||||
}
|
||||
try {
|
||||
RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => {
|
||||
var reduceExpression = node.reduceExpression;
|
||||
var fixupExpression = node.fixupExpression;
|
||||
var count = group.count;
|
||||
msgs.sort(function(x,y) {
|
||||
var ix = x.parts.index;
|
||||
var iy = y.parts.index;
|
||||
if (ix < iy) {return -flag;}
|
||||
if (ix > iy) {return flag;}
|
||||
return 0;
|
||||
});
|
||||
}).catch(err => {
|
||||
throw new Error(RED._("join.errors.invalid-expr",{error:err.message}));
|
||||
});
|
||||
reduceMessageGroup(node, msgs,reduceExpression,fixupExpression,count,accum,(err,result) => {
|
||||
if (err) {
|
||||
done(err);
|
||||
return;
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
} catch(err) {
|
||||
done(new Error(RED._("join.errors.invalid-expr",{error:err.message})));
|
||||
}
|
||||
}
|
||||
|
||||
function reduce_msg(node, msg) {
|
||||
var promise;
|
||||
function reduceMessage(node, msg, done) {
|
||||
if (msg.hasOwnProperty('parts')) {
|
||||
var parts = msg.parts;
|
||||
var pending = node.pending;
|
||||
@@ -335,51 +342,37 @@ module.exports = function(RED) {
|
||||
}
|
||||
var group = pending[gid];
|
||||
var msgs = group.msgs;
|
||||
if(parts.hasOwnProperty('count') && (group.count === undefined)) {
|
||||
if (parts.hasOwnProperty('count') && (group.count === undefined)) {
|
||||
group.count = parts.count;
|
||||
}
|
||||
msgs.push(msg);
|
||||
pending_count++;
|
||||
var completeProcess = function() {
|
||||
var completeProcess = function(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
node.pending_count = pending_count;
|
||||
var max_msgs = max_kept_msgs_count(node);
|
||||
var max_msgs = maxKeptMsgsCount(node);
|
||||
if ((max_msgs > 0) && (pending_count > max_msgs)) {
|
||||
node.pending = {};
|
||||
node.pending_count = 0;
|
||||
var promise = Promise.reject(RED._("join.too-many"));
|
||||
promise.catch(()=>{});
|
||||
return promise;
|
||||
done(RED._("join.too-many"));
|
||||
return;
|
||||
}
|
||||
return Promise.resolve();
|
||||
return done();
|
||||
}
|
||||
if(msgs.length === group.count) {
|
||||
|
||||
if (msgs.length === group.count) {
|
||||
delete pending[gid];
|
||||
pending_count -= msgs.length;
|
||||
promise = reduceAndSendGroup(node, group).then(completeProcess);
|
||||
reduceAndSendGroup(node, group, completeProcess)
|
||||
} else {
|
||||
promise = completeProcess();
|
||||
completeProcess();
|
||||
}
|
||||
} else {
|
||||
node.send(msg);
|
||||
done();
|
||||
}
|
||||
if (!promise) {
|
||||
promise = Promise.resolve();
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
|
||||
function getInitialReduceValue(node, exp, exp_type) {
|
||||
return new Promise((resolve, reject) => {
|
||||
RED.util.evaluateNodeProperty(exp, exp_type, node, {},
|
||||
(err, result) => {
|
||||
if(err) {
|
||||
return reject(err);
|
||||
}
|
||||
else {
|
||||
return resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function JoinNode(n) {
|
||||
@@ -404,8 +397,8 @@ module.exports = function(RED) {
|
||||
var exp_fixup = exp_or_undefined(n.reduceFixup);
|
||||
this.reduce_right = n.reduceRight;
|
||||
try {
|
||||
this.reduce_exp = RED.util.prepareJSONataExpression(exp_reduce, this);
|
||||
this.reduce_fixup = (exp_fixup !== undefined) ? RED.util.prepareJSONataExpression(exp_fixup, this) : undefined;
|
||||
this.reduceExpression = RED.util.prepareJSONataExpression(exp_reduce, this);
|
||||
this.fixupExpression = (exp_fixup !== undefined) ? RED.util.prepareJSONataExpression(exp_fixup, this) : undefined;
|
||||
} catch(e) {
|
||||
this.error(RED._("join.errors.invalid-expr",{error:e.message}));
|
||||
return;
|
||||
@@ -426,9 +419,6 @@ module.exports = function(RED) {
|
||||
this.build = n.build || "array";
|
||||
this.accumulate = n.accumulate || "false";
|
||||
|
||||
this.topics = (n.topics || []).map(function(x) { return x.topic; });
|
||||
this.merge_on_change = n.mergeOnChange || false;
|
||||
this.topic_counts = undefined;
|
||||
this.output = n.output || "stream";
|
||||
this.pending = {};
|
||||
this.pending_count = 0;
|
||||
@@ -493,7 +483,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
|
||||
var pendingMessages = [];
|
||||
var activeMessagePromise = null;
|
||||
var activeMessage = null;
|
||||
// In reduce mode, we must process messages fully in order otherwise
|
||||
// groups may overlap and cause unexpected results. The use of JSONata
|
||||
// means some async processing *might* occur if flow/global context is
|
||||
@@ -502,7 +492,7 @@ module.exports = function(RED) {
|
||||
if (msg) {
|
||||
// A new message has arrived - add it to the message queue
|
||||
pendingMessages.push(msg);
|
||||
if (activeMessagePromise !== null) {
|
||||
if (activeMessage !== null) {
|
||||
// The node is currently processing a message, so do nothing
|
||||
// more with this message
|
||||
return;
|
||||
@@ -511,19 +501,21 @@ module.exports = function(RED) {
|
||||
if (pendingMessages.length === 0) {
|
||||
// There are no more messages to process, clear the active flag
|
||||
// and return
|
||||
activeMessagePromise = null;
|
||||
activeMessage = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// There are more messages to process. Get the next message and
|
||||
// start processing it. Recurse back in to check for any more
|
||||
var nextMsg = pendingMessages.shift();
|
||||
activeMessagePromise = reduce_msg(node, nextMsg)
|
||||
.then(processReduceMessageQueue)
|
||||
.catch((err) => {
|
||||
activeMessage = true;
|
||||
reduceMessage(node, nextMsg, err => {
|
||||
if (err) {
|
||||
node.error(err,nextMsg);
|
||||
return processReduceMessageQueue();
|
||||
});
|
||||
}
|
||||
activeMessage = null;
|
||||
processReduceMessageQueue();
|
||||
})
|
||||
}
|
||||
|
||||
this.on("input", function(msg) {
|
||||
|
||||
@@ -56,6 +56,7 @@ module.exports = function(RED) {
|
||||
RED.util.setMessageProperty(msg,node.property,JSON.parse(value));
|
||||
if (validate) {
|
||||
if (this.compiledSchema(msg[node.property])) {
|
||||
delete msg.schema;
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
@@ -70,6 +71,7 @@ module.exports = function(RED) {
|
||||
// If node.action is str and value is str
|
||||
if (validate) {
|
||||
if (this.compiledSchema(JSON.parse(msg[node.property]))) {
|
||||
delete msg.schema;
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
@@ -87,6 +89,7 @@ module.exports = function(RED) {
|
||||
if (validate) {
|
||||
if (this.compiledSchema(value)) {
|
||||
RED.util.setMessageProperty(msg,node.property,JSON.stringify(value,null,node.indent));
|
||||
delete msg.schema;
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
@@ -104,6 +107,7 @@ module.exports = function(RED) {
|
||||
// If node.action is obj and value is object
|
||||
if (validate) {
|
||||
if (this.compiledSchema(value)) {
|
||||
delete msg.schema;
|
||||
node.send(msg);
|
||||
} else {
|
||||
msg.schemaError = this.compiledSchema.errors;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-help-name="debug">
|
||||
<p>Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays <code>msg.payload</code>.</p>
|
||||
<p>Displays selected message properties in the debug sidebar tab and optionally the runtime log. By default it displays <code>msg.payload</code>, but can be configured to display any property, the full message or the result of a JSONata expression.</p>
|
||||
<h3>Details</h3>
|
||||
<p>The debug sidebar provides a structured view of the messages it is sent, making it easier to understand their structure.</p>
|
||||
<p>JavaScript objects and arrays can be collapsed and expanded as required. Buffer objects can be displayed as raw data or as a string if possible.</p>
|
||||
|
||||
@@ -103,6 +103,8 @@
|
||||
},
|
||||
"debug": {
|
||||
"output": "Output",
|
||||
"none": "None",
|
||||
"invalid-exp": "Invalid JSONata expression: __error__",
|
||||
"msgprop": "message property",
|
||||
"msgobj": "complete msg object",
|
||||
"to": "To",
|
||||
@@ -136,14 +138,7 @@
|
||||
},
|
||||
"link": {
|
||||
"linkIn": "link in",
|
||||
"linkOut": "link out",
|
||||
"label": {
|
||||
"event": "Event name",
|
||||
"node": "name",
|
||||
"type": "flow",
|
||||
"sortByFlow":"Sort by flow",
|
||||
"sortByLabel": "Sort by name"
|
||||
}
|
||||
"linkOut": "link out"
|
||||
},
|
||||
"tls": {
|
||||
"tls": "TLS configuration",
|
||||
@@ -307,12 +302,7 @@
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"comment": "comment",
|
||||
"label": {
|
||||
"title": "Title",
|
||||
"body": "Body"
|
||||
},
|
||||
"tip": "Tip: The body text can be styled as <a href=\"https://help.github.com/articles/markdown-basics/\" target=\"_blank\">GitHub flavoured Markdown</a>"
|
||||
"comment": "comment"
|
||||
},
|
||||
"unknown": {
|
||||
"label": {
|
||||
@@ -442,7 +432,8 @@
|
||||
"errors": {
|
||||
"connect-error": "An error occured on the ws connection: ",
|
||||
"send-error": "An error occurred while sending: ",
|
||||
"missing-conf": "Missing server configuration"
|
||||
"missing-conf": "Missing server configuration",
|
||||
"duplicate-path": "Cannot have two WebSocket listeners on the same path: __path__"
|
||||
}
|
||||
},
|
||||
"watch": {
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
<dt>payload<span class="property-type">object | string</span></dt>
|
||||
<dd>A JavaScript object or JSON string.</dd>
|
||||
<dt>schema<span class="property-type">object</span></dt>
|
||||
<dd>An optional JSON Schema object to validate the payload against.</dd>
|
||||
<dd>An optional JSON Schema object to validate the payload against.
|
||||
The property will be deleted before the <code>msg</code> is sent to the next node.</dd>
|
||||
</dl>
|
||||
<h3>Outputs</h3>
|
||||
<dl class="message-properties">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<script type="text/x-red" data-help-name="debug">
|
||||
<p>サイドバーの「デバッグ」タブに、選択したメッセージプロパティの値を表示します。設定により、ランタイムログへの出力も可能です。デフォルトの表示対象は<code>msg.payload</code>です。</p>
|
||||
<p>サイドバーの「デバッグ」タブに、選択したメッセージプロパティの値を表示します。設定により、ランタイムログへの出力も可能です。デフォルトの表示対象は<code>msg.payload</code>ですが、設定により、指定したプロパティ、メッセージ全体、もしくは、JSONata式の評価結果を出力できます。</p>
|
||||
<h3>詳細</h3>
|
||||
<p>「デバッグ」サイドバーは受け取ったメッセージの階層構造を表示する機能を備えます。この機能によりメッセージの構造を容易に理解できます。</p>
|
||||
<p>JavaScriptオブジェクトと配列は必要に応じて折り畳んだり展開したりできます。バッファオブジェクトを生データとして表示したり、表現可能な場合に文字列として表示したりすることも可能です。</p>
|
||||
|
||||
@@ -103,6 +103,8 @@
|
||||
},
|
||||
"debug": {
|
||||
"output": "対象",
|
||||
"none": "無し",
|
||||
"invalid-exp": "JSONata式が不正: __error__",
|
||||
"msgprop": "メッセージプロパティ",
|
||||
"msgobj": "msgオブジェクト全体",
|
||||
"to": "出力先",
|
||||
@@ -136,14 +138,7 @@
|
||||
},
|
||||
"link": {
|
||||
"linkIn": "link in",
|
||||
"linkOut": "link out",
|
||||
"label": {
|
||||
"event": "イベント名",
|
||||
"node": "名前",
|
||||
"type": "フロー",
|
||||
"sortByFlow": "フロー名で並べ替え",
|
||||
"sortByLabel": "名前で並べ替え"
|
||||
}
|
||||
"linkOut": "link out"
|
||||
},
|
||||
"tls": {
|
||||
"tls": "TLS設定",
|
||||
@@ -307,12 +302,7 @@
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"comment": "comment",
|
||||
"label": {
|
||||
"title": "タイトル",
|
||||
"body": "本文"
|
||||
},
|
||||
"tip": "注釈: 本文は<a href=\"https://help.github.com/articles/markdown-basics/\" target=\"_blank\">GitHubのMarkdown形式</a>として整形されます。"
|
||||
"comment": "comment"
|
||||
},
|
||||
"unknown": {
|
||||
"label": {
|
||||
|
||||
@@ -135,14 +135,7 @@
|
||||
},
|
||||
"link": {
|
||||
"linkIn": "输入",
|
||||
"linkOut": "输出",
|
||||
"label": {
|
||||
"event": "事件名称",
|
||||
"node": "节点名称",
|
||||
"type": "流程",
|
||||
"sortByFlow":"根据流程排序",
|
||||
"sortByLabel": "根据名称排序"
|
||||
}
|
||||
"linkOut": "输出"
|
||||
},
|
||||
"tls": {
|
||||
"tls": "TLS设置",
|
||||
@@ -297,11 +290,6 @@
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"label": {
|
||||
"title": "标题",
|
||||
"body": "主体"
|
||||
},
|
||||
"tip": "提示: 主题内容可被格式化为 <a href=\"https://help.github.com/articles/markdown-basics/\" target=\"_blank\">GitHub风格的Markdown</a>"
|
||||
},
|
||||
"unknown": {
|
||||
"label": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,20 +15,20 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "6.6.1",
|
||||
"ajv": "6.6.2",
|
||||
"body-parser": "1.18.3",
|
||||
"cheerio": "0.22.0",
|
||||
"cookie-parser": "1.4.3",
|
||||
"cookie": "0.3.1",
|
||||
"cors": "2.8.5",
|
||||
"cron": "1.5.1",
|
||||
"cron": "1.6.0",
|
||||
"denque": "1.4.0",
|
||||
"fs-extra": "7.0.1",
|
||||
"fs.notify": "0.0.4",
|
||||
"hash-sum": "1.0.2",
|
||||
"https-proxy-agent": "2.2.1",
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.12.0",
|
||||
"js-yaml": "3.12.1",
|
||||
"media-typer": "1.0.1",
|
||||
"mqtt": "2.18.8",
|
||||
"multer": "1.4.1",
|
||||
@@ -37,7 +37,7 @@
|
||||
"raw-body": "2.3.3",
|
||||
"request": "2.88.0",
|
||||
"sentiment": "2.1.0",
|
||||
"ws": "1.1.5",
|
||||
"ws": "6.1.2",
|
||||
"xml2js": "0.4.19"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +270,10 @@ function getNodeInfo(typeOrId) {
|
||||
if (config.hasOwnProperty("loaded")) {
|
||||
info.loaded = config.loaded;
|
||||
}
|
||||
if (module.pending_version) {
|
||||
info.pending_version = module.pending_version;
|
||||
}
|
||||
|
||||
info.version = module.version;
|
||||
return info;
|
||||
}
|
||||
@@ -342,6 +346,9 @@ function getModuleInfo(module) {
|
||||
path: moduleConfigs[module].path,
|
||||
nodes: []
|
||||
};
|
||||
if (moduleConfigs[module] && moduleConfigs[module].pending_version) {
|
||||
m.pending_version = moduleConfigs[module].pending_version;
|
||||
}
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
var nodeInfo = filterNodeInfo(moduleConfigs[module].nodes[nodes[i]]);
|
||||
nodeInfo.version = m.version;
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
var i18n = require("@node-red/util").i18n;
|
||||
var registry;
|
||||
var runtime;
|
||||
|
||||
function copyObjectProperties(src,dst,copyList,blockList) {
|
||||
@@ -105,6 +107,7 @@ function createNodeApi(node) {
|
||||
module.exports = {
|
||||
init: function(_runtime) {
|
||||
runtime = _runtime;
|
||||
registry = require("@node-red/registry/lib");
|
||||
},
|
||||
createNodeApi: createNodeApi
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "0.20.0-beta.2",
|
||||
"@node-red/util": "0.20.0-beta.3",
|
||||
"semver": "5.6.0",
|
||||
"uglify-js": "3.4.9",
|
||||
"when": "3.7.8"
|
||||
|
||||
@@ -209,6 +209,7 @@ LocalFileSystem.prototype.open = function(){
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self._flushPendingWrites = function() { }
|
||||
return fs.ensureDir(self.storageBaseDir);
|
||||
}
|
||||
}
|
||||
@@ -219,6 +220,12 @@ LocalFileSystem.prototype.close = function(){
|
||||
clearTimeout(this._pendingWriteTimeout);
|
||||
delete this._pendingWriteTimeout;
|
||||
this.flushInterval = 0;
|
||||
self.writePromise = self.writePromise.then(function(){
|
||||
return self._flushPendingWrites.call(self).catch(function(err) {
|
||||
log.error(log._("context.localfilesystem.error-write",{message:err.toString()}));
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
return this.writePromise;
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ function reportNodeStateChange(info,enabled) {
|
||||
}
|
||||
|
||||
function installModule(module,version) {
|
||||
var ex_module = registry.getModuleInfo(module);
|
||||
var isUpgrade = !!ex_module;
|
||||
var existingModule = registry.getModuleInfo(module);
|
||||
var isUpgrade = !!existingModule;
|
||||
return registry.installModule(module,version).then(function(info) {
|
||||
if (isUpgrade) {
|
||||
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:module,version:version}});
|
||||
|
||||
@@ -34,7 +34,7 @@ function getFileMeta(root,path) {
|
||||
var read = 0;
|
||||
var length = 10;
|
||||
var remaining = "";
|
||||
var buffer = Buffer(length);
|
||||
var buffer = Buffer.alloc(length);
|
||||
while(read < size) {
|
||||
read+=fs.readSync(fd,buffer,0,length);
|
||||
var data = remaining+buffer.toString();
|
||||
@@ -63,7 +63,7 @@ function getFileBody(root,path) {
|
||||
var read = 0;
|
||||
var length = 50;
|
||||
var remaining = "";
|
||||
var buffer = Buffer(length);
|
||||
var buffer = Buffer.alloc(length);
|
||||
while(read < size) {
|
||||
var thisRead = fs.readSync(fd,buffer,0,length);
|
||||
read += thisRead;
|
||||
|
||||
@@ -137,17 +137,21 @@ function init(_settings, _runtime) {
|
||||
saveSettings = true;
|
||||
} else {
|
||||
// if it resolves to a dir - use it
|
||||
var stat = fs.statSync(fspath.join(projectsDir,settings.flowFile));
|
||||
if (stat && stat.isDirectory()) {
|
||||
activeProject = settings.flowFile;
|
||||
globalSettings.projects.activeProject = activeProject;
|
||||
// Now check for a credentialSecret
|
||||
if (settings.credentialSecret !== undefined) {
|
||||
globalSettings.projects.projects[settings.flowFile] = {
|
||||
credentialSecret: settings.credentialSecret
|
||||
try {
|
||||
var stat = fs.statSync(fspath.join(projectsDir,settings.flowFile));
|
||||
if (stat && stat.isDirectory()) {
|
||||
activeProject = settings.flowFile;
|
||||
globalSettings.projects.activeProject = activeProject;
|
||||
// Now check for a credentialSecret
|
||||
if (settings.credentialSecret !== undefined) {
|
||||
globalSettings.projects.projects[settings.flowFile] = {
|
||||
credentialSecret: settings.credentialSecret
|
||||
}
|
||||
saveSettings = true;
|
||||
}
|
||||
saveSettings = true;
|
||||
}
|
||||
} catch(err) {
|
||||
// Doesn't exist, handle as a flow file to be created
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
packages/node_modules/@node-red/runtime/locales/ja/runtime.json
vendored
Normal file → Executable file
3
packages/node_modules/@node-red/runtime/locales/ja/runtime.json
vendored
Normal file → Executable file
@@ -12,7 +12,8 @@
|
||||
"loading": "パレットノードのロード",
|
||||
"palette-editor": {
|
||||
"disabled": "パレットエディタを無効化 : ユーザ設定",
|
||||
"npm-not-found": "バレットエディタを無効化 : npmコマンドが見つかりません"
|
||||
"npm-not-found": "パレットエディタを無効化 : npmコマンドが見つかりません",
|
||||
"npm-too-old": "パレットエディタを無効化 : npmのバージョンが古過ぎます。npm 3.x以上が必要です"
|
||||
},
|
||||
"errors": "__count__ 個のノードの登録に失敗しました",
|
||||
"errors_plural": "__count__ 個のノードの登録に失敗しました",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "0.20.0-beta.2",
|
||||
"@node-red/util": "0.20.0-beta.2",
|
||||
"@node-red/registry": "0.20.0-beta.3",
|
||||
"@node-red/util": "0.20.0-beta.3",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.16.4",
|
||||
"fs-extra": "7.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,9 +16,9 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"clone": "2.1.2",
|
||||
"i18next": "12.1.0",
|
||||
"i18next": "13.1.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.5.4",
|
||||
"jsonata": "1.6.3",
|
||||
"when": "3.7.8"
|
||||
}
|
||||
}
|
||||
|
||||
10
packages/node_modules/node-red/package.json
vendored
10
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "0.20.0-beta.2",
|
||||
"version": "0.20.0-beta.3",
|
||||
"description": "A visual tool for wiring the Internet of Things",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,10 +31,10 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "0.20.0-beta.2",
|
||||
"@node-red/runtime": "0.20.0-beta.2",
|
||||
"@node-red/util": "0.20.0-beta.2",
|
||||
"@node-red/nodes": "0.20.0-beta.2",
|
||||
"@node-red/editor-api": "0.20.0-beta.3",
|
||||
"@node-red/runtime": "0.20.0-beta.3",
|
||||
"@node-red/util": "0.20.0-beta.3",
|
||||
"@node-red/nodes": "0.20.0-beta.3",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"express": "4.16.4",
|
||||
|
||||
2
packages/node_modules/node-red/red.js
vendored
2
packages/node_modules/node-red/red.js
vendored
@@ -247,7 +247,7 @@ function basicAuthMiddleware(user,pass) {
|
||||
}
|
||||
var requestUser = basicAuth(req);
|
||||
if (!requestUser || requestUser.name !== user || !checkPasswordAndCache(requestUser.pass)) {
|
||||
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
|
||||
res.set('WWW-Authenticate', 'Basic realm="Authorization Required"');
|
||||
return res.sendStatus(401);
|
||||
}
|
||||
next();
|
||||
|
||||
@@ -64,6 +64,7 @@ if (require.main === module) {
|
||||
verifyDependencies().then(failures => {
|
||||
if (failures.length > 0) {
|
||||
failures.forEach(f => console.log(` - ${f}`));
|
||||
console.log("Run with --fix option to fix up versions")
|
||||
process.exit(1);
|
||||
}
|
||||
}).catch(e => {
|
||||
|
||||
@@ -18,6 +18,8 @@ var util = require("util");
|
||||
|
||||
var nodePage = require("../../node_page");
|
||||
|
||||
var keyPage = require("../../../util/key_page");
|
||||
|
||||
function debugNode(id) {
|
||||
nodePage.call(this, id);
|
||||
}
|
||||
@@ -32,7 +34,7 @@ debugNode.prototype.setOutput = function(complete) {
|
||||
browser.clickWithWait('//div[@class="red-ui-typedInput-options"][1]/a[1]');
|
||||
// Input the path in msg.
|
||||
browser.clickWithWait('//*[contains(@class, "red-ui-typedInput-input")]/input');
|
||||
browser.keys(['Control', 'a', 'Control']);
|
||||
browser.keys(keyPage.selectAll());
|
||||
browser.keys(['Delete']);
|
||||
browser.setValue('//*[contains(@class, "red-ui-typedInput-input")]/input', complete);
|
||||
} else {
|
||||
|
||||
@@ -18,6 +18,8 @@ var util = require("util");
|
||||
|
||||
var nodePage = require("../../node_page");
|
||||
|
||||
var keyPage = require("../../../util/key_page");
|
||||
|
||||
function functionNode(id) {
|
||||
nodePage.call(this, id);
|
||||
}
|
||||
@@ -25,13 +27,13 @@ function functionNode(id) {
|
||||
util.inherits(functionNode, nodePage);
|
||||
|
||||
functionNode.prototype.setFunction = function(func) {
|
||||
browser.click('#node-input-func-editor');
|
||||
browser.keys(['Control', 'Home', 'Control']);
|
||||
browser.clickWithWait('#node-input-func-editor');
|
||||
browser.keys(keyPage.selectAll());
|
||||
for (var i = 0; i < func.length; i++) {
|
||||
browser.keys([func.charAt(i)]);
|
||||
}
|
||||
// Delete the unnecessary code that ace editor does the autocompletion.
|
||||
browser.keys(['Control', 'Shift', 'End', 'Shift', 'Control']);
|
||||
browser.keys(keyPage.selectToEnd());
|
||||
browser.keys(['Delete']);
|
||||
// Need to wait until ace editor correctly checks the syntax.
|
||||
browser.pause(300);
|
||||
|
||||
@@ -18,6 +18,8 @@ var util = require("util");
|
||||
|
||||
var nodePage = require("../../node_page");
|
||||
|
||||
var keyPage = require("../../../util/key_page");
|
||||
|
||||
function templateNode(id) {
|
||||
nodePage.call(this, id);
|
||||
}
|
||||
@@ -33,13 +35,13 @@ templateNode.prototype.setFormat = function(format) {
|
||||
}
|
||||
|
||||
templateNode.prototype.setTemplate = function(template) {
|
||||
browser.click('#node-input-template-editor');
|
||||
browser.keys(['Control', 'a', 'Control']); // call twice to release the keys.
|
||||
browser.clickWithWait('#node-input-template-editor');
|
||||
browser.keys(keyPage.selectAll());
|
||||
// Need to add a character one by one since some words such as 'Control' are treated as a special word.
|
||||
for (var i = 0; i < template.length; i++) {
|
||||
browser.keys([template.charAt(i)]);
|
||||
}
|
||||
browser.keys(['Control', 'Shift', 'End', 'Shift', 'Control']);
|
||||
browser.keys(keyPage.selectToEnd());
|
||||
browser.keys(['Delete']);
|
||||
// Need to wait until ace editor correctly checks the syntax.
|
||||
browser.pause(300);
|
||||
|
||||
50
test/editor/pageobjects/util/key_page.js
Normal file
50
test/editor/pageobjects/util/key_page.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var os = require("os");
|
||||
|
||||
var shortCutKeyMap = {
|
||||
"selectAll": ['Control', 'a', 'Control'],
|
||||
"selectToEnd": ['Control', 'Shift', 'End', 'Shift', 'Control'],
|
||||
};
|
||||
|
||||
var shortCutKeyMapForMac = {
|
||||
"selectAll": ['Command', 'a', 'Command'],
|
||||
"selectToEnd": ['Command', 'Shift', 'ArrowDown', 'Shift', 'Command'],
|
||||
};
|
||||
|
||||
function getShortCutKey(type) {
|
||||
if (os.type() === "Darwin") {
|
||||
return shortCutKeyMapForMac[type];
|
||||
} else {
|
||||
return shortCutKeyMap[type];
|
||||
}
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
var key = getShortCutKey('selectAll');
|
||||
return key;
|
||||
}
|
||||
|
||||
function selectToEnd() {
|
||||
var key = getShortCutKey('selectToEnd');
|
||||
return key;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
selectAll: selectAll,
|
||||
selectToEnd: selectToEnd,
|
||||
};
|
||||
@@ -513,7 +513,7 @@ describe('cookbook', function() {
|
||||
var changeNode = workspace.addNode("change", 400);
|
||||
|
||||
var httpinNodeClear = workspace.addNode("httpin", 0, 200);
|
||||
var functionNodeClear = workspace.addNode("function", 240);
|
||||
var functionNodeClear = workspace.addNode("function", 250);
|
||||
|
||||
httpinNodeFormat.edit();
|
||||
httpinNodeFormat.setMethod("get");
|
||||
|
||||
@@ -356,6 +356,22 @@ describe('debug node', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should publish complete message with edit', function(done) {
|
||||
var flow = [{id:"n1", type:"debug", name:"Debug", complete: "true",
|
||||
targetType: "jsonata", complete: '"<" & payload & ">"'}];
|
||||
helper.load(debugNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
websocket_test(function() {
|
||||
n1.emit("input", {payload:"test"});
|
||||
}, function(msg) {
|
||||
JSON.parse(msg).should.eql([{
|
||||
topic:"debug",data:{id:"n1",name:"Debug",msg:"<test>",
|
||||
format:"string[6]"}
|
||||
}]);
|
||||
}, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should truncate a long message', function(done) {
|
||||
var flow = [{id:"n1", type:"debug" }];
|
||||
helper.load(debugNode, flow, function() {
|
||||
|
||||
@@ -433,4 +433,36 @@ describe('JSON node', function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('msg.schema property should be deleted before sending to next node (string input)', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
jn2.on("input", function(msg) {
|
||||
should.equal(msg.schema, undefined);
|
||||
done();
|
||||
});
|
||||
var jsonString = '{"number":3,"string":"allo"}';
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
jn1.receive({payload:jsonString, schema:schema});
|
||||
});
|
||||
});
|
||||
|
||||
it('msg.schema property should be deleted before sending to next node (object input)', function(done) {
|
||||
var flow = [{id:"jn1",type:"json",action:"str",wires:[["jn2"]]},
|
||||
{id:"jn2", type:"helper"}];
|
||||
helper.load(jsonNode, flow, function() {
|
||||
var jn1 = helper.getNode("jn1");
|
||||
var jn2 = helper.getNode("jn2");
|
||||
jn2.on("input", function(msg) {
|
||||
should.equal(msg.schema, undefined);
|
||||
done();
|
||||
});
|
||||
var jsonObject = {"number":3,"string":"allo"};
|
||||
var schema = {title: "testSchema", type: "object", properties: {number: {type: "number"}, string: {type: "string" }}};
|
||||
jn1.receive({payload:jsonObject, schema:schema});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ describe('api/editor/credentials', function() {
|
||||
request(app)
|
||||
.get("/credentials/unknown-type/n2")
|
||||
.expect("Content-Type",/json/)
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
done(err);
|
||||
|
||||
@@ -102,7 +102,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.getUserKeys.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -139,7 +139,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.getUserKeys.returns(p)
|
||||
request(app)
|
||||
.get("/settings/user/keys")
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -179,7 +179,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.getUserKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -200,7 +200,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.getUserKey.returns(p);
|
||||
request(app)
|
||||
.get("/settings/user/keys/" + key_file_name)
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -238,7 +238,7 @@ describe("api/editor/sshkeys", function() {
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -260,7 +260,7 @@ describe("api/editor/sshkeys", function() {
|
||||
request(app)
|
||||
.post("/settings/user/keys")
|
||||
.send({ name: key_file_name })
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -297,7 +297,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.removeUserKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
@@ -318,7 +318,7 @@ describe("api/editor/sshkeys", function() {
|
||||
mockRuntime.settings.removeUserKey.returns(p);
|
||||
request(app)
|
||||
.delete("/settings/user/keys/" + key_file_name)
|
||||
.expect(500)
|
||||
.expect(400)
|
||||
.end(function(err,res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
|
||||
Reference in New Issue
Block a user