1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge branch 'dev' into mqtt5

This commit is contained in:
Steve-Mcl 2021-02-18 18:50:26 +00:00
commit ab4a9e72d4
247 changed files with 10223 additions and 2444 deletions

View File

@ -1,5 +1,6 @@
name: PublishDockerImage name: PublishDockerImage
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
on: on:
release: release:
types: [published] types: [published]
@ -27,9 +28,6 @@ jobs:
with: with:
node-version: '12' node-version: '12'
- run: node ./node-red/.github/scripts/update-node-red-docker.js - run: node ./node-red/.github/scripts/update-node-red-docker.js
with:
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Create Docker Pull Request - name: Create Docker Pull Request
uses: peter-evans/create-pull-request@v2 uses: peter-evans/create-pull-request@v2
with: with:

View File

@ -11,5 +11,11 @@ matrix:
before_script: before_script:
- npm install -g coveralls - npm install -g coveralls
- node_js: "12" - node_js: "12"
script:
- ./node_modules/.bin/grunt no-coverage
- node_js: "10" - node_js: "10"
script:
- ./node_modules/.bin/grunt no-coverage
- node_js: "8" - node_js: "8"
script:
- ./node_modules/.bin/grunt no-coverage

2
API.md
View File

@ -10,6 +10,6 @@ Module | Description
[@node-red/editor-api](@node-red_editor-api.html) | an Express application that serves the Node-RED editor and provides the Admin HTTP API [@node-red/editor-api](@node-red_editor-api.html) | an Express application that serves the Node-RED editor and provides the Admin HTTP API
[@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED [@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED
[@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules [@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules
@node-red/registry | the internal node registry [@node-red/registry](@node-red_registry.html) | the internal node registry
@node-red/nodes | the default set of core nodes @node-red/nodes | the default set of core nodes
@node-red/editor-client | the client-side resources of the Node-RED editor application @node-red/editor-client | the client-side resources of the Node-RED editor application

View File

@ -1,3 +1,65 @@
### 1.2.8: Maintenance Release
Editor
- Ensure subflow help is picked up for palette tooltip Fixes #2834
- Improve Ru locale (#2826) @alexk111
- Fix scrollbars (#2825) @alexk111
Runtime
- Restrict project file access to inside the project directory
- Validate user-provided language parameter before passing to i18n
- Fix grunt release mkdir issue on Node.js 14 (#2827) @alexk111
- Prevent crash when coreNodesDir is empty (#2831) @hardillb
Nodes
- Batch node: Fixing minor typo in node's documentation (#2848) @matthiasradde
- Split node: Handle out of order messages as long as one of the messages has msg.parts.count set to the proper value (#2748) @s4ke
### 1.2.7: Maintenance Release
Editor
- Ensure subflow-scoped config nodes do not get moved on import Fixes #2789
- Allow TypedInput to be disabled (#2752) @bartbutenaers
- Allow userMenu to be explicitly enabled (#2805) @tfmf
- Improvements to DE translation (#2192) @ketzu
Runtime
- Handle `undefined` error passed to node.error (#2781) @johnwang71
- Disable nyc coverage reporting on older node versions
- Improve Editor API unit test coverage (#2777) @aaronmyatt
Nodes
- Trigger: ensure timestamp option sends .now() at point of sending
### 1.2.6: Maintenance Release
Editor
- Update Japanese translations for 1.2.5 (#2764) @kazuhitoyokoi
- Library: properly handle symlinked folders (#2768) @natcl
Runtime
- Support Windows paths when installing tarball by path name Fixes #2769
- Fix unsecure command usage in GH Action
Nodes
- Update MQTT to latest to fix Node 8 URL breakage
### 1.2.5: Maintenance Release ### 1.2.5: Maintenance Release
Editor Editor

View File

@ -142,6 +142,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js", "packages/node_modules/@node-red/editor-client/src/js/text/bidi.js",
"packages/node_modules/@node-red/editor-client/src/js/text/format.js", "packages/node_modules/@node-red/editor-client/src/js/text/format.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js", "packages/node_modules/@node-red/editor-client/src/js/ui/state.js",
"packages/node_modules/@node-red/editor-client/src/js/plugins.js",
"packages/node_modules/@node-red/editor-client/src/js/nodes.js", "packages/node_modules/@node-red/editor-client/src/js/nodes.js",
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js", "packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js", "packages/node_modules/@node-red/editor-client/src/js/history.js",
@ -461,7 +462,8 @@ module.exports = function(grunt) {
'packages/node_modules/@node-red/runtime/lib/hooks.js', 'packages/node_modules/@node-red/runtime/lib/hooks.js',
'packages/node_modules/@node-red/util/**/*.js', 'packages/node_modules/@node-red/util/**/*.js',
'packages/node_modules/@node-red/editor-api/lib/index.js', 'packages/node_modules/@node-red/editor-api/lib/index.js',
'packages/node_modules/@node-red/editor-api/lib/auth/index.js' 'packages/node_modules/@node-red/editor-api/lib/auth/index.js',
'packages/node_modules/@node-red/registry/lib/index.js'
], ],
options: { options: {
destination: 'docs', destination: 'docs',
@ -623,6 +625,11 @@ module.exports = function(grunt) {
'Builds editor content then runs code style checks and unit tests on all components', 'Builds editor content then runs code style checks and unit tests on all components',
['build','verifyPackageDependencies','jshint:editor','nyc:all']); ['build','verifyPackageDependencies','jshint:editor','nyc:all']);
grunt.registerTask('no-coverage',
'Builds editor content then runs code style checks and unit tests on all components without code coverage',
['build','verifyPackageDependencies','jshint:editor','simplemocha:all']);
grunt.registerTask('test-core', grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code', 'Runs code style check and unit tests on core runtime code',
['build','nyc:core']); ['build','nyc:core']);

View File

@ -27,7 +27,7 @@
], ],
"dependencies": { "dependencies": {
"ajv": "6.12.6", "ajv": "6.12.6",
"async-mutex": "0.2.4", "async-mutex": "0.2.6",
"basic-auth": "2.0.1", "basic-auth": "2.0.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.19.0", "body-parser": "1.19.0",
@ -38,7 +38,7 @@
"cookie-parser": "1.4.5", "cookie-parser": "1.4.5",
"cors": "2.8.5", "cors": "2.8.5",
"cron": "1.7.2", "cron": "1.7.2",
"denque": "1.4.1", "denque": "1.5.0",
"express": "4.17.1", "express": "4.17.1",
"express-session": "1.17.1", "express-session": "1.17.1",
"fs-extra": "8.1.0", "fs-extra": "8.1.0",
@ -54,11 +54,11 @@
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"media-typer": "1.1.0", "media-typer": "1.1.0",
"memorystore": "1.6.4", "memorystore": "1.6.4",
"mime": "2.4.6", "mime": "2.4.7",
"moment-timezone": "0.5.32", "moment-timezone": "0.5.32",
"mqtt": "4.2.5", "mqtt": "4.2.6",
"multer": "1.4.2", "multer": "1.4.2",
"mustache": "4.0.1", "mustache": "4.1.0",
"node-red-admin": "^0.2.6", "node-red-admin": "^0.2.6",
"node-red-node-rbe": "^0.2.9", "node-red-node-rbe": "^0.2.9",
"node-red-node-sentiment": "^0.1.6", "node-red-node-sentiment": "^0.1.6",
@ -73,8 +73,7 @@
"request": "2.88.0", "request": "2.88.0",
"semver": "6.3.0", "semver": "6.3.0",
"tar": "6.0.5", "tar": "6.0.5",
"uglify-js": "3.11.6", "uglify-js": "3.12.4",
"when": "3.7.8",
"ws": "6.2.1", "ws": "6.2.1",
"xml2js": "0.4.23" "xml2js": "0.4.23"
}, },
@ -82,7 +81,7 @@
"bcrypt": "3.0.8" "bcrypt": "3.0.8"
}, },
"devDependencies": { "devDependencies": {
"dompurify": "2.2.2", "dompurify": "2.2.6",
"grunt": "1.3.0", "grunt": "1.3.0",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
"grunt-cli": "~1.3.2", "grunt-cli": "~1.3.2",
@ -97,17 +96,17 @@
"grunt-jsdoc": "2.4.1", "grunt-jsdoc": "2.4.1",
"grunt-jsdoc-to-markdown": "5.0.0", "grunt-jsdoc-to-markdown": "5.0.0",
"grunt-jsonlint": "2.1.3", "grunt-jsonlint": "2.1.3",
"grunt-mkdir": "~1.0.0", "grunt-mkdir": "~1.1.0",
"grunt-npm-command": "~0.1.2", "grunt-npm-command": "~0.1.2",
"grunt-sass": "~3.1.0", "grunt-sass": "~3.1.0",
"grunt-simple-mocha": "~0.4.1", "grunt-simple-mocha": "~0.4.1",
"grunt-simple-nyc": "^3.0.1", "grunt-simple-nyc": "^3.0.1",
"http-proxy": "1.18.1", "http-proxy": "1.18.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "1.2.4", "marked": "1.2.7",
"minami": "1.2.3", "minami": "1.2.3",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"node-red-node-test-helper": "^0.2.5", "node-red-node-test-helper": "^0.2.6",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"nodemon": "2.0.6", "nodemon": "2.0.6",
"should": "13.2.3", "should": "13.2.3",

View File

@ -22,6 +22,7 @@ var flow = require("./flow");
var context = require("./context"); var context = require("./context");
var auth = require("../auth"); var auth = require("../auth");
var info = require("./settings"); var info = require("./settings");
var plugins = require("./plugins");
var apiUtil = require("../util"); var apiUtil = require("../util");
@ -32,6 +33,7 @@ module.exports = {
nodes.init(runtimeAPI); nodes.init(runtimeAPI);
context.init(runtimeAPI); context.init(runtimeAPI);
info.init(settings,runtimeAPI); info.init(settings,runtimeAPI);
plugins.init(runtimeAPI);
var needsPermission = auth.needsPermission; var needsPermission = auth.needsPermission;
@ -50,13 +52,15 @@ module.exports = {
// Nodes // Nodes
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler); adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler);
if (!settings.editorTheme || !settings.editorTheme.palette || settings.editorTheme.palette.upload !== false) { if (!settings.externalModules || !settings.externalModules.palette || settings.externalModules.palette.allowInstall !== false) {
if (!settings.externalModules || !settings.externalModules.palette || settings.externalModules.palette.allowUpload !== false) {
const multer = require('multer'); const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() }); const upload = multer({ storage: multer.memoryStorage() });
adminApp.post("/nodes",needsPermission("nodes.write"),upload.single("tarball"),nodes.post,apiUtil.errorHandler); adminApp.post("/nodes",needsPermission("nodes.write"),upload.single("tarball"),nodes.post,apiUtil.errorHandler);
} else { } else {
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler); adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
} }
}
adminApp.get(/^\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler); adminApp.get(/^\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler);
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler); adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler);
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler); adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler);
@ -78,6 +82,10 @@ module.exports = {
adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler); adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
// Plugins
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
return adminApp; return adminApp;
} }
} }

View File

@ -60,6 +60,7 @@ module.exports = {
runtimeAPI.nodes.addModule(opts).then(function(info) { runtimeAPI.nodes.addModule(opts).then(function(info) {
res.json(info); res.json(info);
}).catch(function(err) { }).catch(function(err) {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err); apiUtils.rejectHandler(req,res,err);
}) })
}, },

View File

@ -0,0 +1,46 @@
var apiUtils = require("../util");
var runtimeAPI;
module.exports = {
init: function(_runtimeAPI) {
runtimeAPI = _runtimeAPI;
},
getAll: function(req,res) {
var opts = {
user: req.user,
req: apiUtils.getRequestLogObject(req)
}
if (req.get("accept") == "application/json") {
runtimeAPI.plugins.getPluginList(opts).then(function(list) {
res.json(list);
})
} else {
opts.lang = apiUtils.determineLangFromHeaders(req.acceptsLanguages());
if (/[^a-z\-\*]/i.test(opts.lang)) {
res.json({});
return;
}
runtimeAPI.plugins.getPluginConfigs(opts).then(function(configs) {
res.send(configs);
})
}
},
getCatalogs: function(req,res) {
var opts = {
user: req.user,
lang: req.query.lng,
req: apiUtils.getRequestLogObject(req)
}
if (/[^a-z\-\*]/i.test(opts.lang)) {
res.json({});
return;
}
runtimeAPI.plugins.getPluginCatalogs(opts).then(function(result) {
res.json(result);
}).catch(function(err) {
console.log(err.stack);
apiUtils.rejectHandler(req,res,err);
})
}
};

View File

@ -76,7 +76,7 @@ module.exports = {
editorApp.get("/icons/:scope/:module/:icon",ui.icon); editorApp.get("/icons/:scope/:module/:icon",ui.icon);
var theme = require("./theme"); var theme = require("./theme");
theme.init(settings); theme.init(settings, runtimeAPI);
editorApp.use("/theme",theme.app()); editorApp.use("/theme",theme.app());
editorApp.use("/",ui.editorResources); editorApp.use("/",ui.editorResources);

View File

@ -17,7 +17,6 @@
var apiUtils = require("../util"); var apiUtils = require("../util");
var fs = require('fs'); var fs = require('fs');
var fspath = require('path'); var fspath = require('path');
var when = require('when');
var runtimeAPI; var runtimeAPI;

View File

@ -39,9 +39,12 @@ module.exports = {
}, },
get: function(req,res) { get: function(req,res) {
var namespace = req.params[0]; var namespace = req.params[0];
var lngs = req.query.lng;
namespace = namespace.replace(/\.json$/,""); namespace = namespace.replace(/\.json$/,"");
var lang = req.query.lng || i18n.defaultLang; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []); var lang = req.query.lng || i18n.defaultLang; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []);
if (/[^a-z\-\*]/i.test(lang)) {
res.json({});
return;
}
var prevLang = i18n.i.language; var prevLang = i18n.i.language;
// Trigger a load from disk of the language if it is not the default // Trigger a load from disk of the language if it is not the default
i18n.i.changeLanguage(lang, function(){ i18n.i.changeLanguage(lang, function(){

View File

@ -41,6 +41,10 @@ var theme = null;
var themeContext = clone(defaultContext); var themeContext = clone(defaultContext);
var themeSettings = null; var themeSettings = null;
var activeTheme = null;
var activeThemeInitialised = false;
var runtimeAPI;
var themeApp; var themeApp;
function serveFile(app,baseUrl,file) { function serveFile(app,baseUrl,file) {
@ -58,7 +62,7 @@ function serveFile(app,baseUrl,file) {
} }
} }
function serveFilesFromTheme(themeValue, themeApp, directory) { function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
var result = []; var result = [];
if (themeValue) { if (themeValue) {
var array = themeValue; var array = themeValue;
@ -67,7 +71,14 @@ function serveFilesFromTheme(themeValue, themeApp, directory) {
} }
for (var i=0;i<array.length;i++) { for (var i=0;i<array.length;i++) {
var url = serveFile(themeApp,directory,array[i]); let fullPath = array[i];
if (baseDirectory) {
fullPath = path.resolve(baseDirectory,array[i]);
if (fullPath.indexOf(baseDirectory) !== 0) {
continue;
}
}
var url = serveFile(themeApp,directory,fullPath);
if (url) { if (url) {
result.push(url); result.push(url);
} }
@ -77,10 +88,12 @@ function serveFilesFromTheme(themeValue, themeApp, directory) {
} }
module.exports = { module.exports = {
init: function(settings) { init: function(settings, _runtimeAPI) {
runtimeAPI = _runtimeAPI;
themeContext = clone(defaultContext); themeContext = clone(defaultContext);
themeSettings = null; themeSettings = null;
theme = settings.editorTheme || {}; theme = settings.editorTheme || {};
activeTheme = theme.theme;
}, },
app: function() { app: function() {
@ -169,7 +182,9 @@ module.exports = {
} }
} }
} }
themeApp.get("/", function(req,res) { themeApp.get("/", async function(req,res) {
const themePluginList = await runtimeAPI.plugins.getPluginsByType({type:"node-red-theme"});
themeContext.themes = themePluginList.map(theme => theme.id);
res.json(themeContext); res.json(themeContext);
}) })
@ -185,10 +200,38 @@ module.exports = {
themeSettings.projects = theme.projects; themeSettings.projects = theme.projects;
} }
if (theme.theme) {
themeSettings.theme = theme.theme;
}
return themeApp; return themeApp;
}, },
context: function() { context: async function() {
if (activeTheme && !activeThemeInitialised) {
const themePlugin = await runtimeAPI.plugins.getPlugin({
id:activeTheme
});
if (themePlugin) {
if (themePlugin.css) {
const cssFiles = serveFilesFromTheme(
themePlugin.css,
themeApp,
"/css/",
themePlugin.path
);
themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
}
if (themePlugin.scripts) {
const scriptFiles = serveFilesFromTheme(
themePlugin.scripts,
themeApp,
"/scripts/",
themePlugin.path
)
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
}
}
activeThemeInitialised = true;
}
return themeContext; return themeContext;
}, },
settings: function() { settings: function() {

View File

@ -68,8 +68,8 @@ module.exports = {
apiUtils.rejectHandler(req,res,err); apiUtils.rejectHandler(req,res,err);
}) })
}, },
editor: function(req,res) { editor: async function(req,res) {
res.send(Mustache.render(editorTemplate,theme.context())); res.send(Mustache.render(editorTemplate,await theme.context()));
}, },
editorResources: express.static(path.join(editorClientDir,'public')) editorResources: express.static(path.join(editorClientDir,'public'))
}; };

View File

@ -28,7 +28,6 @@ var express = require("express");
var bodyParser = require("body-parser"); var bodyParser = require("body-parser");
var util = require('util'); var util = require('util');
var passport = require('passport'); var passport = require('passport');
var when = require('when');
var cors = require('cors'); var cors = require('cors');
var auth = require("./auth"); var auth = require("./auth");
@ -60,8 +59,8 @@ function init(settings,_server,storage,runtimeAPI) {
adminApp.use(corsHandler); adminApp.use(corsHandler);
if (settings.httpAdminMiddleware) { if (settings.httpAdminMiddleware) {
if (typeof settings.httpAdminMiddleware === "function") { if (typeof settings.httpAdminMiddleware === "function" || Array.isArray(settings.httpAdminMiddleware)) {
adminApp.use(settings.httpAdminMiddleware) adminApp.use(settings.httpAdminMiddleware);
} }
} }
@ -111,11 +110,9 @@ function init(settings,_server,storage,runtimeAPI) {
* @return {Promise} resolves when the application is ready to handle requests * @return {Promise} resolves when the application is ready to handle requests
* @memberof @node-red/editor-api * @memberof @node-red/editor-api
*/ */
function start() { async function start() {
if (editor) { if (editor) {
return editor.start(); return editor.start();
} else {
return when.resolve();
} }
} }
@ -124,11 +121,10 @@ function start() {
* @return {Promise} resolves when the application is stopped * @return {Promise} resolves when the application is stopped
* @memberof @node-red/editor-api * @memberof @node-red/editor-api
*/ */
function stop() { async function stop() {
if (editor) { if (editor) {
editor.stop(); editor.stop();
} }
return when.resolve();
} }
module.exports = { module.exports = {
init: init, init: init,

View File

@ -25,14 +25,13 @@
"express-session": "1.17.1", "express-session": "1.17.1",
"express": "4.17.1", "express": "4.17.1",
"memorystore": "1.6.4", "memorystore": "1.6.4",
"mime": "2.4.6", "mime": "2.4.7",
"multer": "1.4.2", "multer": "1.4.2",
"mustache": "4.0.1", "mustache": "4.1.0",
"oauth2orize": "1.11.0", "oauth2orize": "1.11.0",
"passport-http-bearer": "1.0.1", "passport-http-bearer": "1.0.1",
"passport-oauth2-client-password": "0.1.2", "passport-oauth2-client-password": "0.1.2",
"passport": "0.4.1", "passport": "0.4.1",
"when": "3.7.8",
"ws": "6.2.1" "ws": "6.2.1"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@ -32,7 +32,7 @@
"label" : { "label" : {
"view" : { "view" : {
"view" : "Ansicht", "view" : "Ansicht",
"grid" : "Gitter", "grid" : "Raster",
"showGrid" : "Raster anzeigen", "showGrid" : "Raster anzeigen",
"snapGrid" : "Am Raster ausrichten", "snapGrid" : "Am Raster ausrichten",
"gridSize" : "Rastergröße", "gridSize" : "Rastergröße",

View File

@ -38,6 +38,7 @@
} }
}, },
"event": { "event": {
"loadPlugins": "Loading Plugins",
"loadPalette": "Loading Palette", "loadPalette": "Loading Palette",
"loadNodeCatalogs": "Loading Node catalogs", "loadNodeCatalogs": "Loading Node catalogs",
"loadNodes": "Loading Nodes __count__", "loadNodes": "Loading Nodes __count__",
@ -337,8 +338,21 @@
"output": "outputs:", "output": "outputs:",
"status": "status node", "status": "status node",
"deleteSubflow": "delete subflow", "deleteSubflow": "delete subflow",
"confirmDelete": "Are you sure you want to delete this subflow?",
"info": "Description", "info": "Description",
"category": "Category", "category": "Category",
"module": "Module",
"license": "License",
"licenseNone": "none",
"licenseOther": "Other",
"type": "Node Type",
"version": "Version",
"versionPlaceholder": "x.y.z",
"keys": "Keywords",
"keysPlaceholder": "Comma-separated keywords",
"author": "Author",
"authorPlaceholder": "Your Name <email@example.com>",
"desc": "Description",
"env": { "env": {
"restore": "Restore to subflow default", "restore": "Restore to subflow default",
"remove": "Remove environment variable" "remove": "Remove environment variable"
@ -385,6 +399,7 @@
"icon": "Icon", "icon": "Icon",
"inputType": "Input type", "inputType": "Input type",
"selectType": "select types...", "selectType": "select types...",
"loadCredentials": "Loading node credentials",
"inputs" : { "inputs" : {
"input": "input", "input": "input",
"select": "select", "select": "select",
@ -419,7 +434,8 @@
}, },
"errors": { "errors": {
"scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it", "scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it",
"invalidProperties": "Invalid properties:" "invalidProperties": "Invalid properties:",
"credentialLoadFailed": "Failed to load node credentials"
} }
}, },
"keyboard": { "keyboard": {
@ -1079,6 +1095,7 @@
"editor-tab": { "editor-tab": {
"properties": "Properties", "properties": "Properties",
"envProperties": "Environment Variables", "envProperties": "Environment Variables",
"module": "Module Properties",
"description": "Description", "description": "Description",
"appearance": "Appearance", "appearance": "Appearance",
"preview": "UI Preview", "preview": "UI Preview",

View File

@ -243,19 +243,19 @@
"args": "array, function", "args": "array, function",
"desc": "Returns the one and only value in the `array` parameter that satisfies the `function` predicate (i.e. the `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.\n\nThe function should be supplied in the following signature: `function(value [, index [, array]])` where value is each input of the array, index is the position of that value and the whole array is passed as the third argument" "desc": "Returns the one and only value in the `array` parameter that satisfies the `function` predicate (i.e. the `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.\n\nThe function should be supplied in the following signature: `function(value [, index [, array]])` where value is each input of the array, index is the position of that value and the whole array is passed as the third argument"
}, },
"$encodeUrl": { "$encodeUrlComponent": {
"args": "str", "args": "str",
"desc": "Encodes a Uniform Resource Locator (URL) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.\n\nExample: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" "desc": "Encodes a Uniform Resource Locator (URL) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.\n\nExample: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
}, },
"$encodeUrlComponent": { "$encodeUrl": {
"args": "str", "args": "str",
"desc": "Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character. \n\nExample: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" "desc": "Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character. \n\nExample: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
}, },
"$decodeUrl": { "$decodeUrlComponent": {
"args": "str", "args": "str",
"desc": "Decodes a Uniform Resource Locator (URL) component previously created by encodeUrlComponent. \n\nExample: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" "desc": "Decodes a Uniform Resource Locator (URL) component previously created by encodeUrlComponent. \n\nExample: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
}, },
"$decodeUrlComponent": { "$decodeUrl": {
"args": "str", "args": "str",
"desc": "Decodes a Uniform Resource Locator (URL) previously created by encodeUrl. \n\nExample: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" "desc": "Decodes a Uniform Resource Locator (URL) previously created by encodeUrl. \n\nExample: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
}, },

View File

@ -38,6 +38,7 @@
} }
}, },
"event": { "event": {
"loadPlugins": "プラグインを読み込み中",
"loadPalette": "パレットを読み込み中", "loadPalette": "パレットを読み込み中",
"loadNodeCatalogs": "ノードカタログを読み込み中", "loadNodeCatalogs": "ノードカタログを読み込み中",
"loadNodes": "ノードを読み込み中 __count__", "loadNodes": "ノードを読み込み中 __count__",
@ -85,7 +86,7 @@
"userSettings": "ユーザ設定", "userSettings": "ユーザ設定",
"nodes": "ノード", "nodes": "ノード",
"displayStatus": "ノードのステータスを表示", "displayStatus": "ノードのステータスを表示",
"displayConfig": "ノードの設定", "displayConfig": "設定ノード",
"import": "読み込み", "import": "読み込み",
"export": "書き出し", "export": "書き出し",
"search": "ノードを検索", "search": "ノードを検索",
@ -203,8 +204,8 @@
"replacedNodes_plural": "置換された __count__ 個のノード", "replacedNodes_plural": "置換された __count__ 個のノード",
"pasteNodes": "JSON形式のフローデータを貼り付け", "pasteNodes": "JSON形式のフローデータを貼り付け",
"selectFile": "読み込むファイルを選択", "selectFile": "読み込むファイルを選択",
"importNodes": "フローをクリップボードから読み込み", "importNodes": "フローを読み込み",
"exportNodes": "フローをクリップボードへ書き出し", "exportNodes": "フローを書き出し",
"download": "ダウンロード", "download": "ダウンロード",
"importUnrecognised": "認識できない型が読み込まれました:", "importUnrecognised": "認識できない型が読み込まれました:",
"importUnrecognised_plural": "認識できない型が読み込まれました:", "importUnrecognised_plural": "認識できない型が読み込まれました:",
@ -266,7 +267,7 @@
"successfulDeploy": "デプロイが成功しました", "successfulDeploy": "デプロイが成功しました",
"successfulRestart": "フローの再起動が成功しました", "successfulRestart": "フローの再起動が成功しました",
"deployFailed": "デプロイが失敗しました: __message__", "deployFailed": "デプロイが失敗しました: __message__",
"unusedConfigNodes": "使われていない「ノードの設定」があります。", "unusedConfigNodes": "使われていない設定ノードがあります。",
"unusedConfigNodesLink": "設定を参照する", "unusedConfigNodesLink": "設定を参照する",
"errors": { "errors": {
"noResponse": "サーバの応答がありません" "noResponse": "サーバの応答がありません"
@ -337,8 +338,21 @@
"output": "出力:", "output": "出力:",
"status": "ステータスノード", "status": "ステータスノード",
"deleteSubflow": "サブフローを削除", "deleteSubflow": "サブフローを削除",
"confirmDelete": "このサブフローを削除しても良いですか?",
"info": "詳細", "info": "詳細",
"category": "カテゴリ", "category": "カテゴリ",
"module": "モジュール",
"license": "ライセンス",
"licenseNone": "なし",
"licenseOther": "その他",
"type": "ノードの型",
"version": "バージョン",
"versionPlaceholder": "x.y.z",
"keys": "キーワード",
"keysPlaceholder": "カンマ区切りのキーワード",
"author": "作者",
"authorPlaceholder": "名前 <email@example.com>",
"desc": "説明",
"env": { "env": {
"restore": "デフォルト値に戻す", "restore": "デフォルト値に戻す",
"remove": "環境変数を削除" "remove": "環境変数を削除"
@ -362,9 +376,9 @@
"configDelete": "削除", "configDelete": "削除",
"nodesUse": "__count__ 個のノードが、この設定を使用しています", "nodesUse": "__count__ 個のノードが、この設定を使用しています",
"nodesUse_plural": "__count__ 個のノードが、この設定を使用しています", "nodesUse_plural": "__count__ 個のノードが、この設定を使用しています",
"addNewConfig": "新規に __type__ ノードの設定を追加", "addNewConfig": "新規に __type__ 設定ノードを追加",
"editNode": "__type__ ノードを編集", "editNode": "__type__ ノードを編集",
"editConfig": "__type__ ノードの設定を編集", "editConfig": "__type__ 設定ノードを編集",
"addNewType": "新規に __type__ を追加...", "addNewType": "新規に __type__ を追加...",
"nodeProperties": "プロパティ", "nodeProperties": "プロパティ",
"label": "ラベル", "label": "ラベル",
@ -385,6 +399,7 @@
"icon": "記号", "icon": "記号",
"inputType": "入力形式", "inputType": "入力形式",
"selectType": "形式選択...", "selectType": "形式選択...",
"loadCredentials": "ノードの認証情報を読み込み中",
"inputs": { "inputs": {
"input": "入力", "input": "入力",
"select": "メニュー", "select": "メニュー",
@ -419,7 +434,8 @@
}, },
"errors": { "errors": {
"scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします", "scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします",
"invalidProperties": "プロパティが不正です:" "invalidProperties": "プロパティが不正です:",
"credentialLoadFailed": "ノードの認証情報の読み込みに失敗"
} }
}, },
"keyboard": { "keyboard": {
@ -637,8 +653,8 @@
"noHelp": "ヘルプのトピックが未選択" "noHelp": "ヘルプのトピックが未選択"
}, },
"config": { "config": {
"name": "ノードの設定を表示", "name": "設定ノードを表示",
"label": "ノードの設定", "label": "設定ノード",
"global": "全てのフロー上", "global": "全てのフロー上",
"none": "なし", "none": "なし",
"subflows": "サブフロー", "subflows": "サブフロー",
@ -1079,6 +1095,7 @@
"editor-tab": { "editor-tab": {
"properties": "プロパティ", "properties": "プロパティ",
"envProperties": "環境変数", "envProperties": "環境変数",
"module": "モジュールプロパティ",
"description": "説明", "description": "説明",
"appearance": "外観", "appearance": "外観",
"preview": "UIプレビュー", "preview": "UIプレビュー",
@ -1089,6 +1106,7 @@
"en-US": "英語", "en-US": "英語",
"ja": "日本語", "ja": "日本語",
"ko": "韓国語", "ko": "韓国語",
"ru": "ロシア語",
"zh-CN": "中国語(簡体)", "zh-CN": "中国語(簡体)",
"zh-TW": "中国語(繁体)" "zh-TW": "中国語(繁体)"
} }

View File

@ -243,19 +243,19 @@
"args": "array, function", "args": "array, function",
"desc": "`array`の要素のうち、条件判定関数`function`を満たす(`function`に与えた場合に真偽値`true`を返す)要素が1つのみである場合、それを返します。マッチする要素が1つのみでない場合、例外を送出します。\n\n指定する関数は`function(value [, index [, array]])`というシグネチャでなければなりません。ここで、`value`は`array`の要素値、`index`は要素の添字、第三引数には配列全体を渡します。" "desc": "`array`の要素のうち、条件判定関数`function`を満たす(`function`に与えた場合に真偽値`true`を返す)要素が1つのみである場合、それを返します。マッチする要素が1つのみでない場合、例外を送出します。\n\n指定する関数は`function(value [, index [, array]])`というシグネチャでなければなりません。ここで、`value`は`array`の要素値、`index`は要素の添字、第三引数には配列全体を渡します。"
}, },
"$encodeUrl": { "$encodeUrlComponent": {
"args": "str", "args": "str",
"desc": "Uniform Resource Locator (URL)を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" "desc": "Uniform Resource Locator (URL)を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
}, },
"$encodeUrlComponent": { "$encodeUrl": {
"args": "str", "args": "str",
"desc": "Uniform Resource Locator (URL)要素を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" "desc": "Uniform Resource Locator (URL)要素を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
}, },
"$decodeUrl": { "$decodeUrlComponent": {
"args": "str", "args": "str",
"desc": "encodeUrlComponentで置換したUniform Resource Locator (URL)をデコードします。\n\n例: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" "desc": "encodeUrlComponentで置換したUniform Resource Locator (URL)をデコードします。\n\n例: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
}, },
"$decodeUrlComponent": { "$decodeUrl": {
"args": "str", "args": "str",
"desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。 \n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" "desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。 \n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
}, },

View File

@ -246,8 +246,8 @@
"import": { "import": {
"import": "Импортировать в", "import": "Импортировать в",
"importSelected": "Импортировать выбранные", "importSelected": "Импортировать выбранные",
"importCopy": "Импортировать копию", "importCopy": "Импортировать копии",
"viewNodes": "Посмотреть узлы...", "viewNodes": "Показать узлы...",
"newFlow": "новый поток", "newFlow": "новый поток",
"replace": "заменить", "replace": "заменить",
"errors": { "errors": {
@ -257,7 +257,7 @@
"missingType": "Недопустимый поток - у элемента __index__ отсутствует свойство 'type'" "missingType": "Недопустимый поток - у элемента __index__ отсутствует свойство 'type'"
}, },
"conflictNotification1": "Некоторые импортируемые Вами узлы уже существуют в рабочей области.", "conflictNotification1": "Некоторые импортируемые Вами узлы уже существуют в рабочей области.",
"conflictNotification2": "Выберите, какие узлы импортировать и следует ли заменить существующие узлы или импортировать их копию." "conflictNotification2": "Выберите, какие узлы импортировать и следует ли заменить ими существующие узлы или импортировать их копии."
}, },
"copyMessagePath": "Путь скопирован", "copyMessagePath": "Путь скопирован",
"copyMessageValue": "Значение скопировано", "copyMessageValue": "Значение скопировано",
@ -373,12 +373,12 @@
"configAdd": "Добавить", "configAdd": "Добавить",
"configUpdate": "Обновить", "configUpdate": "Обновить",
"configDelete": "Удалить", "configDelete": "Удалить",
"nodesUse": "__count__ узел использует эту конфигурацию", "nodesUse": "__count__ узел использует этот конфиг",
"nodesUse_plural_2": "__count__ узла используют эту конфигурацию", "nodesUse_plural_2": "__count__ узла используют этот конфиг",
"nodesUse_plural_5": "__count__ узлов используют эту конфигурацию", "nodesUse_plural_5": "__count__ узлов используют этот конфиг",
"addNewConfig": "Добавить новый конфигурационный узел __type__", "addNewConfig": "Добавить новый конфиг узел __type__",
"editNode": "Изменить узел __type__", "editNode": "Изменить узел __type__",
"editConfig": "Изменить конфигурационный узел __type__", "editConfig": "Изменить конфиг узел __type__",
"addNewType": "Добавить новый __type__...", "addNewType": "Добавить новый __type__...",
"nodeProperties": "свойства узла", "nodeProperties": "свойства узла",
"label": "Метка", "label": "Метка",
@ -615,7 +615,7 @@
"info": { "info": {
"name": "Информация", "name": "Информация",
"tabName": "Имя", "tabName": "Имя",
"label": "сведения", "label": "инфо",
"node": "Узел", "node": "Узел",
"type": "Тип", "type": "Тип",
"group": "Группа", "group": "Группа",
@ -648,8 +648,8 @@
"showTips":"Вы можете открыть советы из панели настроек", "showTips":"Вы можете открыть советы из панели настроек",
"outline": "Структура", "outline": "Структура",
"empty": "пусто", "empty": "пусто",
"globalConfig": "Узлы глобальной конфигурации", "globalConfig": "Глобальные конфиг узлы",
"triggerAction": "Запустить действие", "triggerAction": "Вызвать действие",
"find": "Найти в рабочей области", "find": "Найти в рабочей области",
"search": { "search": {
"configNodes": "Узлы конфигурации", "configNodes": "Узлы конфигурации",
@ -671,8 +671,8 @@
}, },
"config": { "config": {
"name": "Узлы конфигураций", "name": "Узлы конфигураций",
"label": "конфигурация", "label": "конфиг",
"global": "На всех потока", "global": "На всех потоках",
"none": "нет", "none": "нет",
"subflows": "подпотоки", "subflows": "подпотоки",
"flows": "потоки", "flows": "потоки",
@ -690,8 +690,8 @@
"none": "ничего не выбрано", "none": "ничего не выбрано",
"refresh": "обновите, чтобы загрузить", "refresh": "обновите, чтобы загрузить",
"empty": "пусто", "empty": "пусто",
"node": "Узел", "node": "Узловой",
"flow": "Поток", "flow": "Потоковый",
"global": "Глобальный", "global": "Глобальный",
"deleteConfirm": "Вы уверены, что хотите удалить этот элемент?", "deleteConfirm": "Вы уверены, что хотите удалить этот элемент?",
"autoRefresh": "Обновить при изменении выбора", "autoRefresh": "Обновить при изменении выбора",
@ -877,7 +877,7 @@
"bool": "логический тип", "bool": "логический тип",
"json": "JSON", "json": "JSON",
"bin": "буфер", "bin": "буфер",
"date": "отметка времени", "date": "метка времени",
"jsonata": "выражение", "jsonata": "выражение",
"env": "переменная среды", "env": "переменная среды",
"cred": "учетные данные" "cred": "учетные данные"

View File

@ -243,19 +243,19 @@
"args": "array, function", "args": "array, function",
"desc": "Возвращает одно-единственное значение из массива `array`, которое удовлетворяет предикату `function` (то есть когда `function` возвращает логическое `true` при передаче значения). Выдает исключение, если число подходящих значений не одно.\n\nФункция должна соответствовать следующей сигнатуре: `function(value [, index [, array]])` где value - элемент массива, index - позиция этого значения, а весь массив передается в качестве третьего аргумента" "desc": "Возвращает одно-единственное значение из массива `array`, которое удовлетворяет предикату `function` (то есть когда `function` возвращает логическое `true` при передаче значения). Выдает исключение, если число подходящих значений не одно.\n\nФункция должна соответствовать следующей сигнатуре: `function(value [, index [, array]])` где value - элемент массива, index - позиция этого значения, а весь массив передается в качестве третьего аргумента"
}, },
"$encodeUrl": { "$encodeUrlComponent": {
"args": "str", "args": "str",
"desc": "Кодирует компонент Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" "desc": "Кодирует компонент Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
}, },
"$encodeUrlComponent": { "$encodeUrl": {
"args": "str", "args": "str",
"desc": "Кодирует Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" "desc": "Кодирует Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
}, },
"$decodeUrl": { "$decodeUrlComponent": {
"args": "str", "args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrlComponent.\n\nПример: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" "desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrlComponent.\n\nПример: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
}, },
"$decodeUrlComponent": { "$decodeUrl": {
"args": "str", "args": "str",
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" "desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
}, },

View File

@ -243,19 +243,19 @@
"args": "array, function", "args": "array, function",
"desc": "返回满足参数function谓语的array参数中的唯一值 (比如传递值时函数返回布尔值“true”)。如果匹配值的数量不唯一时,则抛出异常。\n\n应在以下签名中提供函数 `functionvalue [index [array []]]` 其中value是数组的每个输入index是该值的位置整个数组作为第三个参数传递。" "desc": "返回满足参数function谓语的array参数中的唯一值 (比如传递值时函数返回布尔值“true”)。如果匹配值的数量不唯一时,则抛出异常。\n\n应在以下签名中提供函数 `functionvalue [index [array []]]` 其中value是数组的每个输入index是该值的位置整个数组作为第三个参数传递。"
}, },
"$encodeUrl": { "$encodeUrlComponent": {
"args": "str", "args": "str",
"desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL组件进行编码。\n\n示例 `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" "desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL组件进行编码。\n\n示例 `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
}, },
"$encodeUrlComponent": { "$encodeUrl": {
"args": "str", "args": "str",
"desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL进行编码。\n\n示例 `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" "desc": "通过用表示字符的UTF-8编码的一个两个三个或四个转义序列替换某些字符的每个实例对统一资源定位符URL进行编码。\n\n示例 `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
}, },
"$decodeUrl": { "$decodeUrlComponent": {
"args": "str", "args": "str",
"desc": "解码以前由encodeUrlComponent创建的统一资源定位器URL组件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" "desc": "解码以前由encodeUrlComponent创建的统一资源定位器URL组件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
}, },
"$decodeUrlComponent": { "$decodeUrl": {
"args": "str", "args": "str",
"desc": "解码先前由encodeUrl创建的统一资源定位符URL。 \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" "desc": "解码先前由encodeUrl创建的统一资源定位符URL。 \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
}, },

View File

@ -243,19 +243,19 @@
"args": "array, function", "args": "array, function",
"desc": "返回滿足參數function謂語的array參數中的唯一值 (比如傳遞值時函數返回布林值“true”)。如果匹配值的數量不唯一時,則拋出異常。\n\n應在以下簽名中提供函數`functionvalue [index [array []]]`其中value是數組的每個輸入index是該值的位置整個數組作為第三個參數傳遞。" "desc": "返回滿足參數function謂語的array參數中的唯一值 (比如傳遞值時函數返回布林值“true”)。如果匹配值的數量不唯一時,則拋出異常。\n\n應在以下簽名中提供函數`functionvalue [index [array []]]`其中value是數組的每個輸入index是該值的位置整個數組作為第三個參數傳遞。"
}, },
"$encodeUrl": { "$encodeUrlComponent": {
"args": "str", "args": "str",
"desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL組件進行編碼。\n\n示例`$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" "desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL組件進行編碼。\n\n示例`$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
}, },
"$encodeUrlComponent": { "$encodeUrl": {
"args": "str", "args": "str",
"desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL進行編碼。\n\n示例 `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" "desc": "通過用表示字符的UTF-8編碼的一個兩個三個或四個轉義序列替換某些字符的每個實例對統一資源定位符URL進行編碼。\n\n示例 `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
}, },
"$decodeUrl": { "$decodeUrlComponent": {
"args": "str", "args": "str",
"desc": "解碼以前由encodeUrlComponent創建的統一資源定位器URL組件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" "desc": "解碼以前由encodeUrlComponent創建的統一資源定位器URL組件。 \n\n示例 `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
}, },
"$decodeUrlComponent": { "$decodeUrl": {
"args": "str", "args": "str",
"desc": "解碼先前由encodeUrl創建的統一資源定位符URL。 \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" "desc": "解碼先前由encodeUrl創建的統一資源定位符URL。 \n\n示例 `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
}, },

View File

@ -343,17 +343,29 @@ RED.history = (function() {
if (ev.changes.hasOwnProperty(i)) { if (ev.changes.hasOwnProperty(i)) {
inverseEv.changes[i] = ev.node[i]; inverseEv.changes[i] = ev.node[i];
if (ev.node._def.defaults && ev.node._def.defaults[i] && ev.node._def.defaults[i].type) { if (ev.node._def.defaults && ev.node._def.defaults[i] && ev.node._def.defaults[i].type) {
// This is a config node property // This property is a reference to another node or nodes.
var currentConfigNode = RED.nodes.node(ev.node[i]); var nodeList = ev.node[i];
if (currentConfigNode) { if (!Array.isArray(nodeList)) {
nodeList = [nodeList];
}
nodeList.forEach(function(id) {
var currentConfigNode = RED.nodes.node(id);
if (currentConfigNode && currentConfigNode._def.category === "config") {
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
RED.events.emit("nodes:change",currentConfigNode); RED.events.emit("nodes:change",currentConfigNode);
} }
var newConfigNode = RED.nodes.node(ev.changes[i]); });
if (newConfigNode) { nodeList = ev.changes[i];
if (!Array.isArray(nodeList)) {
nodeList = [nodeList];
}
nodeList.forEach(function(id) {
var newConfigNode = RED.nodes.node(id);
if (newConfigNode && newConfigNode._def.category === "config") {
newConfigNode.users.push(ev.node); newConfigNode.users.push(ev.node);
RED.events.emit("nodes:change",newConfigNode); RED.events.emit("nodes:change",newConfigNode);
} }
});
} }
ev.node[i] = ev.changes[i]; ev.node[i] = ev.changes[i];
} }

View File

@ -108,6 +108,31 @@ RED.i18n = (function() {
} }
}); });
}) })
},
loadPluginCatalogs: function(done) {
var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage());
var toLoad = languageList.length;
languageList.forEach(function(lang) {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: apiRootUrl+'plugins/messages?lng='+lang,
success: function(data) {
var namespaces = Object.keys(data);
namespaces.forEach(function(ns) {
i18n.addResourceBundle(lang,ns,data[ns]);
});
toLoad--;
if (toLoad === 0) {
done();
}
}
});
})
} }
} }
})(); })();

View File

@ -164,6 +164,21 @@ RED.nodes = (function() {
// TODO: too tightly coupled into palette UI // TODO: too tightly coupled into palette UI
} }
if (def.defaults) {
for (var d in def.defaults) {
if (def.defaults.hasOwnProperty(d)) {
if (def.defaults[d].type) {
try {
def.defaults[d]._type = parseNodePropertyTypeString(def.defaults[d].type)
} catch(err) {
console.warn(err);
}
}
}
}
}
RED.events.emit("registry:node-type-added",nt); RED.events.emit("registry:node-type-added",nt);
}, },
removeNodeType: function(nt) { removeNodeType: function(nt) {
@ -193,6 +208,59 @@ RED.nodes = (function() {
return (1+Math.random()*4294967295).toString(16); return (1+Math.random()*4294967295).toString(16);
} }
function parseNodePropertyTypeString(typeString) {
typeString = typeString.trim();
var c;
var pos = 0;
var isArray = /\[\]$/.test(typeString);
if (isArray) {
typeString = typeString.substring(0,typeString.length-2);
}
var l = typeString.length;
var inBrackets = false;
var inToken = false;
var currentToken = "";
var types = [];
while (pos < l) {
c = typeString[pos];
if (inToken) {
if (c === "|") {
types.push(currentToken.trim())
currentToken = "";
inToken = false;
} else if (c === ")") {
types.push(currentToken.trim())
currentToken = "";
inBrackets = false;
inToken = false;
} else {
currentToken += c;
}
} else {
if (c === "(") {
if (inBrackets) {
throw new Error("Invalid character '"+c+"' at position "+pos)
}
inBrackets = true;
} else if (c !== " ") {
inToken = true;
currentToken = c;
}
}
pos++;
}
currentToken = currentToken.trim();
if (currentToken.length > 0) {
types.push(currentToken)
}
return {
types: types,
array: isArray
}
}
function addNode(n) { function addNode(n) {
if (n.type.indexOf("subflow") !== 0) { if (n.type.indexOf("subflow") !== 0) {
n["_"] = n._def._; n["_"] = n._def._;
@ -670,6 +738,7 @@ RED.nodes = (function() {
node.in = []; node.in = [];
node.out = []; node.out = [];
node.env = n.env; node.env = n.env;
node.meta = n.meta;
if (exportCreds) { if (exportCreds) {
var credentialSet = {}; var credentialSet = {};
@ -780,6 +849,12 @@ RED.nodes = (function() {
subflowSet.push(n); subflowSet.push(n);
} }
}); });
RED.nodes.eachConfig(function(n) {
if (n.z == subflowId) {
subflowSet.push(n);
exportedConfigNodes[n.id] = true;
}
});
var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes); var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes);
nns = exportableSubflow.concat(nns); nns = exportableSubflow.concat(nns);
} }
@ -787,16 +862,29 @@ RED.nodes = (function() {
if (node.type !== "subflow") { if (node.type !== "subflow") {
var convertedNode = RED.nodes.convertNode(node); var convertedNode = RED.nodes.convertNode(node);
for (var d in node._def.defaults) { for (var d in node._def.defaults) {
if (node._def.defaults[d].type && node[d] in configNodes) { if (node._def.defaults[d].type) {
var confNode = configNodes[node[d]]; var nodeList = node[d];
var exportable = registry.getNodeType(node._def.defaults[d].type).exportable; if (!Array.isArray(nodeList)) {
if ((exportable == null || exportable)) { nodeList = [nodeList];
if (!(node[d] in exportedConfigNodes)) {
exportedConfigNodes[node[d]] = true;
set.push(confNode);
} }
nodeList = nodeList.filter(function(id) {
if (id in configNodes) {
var confNode = configNodes[id];
if (confNode._def.exportable !== false) {
if (!(id in exportedConfigNodes)) {
exportedConfigNodes[id] = true;
set.push(confNode);
return true;
}
}
return false;
}
return true;
})
if (nodeList.length === 0) {
convertedNode[d] = Array.isArray(node[d])?[]:""
} else { } else {
convertedNode[d] = ""; convertedNode[d] = Array.isArray(node[d])?nodeList:nodeList[0]
} }
} }
} }
@ -1360,7 +1448,7 @@ RED.nodes = (function() {
} }
} }
} else { } else {
if (n.z && !workspaces[n.z]) { if (n.z && !workspaces[n.z] && !subflow_map[n.z]) {
n.z = activeWorkspace; n.z = activeWorkspace;
} }
} }
@ -1587,15 +1675,6 @@ RED.nodes = (function() {
} }
} }
} }
// TODO: make this a part of the node definition so it doesn't have to
// be hardcoded here
var nodeTypeArrayReferences = {
"catch":"scope",
"status":"scope",
"complete": "scope",
"link in":"links",
"link out":"links"
}
// Remap all wires and config node references // Remap all wires and config node references
for (i=0;i<new_nodes.length;i++) { for (i=0;i<new_nodes.length;i++) {
@ -1624,19 +1703,24 @@ RED.nodes = (function() {
} }
for (var d3 in n._def.defaults) { for (var d3 in n._def.defaults) {
if (n._def.defaults.hasOwnProperty(d3)) { if (n._def.defaults.hasOwnProperty(d3)) {
if (n._def.defaults[d3].type && node_map[n[d3]]) { if (n._def.defaults[d3].type) {
configNode = node_map[n[d3]]; var nodeList = n[d3];
n[d3] = configNode.id; if (!Array.isArray(nodeList)) {
if (configNode.users.indexOf(n) === -1) { nodeList = [nodeList];
configNode.users.push(n);
} }
} else if (nodeTypeArrayReferences.hasOwnProperty(n.type) && nodeTypeArrayReferences[n.type] === d3 && n[d3] !== undefined && n[d3] !== null) { nodeList = nodeList.map(function(id) {
for (var j = 0;j<n[d3].length;j++) { var node = node_map[id];
if (node_map[n[d3][j]]) { if (node) {
n[d3][j] = node_map[n[d3][j]].id; if (node._def.category === 'config') {
if (node.users.indexOf(n) === -1) {
node.users.push(n);
} }
} }
return node.id;
}
return id;
})
n[d3] = Array.isArray(n[d3])?nodeList:nodeList[0];
} }
} }
} }
@ -1920,6 +2004,18 @@ RED.nodes = (function() {
RED.events.emit("groups:remove",group); RED.events.emit("groups:remove",group);
} }
function getNodeHelp(type) {
var helpContent = "";
var helpElement = $("script[data-help-name='"+type+"']");
if (helpElement) {
helpContent = helpElement.html();
var helpType = helpElement.attr("type");
if (helpType === "text/markdown") {
helpContent = RED.utils.renderMarkdown(helpContent);
}
}
return helpContent;
}
return { return {
init: function() { init: function() {
@ -1999,6 +2095,7 @@ RED.nodes = (function() {
registerType: registry.registerNodeType, registerType: registry.registerNodeType,
getType: registry.getNodeType, getType: registry.getNodeType,
getNodeHelp: getNodeHelp,
convertNode: convertNode, convertNode: convertNode,
add: addNode, add: addNode,

View File

@ -0,0 +1,46 @@
RED.plugins = (function() {
var plugins = {};
var pluginsByType = {};
function registerPlugin(id,definition) {
plugins[id] = definition;
if (definition.type) {
pluginsByType[definition.type] = pluginsByType[definition.type] || [];
pluginsByType[definition.type].push(definition);
}
if (RED._loadingModule) {
definition.module = RED._loadingModule;
definition["_"] = function() {
var args = Array.prototype.slice.call(arguments);
var originalKey = args[0];
if (!/:/.test(args[0])) {
args[0] = definition.module+":"+args[0];
}
var result = RED._.apply(null,args);
if (result === args[0]) {
return originalKey;
}
return result;
}
} else {
definition["_"] = RED["_"]
}
if (definition.onadd && typeof definition.onadd === 'function') {
definition.onadd();
}
RED.events.emit("registry:plugin-added",id);
}
function getPlugin(id) {
return plugins[id]
}
function getPluginsByType(type) {
return pluginsByType[type] || [];
}
return {
registerPlugin: registerPlugin,
getPlugin: getPlugin,
getPluginsByType: getPluginsByType
}
})();

View File

@ -52,6 +52,5 @@
Set.prototype = _Set.prototype; Set.prototype = _Set.prototype;
Set.prototype.constructor = Set; Set.prototype.constructor = Set;
} }
} }
})(); })();

View File

@ -15,19 +15,65 @@
**/ **/
var RED = (function() { var RED = (function() {
function appendNodeConfig(nodeConfig,done) {
function loadPluginList() {
loader.reportProgress(RED._("event.loadPlugins"), 10)
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'plugins',
success: function(data) {
loader.reportProgress(RED._("event.loadPlugins"), 13)
RED.i18n.loadPluginCatalogs(function() {
loadPlugins(function() {
loadNodeList();
});
});
}
});
}
function loadPlugins(done) {
loader.reportProgress(RED._("event.loadPlugins",{count:""}), 17)
var lang = localStorage.getItem("editor-language")||i18n.detectLanguage();
$.ajax({
headers: {
"Accept":"text/html",
"Accept-Language": lang
},
cache: false,
url: 'plugins',
success: function(data) {
var configs = data.trim().split(/(?=<!-- --- \[red-plugin:\S+\] --- -->)/);
var totalCount = configs.length;
var stepConfig = function() {
// loader.reportProgress(RED._("event.loadNodes",{count:(totalCount-configs.length)+"/"+totalCount}), 30 + ((totalCount-configs.length)/totalCount)*40 )
if (configs.length === 0) {
done();
} else {
var config = configs.shift();
appendPluginConfig(config,stepConfig);
}
}
stepConfig();
}
});
}
function appendConfig(config, moduleIdMatch, targetContainer, done) {
done = done || function(){}; done = done || function(){};
var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
var moduleId; var moduleId;
if (m) { if (moduleIdMatch) {
moduleId = m[1]; moduleId = moduleIdMatch[1];
RED._loadingModule = moduleId;
} else { } else {
moduleId = "unknown"; moduleId = "unknown";
} }
try { try {
var hasDeferred = false; var hasDeferred = false;
var nodeConfigEls = $("<div>"+config+"</div>");
var nodeConfigEls = $("<div>"+nodeConfig+"</div>");
var scripts = nodeConfigEls.find("script"); var scripts = nodeConfigEls.find("script");
var scriptCount = scripts.length; var scriptCount = scripts.length;
scripts.each(function(i,el) { scripts.each(function(i,el) {
@ -38,14 +84,15 @@ var RED = (function() {
newScript.onload = function() { newScript.onload = function() {
scriptCount--; scriptCount--;
if (scriptCount === 0) { if (scriptCount === 0) {
$("#red-ui-editor-node-configs").append(nodeConfigEls); $(targetContainer).append(nodeConfigEls);
delete RED._loadingModule;
done() done()
} }
} }
if ($(el).attr('type') === "module") { if ($(el).attr('type') === "module") {
newScript.type = "module"; newScript.type = "module";
} }
$("#red-ui-editor-node-configs").append(newScript); $(targetContainer).append(newScript);
newScript.src = RED.settings.apiRootUrl+srcUrl; newScript.src = RED.settings.apiRootUrl+srcUrl;
hasDeferred = true; hasDeferred = true;
} else { } else {
@ -61,7 +108,8 @@ var RED = (function() {
} }
}) })
if (!hasDeferred) { if (!hasDeferred) {
$("#red-ui-editor-node-configs").append(nodeConfigEls); $(targetContainer).append(nodeConfigEls);
delete RED._loadingModule;
done(); done();
} }
} catch(err) { } catch(err) {
@ -70,9 +118,27 @@ var RED = (function() {
timeout: 10000 timeout: 10000
}); });
console.log("["+moduleId+"] "+err.toString()); console.log("["+moduleId+"] "+err.toString());
delete RED._loadingModule;
done(); done();
} }
} }
function appendPluginConfig(pluginConfig,done) {
appendConfig(
pluginConfig,
/<!-- --- \[red-plugin:(\S+)\] --- -->/.exec(pluginConfig.trim()),
"#red-ui-editor-plugin-configs",
done
);
}
function appendNodeConfig(nodeConfig,done) {
appendConfig(
nodeConfig,
/<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim()),
"#red-ui-editor-node-configs",
done
);
}
function loadNodeList() { function loadNodeList() {
loader.reportProgress(RED._("event.loadPalette"), 20) loader.reportProgress(RED._("event.loadPalette"), 20)
@ -186,6 +252,7 @@ var RED = (function() {
RED.workspaces.show(currentHash.substring(6)); RED.workspaces.show(currentHash.substring(6));
} }
} catch(err) { } catch(err) {
console.warn(err);
RED.notify( RED.notify(
RED._("event.importError", {message: err.message}), RED._("event.importError", {message: err.message}),
{ {
@ -269,7 +336,7 @@ var RED = (function() {
} }
} }
] ]
// } else if (RED.settings.theme('palette.editable') !== false) { // } else if (RED.settings.get('externalModules.palette.allowInstall', true) !== false) {
} else { } else {
options.buttons = [ options.buttons = [
{ {
@ -509,7 +576,7 @@ var RED = (function() {
]}); ]});
menuOptions.push(null); menuOptions.push(null);
if (RED.settings.theme('palette.editable') !== false) { if (RED.settings.get('externalModules.palette.allowInstall', true) !== false) {
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"}); menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
menuOptions.push(null); menuOptions.push(null);
} }
@ -544,7 +611,7 @@ var RED = (function() {
RED.palette.init(); RED.palette.init();
RED.eventLog.init(); RED.eventLog.init();
if (RED.settings.theme('palette.editable') !== false) { if (RED.settings.get('externalModules.palette.allowInstall', true) !== false) {
RED.palette.editor.init(); RED.palette.editor.init();
} else { } else {
console.log("Palette editor disabled"); console.log("Palette editor disabled");
@ -579,7 +646,7 @@ var RED = (function() {
RED.actions.add("core:show-about", showAbout); RED.actions.add("core:show-about", showAbout);
loadNodeList(); loadPluginList();
} }
@ -595,6 +662,7 @@ var RED = (function() {
'<div id="red-ui-sidebar"></div>'+ '<div id="red-ui-sidebar"></div>'+
'<div id="red-ui-sidebar-separator"></div>'+ '<div id="red-ui-sidebar-separator"></div>'+
'</div>').appendTo(options.target); '</div>').appendTo(options.target);
$('<div id="red-ui-editor-plugin-configs"></div>').appendTo(options.target);
$('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target); $('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target);
$('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target); $('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target);
@ -613,9 +681,12 @@ var RED = (function() {
$('<span>').html(theme.header.title).appendTo(logo); $('<span>').html(theme.header.title).appendTo(logo);
} }
} }
if (theme.themes) {
knownThemes = theme.themes;
}
}); });
} }
var knownThemes = null;
var initialised = false; var initialised = false;
function init(options) { function init(options) {
@ -635,7 +706,13 @@ var RED = (function() {
buildEditor(options); buildEditor(options);
RED.i18n.init(options, function() { RED.i18n.init(options, function() {
RED.settings.init(options, loadEditor); RED.settings.init(options, function() {
if (knownThemes) {
RED.settings.editorTheme = RED.settings.editorTheme || {};
RED.settings.editorTheme.themes = knownThemes;
}
loadEditor();
});
}) })
} }

View File

@ -57,12 +57,11 @@ RED.settings = (function () {
return JSON.parse(localStorage.getItem(key)); return JSON.parse(localStorage.getItem(key));
} else { } else {
var v; var v;
try { try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {}
v = RED.utils.getMessageProperty(userSettings,key);
if (v === undefined) { if (v === undefined) {
v = defaultIfUndefined; try { v = RED.utils.getMessageProperty(RED.settings,key); } catch(err) {}
} }
} catch(err) { if (v === undefined) {
v = defaultIfUndefined; v = defaultIfUndefined;
} }
return v; return v;

View File

@ -30,6 +30,27 @@ RED.clipboard = (function() {
var pendingImportConfig; var pendingImportConfig;
function downloadData(file, data) {
if (window.navigator.msSaveBlob) {
// IE11 workaround
// IE does not support data uri scheme for downloading data
var blob = new Blob([data], {
type: "data:text/plain;charset=utf-8"
});
navigator.msSaveBlob(blob, file);
}
else {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
element.setAttribute('download', file);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
}
function setupDialogs() { function setupDialogs() {
dialog = $('<div id="red-ui-clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>') dialog = $('<div id="red-ui-clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("#red-ui-editor") .appendTo("#red-ui-editor")
@ -56,13 +77,8 @@ RED.clipboard = (function() {
class: "primary", class: "primary",
text: RED._("clipboard.download"), text: RED._("clipboard.download"),
click: function() { click: function() {
var element = document.createElement('a'); var data = $("#red-ui-clipboard-dialog-export-text").val();
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent($("#red-ui-clipboard-dialog-export-text").val())); downloadData("flows.json", data);
element.setAttribute('download', "flows.json");
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
$( this ).dialog( "close" ); $( this ).dialog( "close" );
} }
}, },
@ -72,9 +88,7 @@ RED.clipboard = (function() {
text: RED._("clipboard.export.copy"), text: RED._("clipboard.export.copy"),
click: function() { click: function() {
if (activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") { if (activeTab === "red-ui-clipboard-dialog-export-tab-clipboard") {
$("#red-ui-clipboard-dialog-export-text").select(); copyText($("#red-ui-clipboard-dialog-export-text").val());
document.execCommand("copy");
document.getSelection().removeAllRanges();
RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"}); RED.notify(RED._("clipboard.nodesExported"),{id:"clipboard"});
$( this ).dialog( "close" ); $( this ).dialog( "close" );
} else { } else {
@ -222,7 +236,14 @@ RED.clipboard = (function() {
'</div>'+ '</div>'+
'<div id="red-ui-clipboard-dialog-export-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+ '<div id="red-ui-clipboard-dialog-export-tabs-content" class="red-ui-clipboard-dialog-tabs-content">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+ '<div id="red-ui-clipboard-dialog-export-tab-clipboard" class="red-ui-clipboard-dialog-tab-clipboard">'+
'<div class="form-row" style="height:calc(100% - 30px)">'+ '<div id="red-ui-clipboard-dialog-export-tab-clipboard-tab-bar">'+
'<ul id="red-ui-clipboard-dialog-export-tab-clipboard-tabs"></ul>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-preview">'+
'<div id="red-ui-clipboard-dialog-export-tab-clipboard-preview-list"></div>'+
'</div>'+
'<div class="red-ui-clipboard-dialog-export-tab-clipboard-tab" id="red-ui-clipboard-dialog-export-tab-clipboard-json">'+
'<div class="form-row" style="height:calc(100% - 40px)">'+
'<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+ '<textarea readonly id="red-ui-clipboard-dialog-export-text"></textarea>'+
'</div>'+ '</div>'+
'<div class="form-row" style="text-align: right;">'+ '<div class="form-row" style="text-align: right;">'+
@ -232,6 +253,7 @@ RED.clipboard = (function() {
'</span>'+ '</span>'+
'</div>'+ '</div>'+
'</div>'+ '</div>'+
'</div>'+
'<div id="red-ui-clipboard-dialog-export-tab-library" class="red-ui-clipboard-dialog-tab-library">'+ '<div id="red-ui-clipboard-dialog-export-tab-library" class="red-ui-clipboard-dialog-tab-library">'+
'<div id="red-ui-clipboard-dialog-export-tab-library-browser"></div>'+ '<div id="red-ui-clipboard-dialog-export-tab-library-browser"></div>'+
'<div class="form-row">'+ '<div class="form-row">'+
@ -592,6 +614,30 @@ RED.clipboard = (function() {
}) })
loadFlowLibrary(libraryBrowser,"local",RED._("library.types.local")); loadFlowLibrary(libraryBrowser,"local",RED._("library.types.local"));
var clipboardTabs = RED.tabs.create({
id: "red-ui-clipboard-dialog-export-tab-clipboard-tabs",
onchange: function(tab) {
$(".red-ui-clipboard-dialog-export-tab-clipboard-tab").hide();
$("#" + tab.id).show();
}
});
clipboardTabs.addTab({
id: "red-ui-clipboard-dialog-export-tab-clipboard-preview",
label: RED._("clipboard.exportNodes")
});
clipboardTabs.addTab({
id: "red-ui-clipboard-dialog-export-tab-clipboard-json",
label: RED._("editor.types.json")
});
var previewList = $("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
data: []
})
refreshExportPreview();
$("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select(); $("#red-ui-clipboard-dialog-tab-library-name").val("flows.json").select();
dialogContainer.i18n(); dialogContainer.i18n();
@ -630,10 +676,10 @@ RED.clipboard = (function() {
} }
$(this).parent().children().removeClass('selected'); $(this).parent().children().removeClass('selected');
$(this).addClass('selected'); $(this).addClass('selected');
var type = $(this).attr('id'); var type = $(this).attr('id').substring("red-ui-clipboard-dialog-export-rng-".length);
var flow = ""; var flow = "";
var nodes = null; var nodes = null;
if (type === 'red-ui-clipboard-dialog-export-rng-selected') { if (type === 'selected') {
var selection = RED.workspaces.selection(); var selection = RED.workspaces.selection();
if (selection.length > 0) { if (selection.length > 0) {
nodes = []; nodes = [];
@ -647,14 +693,14 @@ RED.clipboard = (function() {
} }
// Don't include the subflow meta-port nodes in the exported selection // Don't include the subflow meta-port nodes in the exported selection
nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'})); nodes = RED.nodes.createExportableNodeSet(nodes.filter(function(n) { return n.type !== 'subflow'}));
} else if (type === 'red-ui-clipboard-dialog-export-rng-flow') { } else if (type === 'flow') {
var activeWorkspace = RED.workspaces.active(); var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.groups(activeWorkspace); nodes = RED.nodes.groups(activeWorkspace);
nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace})); nodes = nodes.concat(RED.nodes.filterNodes({z:activeWorkspace}));
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace); var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode); nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes); nodes = RED.nodes.createExportableNodeSet(nodes);
} else if (type === 'red-ui-clipboard-dialog-export-rng-full') { } else if (type === 'full') {
nodes = RED.nodes.createCompleteNodeSet(false); nodes = RED.nodes.createCompleteNodeSet(false);
} }
if (nodes !== null) { if (nodes !== null) {
@ -670,8 +716,10 @@ RED.clipboard = (function() {
$("#red-ui-clipboard-dialog-export").addClass('disabled'); $("#red-ui-clipboard-dialog-export").addClass('disabled');
} }
$("#red-ui-clipboard-dialog-export-text").val(flow); $("#red-ui-clipboard-dialog-export-text").val(flow);
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50); setTimeout(function() {
$("#red-ui-clipboard-dialog-export-text").trigger("focus"); $("#red-ui-clipboard-dialog-export-text").scrollTop(0);
refreshExportPreview(type);
},50);
}) })
$("#red-ui-clipboard-dialog-ok").hide(); $("#red-ui-clipboard-dialog-ok").hide();
@ -717,6 +765,93 @@ RED.clipboard = (function() {
} }
function refreshExportPreview(type) {
var flowData = $("#red-ui-clipboard-dialog-export-text").val() || "[]";
var flow = JSON.parse(flowData);
var flows = {};
var subflows = {};
var nodes = [];
var nodesByZ = {};
var treeFlows = [];
var treeSubflows = [];
flow.forEach(function(node) {
if (node.type === "tab") {
flows[node.id] = {
element: getFlowLabel(node,false),
deferBuild: type !== "flow",
expanded: type === "flow",
children: []
};
treeFlows.push(flows[node.id])
} else if (node.type === "subflow") {
subflows[node.id] = {
element: getNodeLabel(node,false),
deferBuild: true,
children: []
};
treeSubflows.push(subflows[node.id])
} else {
nodes.push(node);
}
});
var globalNodes = [];
var parentlessNodes = [];
nodes.forEach(function(node) {
var treeNode = {
element: getNodeLabel(node, false, false)
};
if (node.z) {
if (!flows[node.z] && !subflows[node.z]) {
parentlessNodes.push(treeNode)
} else if (flows[node.z]) {
flows[node.z].children.push(treeNode)
} else if (subflows[node.z]) {
subflows[node.z].children.push(treeNode)
}
} else {
globalNodes.push(treeNode);
}
});
var treeData = [];
if (parentlessNodes.length > 0) {
treeData = treeData.concat(parentlessNodes);
}
if (type === "flow") {
treeData = treeData.concat(treeFlows);
} else if (treeFlows.length > 0) {
treeData.push({
label: RED._("menu.label.flows"),
deferBuild: treeFlows.length > 20,
expanded: treeFlows.length <= 20,
children: treeFlows
})
}
if (treeSubflows.length > 0) {
treeData.push({
label: RED._("menu.label.subflows"),
deferBuild: treeSubflows.length > 10,
expanded: treeSubflows.length <= 10,
children: treeSubflows
})
}
if (globalNodes.length > 0) {
treeData.push({
label: RED._("sidebar.info.globalConfig"),
deferBuild: globalNodes.length > 10,
expanded: globalNodes.length <= 10,
children: globalNodes
})
}
$("#red-ui-clipboard-dialog-export-tab-clipboard-preview-list").treeList('data',treeData);
}
function loadFlowLibrary(browser,library,label) { function loadFlowLibrary(browser,library,label) {
// if (includeExamples) { // if (includeExamples) {
// listing.push({ // listing.push({
@ -756,6 +891,7 @@ RED.clipboard = (function() {
} }
function copyText(value,element,msg) { function copyText(value,element,msg) {
var truncated = false; var truncated = false;
var currentFocus = document.activeElement;
if (typeof value !== "string" ) { if (typeof value !== "string" ) {
value = JSON.stringify(value, function(key,value) { value = JSON.stringify(value, function(key,value) {
if (value !== null && typeof value === 'object') { if (value !== null && typeof value === 'object') {
@ -787,7 +923,7 @@ RED.clipboard = (function() {
if (truncated) { if (truncated) {
msg += "_truncated"; msg += "_truncated";
} }
$("#red-ui-clipboard-hidden").val(value).select(); $("#red-ui-clipboard-hidden").val(value).focus().select();
var result = document.execCommand("copy"); var result = document.execCommand("copy");
if (result && element) { if (result && element) {
var popover = RED.popover.create({ var popover = RED.popover.create({
@ -801,6 +937,10 @@ RED.clipboard = (function() {
},1000); },1000);
popover.open(); popover.open();
} }
$("#red-ui-clipboard-hidden").val("");
if (currentFocus) {
$(currentFocus).focus();
}
return result; return result;
} }
@ -1103,7 +1243,7 @@ RED.clipboard = (function() {
init: function() { init: function() {
setupDialogs(); setupDialogs();
$('<input type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor"); $('<textarea type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
RED.actions.add("core:show-export-dialog",showExportNodes); RED.actions.add("core:show-export-dialog",showExportNodes);
RED.actions.add("core:show-import-dialog",showImportNodes); RED.actions.add("core:show-import-dialog",showImportNodes);

View File

@ -965,6 +965,18 @@
}, },
hide: function() { hide: function() {
this.uiSelect.hide(); this.uiSelect.hide();
},
disable: function(val) {
if(val === true) {
this.uiSelect.attr("disabled", "disabled");
} else if (val === false) {
this.uiSelect.attr("disabled", null); //remove attr
} else {
this.uiSelect.attr("disabled", val); //user value
}
},
disabled: function() {
return this.uiSelect.attr("disabled");
} }
}); });
})(jQuery); })(jQuery);

View File

@ -414,6 +414,7 @@ RED.editor = (function() {
for (var cred in credDefinition) { for (var cred in credDefinition) {
if (credDefinition.hasOwnProperty(cred)) { if (credDefinition.hasOwnProperty(cred)) {
var input = $("#" + prefix + '-' + cred); var input = $("#" + prefix + '-' + cred);
if (input.length > 0) {
var value = input.val(); var value = input.val();
if (credDefinition[cred].type == 'password') { if (credDefinition[cred].type == 'password') {
node.credentials['has_' + cred] = (value !== ""); node.credentials['has_' + cred] = (value !== "");
@ -429,6 +430,7 @@ RED.editor = (function() {
} }
} }
} }
}
return changed; return changed;
} }
@ -442,8 +444,9 @@ RED.editor = (function() {
for (var d in definition.defaults) { for (var d in definition.defaults) {
if (definition.defaults.hasOwnProperty(d)) { if (definition.defaults.hasOwnProperty(d)) {
if (definition.defaults[d].type) { if (definition.defaults[d].type) {
if (!definition.defaults[d]._type.array) {
var configTypeDef = RED.nodes.getType(definition.defaults[d].type); var configTypeDef = RED.nodes.getType(definition.defaults[d].type);
if (configTypeDef) { if (configTypeDef && configTypeDef.category === 'config') {
if (configTypeDef.exclusive) { if (configTypeDef.exclusive) {
prepareConfigNodeButton(node,d,definition.defaults[d].type,prefix); prepareConfigNodeButton(node,d,definition.defaults[d].type,prefix);
} else { } else {
@ -453,6 +456,7 @@ RED.editor = (function() {
console.log("Unknown type:", definition.defaults[d].type); console.log("Unknown type:", definition.defaults[d].type);
preparePropertyEditor(node,d,prefix,definition.defaults); preparePropertyEditor(node,d,prefix,definition.defaults);
} }
}
} else { } else {
preparePropertyEditor(node,d,prefix,definition.defaults); preparePropertyEditor(node,d,prefix,definition.defaults);
} }
@ -465,6 +469,7 @@ RED.editor = (function() {
definition.oneditprepare.call(node); definition.oneditprepare.call(node);
} catch(err) { } catch(err) {
console.log("oneditprepare",node.id,node.type,err.toString()); console.log("oneditprepare",node.id,node.type,err.toString());
console.log(err.stack);
} }
} }
// Now invoke any change handlers added to the fields - passing true // Now invoke any change handlers added to the fields - passing true
@ -491,12 +496,14 @@ RED.editor = (function() {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
completePrepare(); completePrepare();
} else { } else {
$.getJSON(getCredentialsURL(node.type, node.id), function (data) { getNodeCredentials(node.type, node.id, function(data) {
if (data) {
node.credentials = data; node.credentials = data;
node.credentials._ = $.extend(true,{},data); node.credentials._ = $.extend(true,{},data);
if (!/^subflow:/.test(definition.type)) { if (!/^subflow:/.test(definition.type)) {
populateCredentialsInputs(node, definition.credentials, node.credentials, prefix); populateCredentialsInputs(node, definition.credentials, node.credentials, prefix);
} }
}
completePrepare(); completePrepare();
}); });
} }
@ -1083,8 +1090,11 @@ RED.editor = (function() {
node.infoEditor = nodeInfoEditor; node.infoEditor = nodeInfoEditor;
return nodeInfoEditor; return nodeInfoEditor;
} }
var buildingEditDialog = false;
function showEditDialog(node, defaultTab) { function showEditDialog(node, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = node; var editing_node = node;
var isDefaultIcon; var isDefaultIcon;
var defaultIcon; var defaultIcon;
@ -1192,7 +1202,7 @@ RED.editor = (function() {
changed = true; changed = true;
} }
} catch(err) { } catch(err) {
console.log("oneditsave",editing_node.id,editing_node.type,err.toString()); console.warn("oneditsave",editing_node.id,editing_node.type,err.toString());
} }
for (d in editing_node._def.defaults) { for (d in editing_node._def.defaults) {
@ -1609,6 +1619,7 @@ RED.editor = (function() {
if (defaultTab) { if (defaultTab) {
editorTabs.activateTab(defaultTab); editorTabs.activateTab(defaultTab);
} }
buildingEditDialog = false;
done(); done();
}); });
}, },
@ -1660,6 +1671,8 @@ RED.editor = (function() {
* prefix - the input prefix of the parent property * prefix - the input prefix of the parent property
*/ */
function showEditConfigNodeDialog(name,type,id,prefix) { function showEditConfigNodeDialog(name,type,id,prefix) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var adding = (id == "_ADD_"); var adding = (id == "_ADD_");
var node_def = RED.nodes.getType(type); var node_def = RED.nodes.getType(type);
var editing_config_node = RED.nodes.node(id); var editing_config_node = RED.nodes.node(id);
@ -1823,6 +1836,7 @@ RED.editor = (function() {
trayBody.i18n(); trayBody.i18n();
trayFooter.i18n(); trayFooter.i18n();
finishedBuilding = true; finishedBuilding = true;
buildingEditDialog = false;
done(); done();
}); });
}, },
@ -1890,7 +1904,7 @@ RED.editor = (function() {
try { try {
configTypeDef.oneditsave.call(editing_config_node); configTypeDef.oneditsave.call(editing_config_node);
} catch(err) { } catch(err) {
console.log("oneditsave",editing_config_node.id,editing_config_node.type,err.toString()); console.warn("oneditsave",editing_config_node.id,editing_config_node.type,err.toString());
} }
} }
@ -2146,6 +2160,8 @@ RED.editor = (function() {
} }
function showEditSubflowDialog(subflow) { function showEditSubflowDialog(subflow) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = subflow; var editing_node = subflow;
editStack.push(subflow); editStack.push(subflow);
RED.view.state(RED.state.EDITING); RED.view.state(RED.state.EDITING);
@ -2250,6 +2266,14 @@ RED.editor = (function() {
changed = true; changed = true;
} }
var newMeta = RED.subflow.exportSubflowModuleProperties(editing_node);
if (!isSameObj(editing_node.meta,newMeta)) {
changes.meta = editing_node.meta;
editing_node.meta = newMeta;
changed = true;
}
if (changed) { if (changed) {
var wasChanged = editing_node.changed; var wasChanged = editing_node.changed;
editing_node.changed = true; editing_node.changed = true;
@ -2356,6 +2380,16 @@ RED.editor = (function() {
}; };
editorTabs.addTab(nodePropertiesTab); editorTabs.addTab(nodePropertiesTab);
var moduleTab = {
id: "editor-tab-module",
label: RED._("editor-tab.module"),
name: RED._("editor-tab.module"),
content: $('<div>', {class:"red-ui-tray-content"}).appendTo(editorContent).hide(),
iconClass: "fa fa-cube",
};
editorTabs.addTab(moduleTab);
RED.subflow.buildModuleForm(moduleTab.content, editing_node);
var descriptionTab = { var descriptionTab = {
id: "editor-tab-description", id: "editor-tab-description",
label: RED._("editor-tab.description"), label: RED._("editor-tab.description"),
@ -2384,15 +2418,17 @@ RED.editor = (function() {
buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node); buildEditForm(nodePropertiesTab.content,"dialog-form","subflow-template", undefined, editing_node);
trayBody.i18n(); trayBody.i18n();
getNodeCredentials("subflow", subflow.id, function(data) {
$.getJSON(getCredentialsURL("subflow", subflow.id), function (data) { if (data) {
subflow.credentials = data; subflow.credentials = data;
subflow.credentials._ = $.extend(true,{},data); subflow.credentials._ = $.extend(true,{},data);
}
$("#subflow-input-name").val(subflow.name); $("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name")); RED.text.bidi.prepareInput($("#subflow-input-name"));
finishedBuilding = true; finishedBuilding = true;
buildingEditDialog = false;
done(); done();
}); });
}, },
@ -2413,7 +2449,39 @@ RED.editor = (function() {
RED.tray.show(trayOptions); RED.tray.show(trayOptions);
} }
function getNodeCredentials(type, id, done) {
var timeoutNotification;
var intialTimeout = setTimeout(function() {
timeoutNotification = RED.notify($('<p data-i18n="[prepend]editor.loadCredentials"> <img src="red/images/spin.svg"/></p>').i18n(),{fixed: true})
},800);
$.ajax({
url: getCredentialsURL(type,id),
dataType: 'json',
success: function(data) {
if (timeoutNotification) {
timeoutNotification.close();
timeoutNotification = null;
}
clearTimeout(intialTimeout);
done(data);
},
error: function(jqXHR,status,error) {
if (timeoutNotification) {
timeoutNotification.close();
timeoutNotification = null;
}
clearTimeout(intialTimeout);
RED.notify(RED._("editor.errors.credentialLoadFailed"),"error")
done(null);
},
timeout: 30000,
});
}
function showEditGroupDialog(group) { function showEditGroupDialog(group) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = group; var editing_node = group;
editStack.push(group); editStack.push(group);
RED.view.state(RED.state.EDITING); RED.view.state(RED.state.EDITING);
@ -2457,7 +2525,7 @@ RED.editor = (function() {
changed = true; changed = true;
} }
} catch(err) { } catch(err) {
console.log("oneditsave",editing_node.id,editing_node.type,err.toString()); console.warn("oneditsave",editing_node.id,editing_node.type,err.toString());
} }
for (d in editing_node._def.defaults) { for (d in editing_node._def.defaults) {
@ -2637,6 +2705,7 @@ RED.editor = (function() {
prepareEditDialog(group,group._def,"node-input", function() { prepareEditDialog(group,group._def,"node-input", function() {
trayBody.i18n(); trayBody.i18n();
finishedBuilding = true; finishedBuilding = true;
buildingEditDialog = false;
done(); done();
}); });
}, },

View File

@ -329,7 +329,9 @@ RED.palette.editor = (function() {
catalogueLoadStatus.push(err||v); catalogueLoadStatus.push(err||v);
if (!err) { if (!err) {
if (v.modules) { if (v.modules) {
v.modules.forEach(function(m) { var a = false;
v.modules = v.modules.filter(function(m) {
if (checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) {
loadedIndex[m.id] = m; loadedIndex[m.id] = m;
m.index = [m.id]; m.index = [m.id];
if (m.keywords) { if (m.keywords) {
@ -344,6 +346,9 @@ RED.palette.editor = (function() {
m.timestamp = 0; m.timestamp = 0;
} }
m.index = m.index.join(",").toLowerCase(); m.index = m.index.join(",").toLowerCase();
return true;
}
return false;
}) })
loadedList = loadedList.concat(v.modules); loadedList = loadedList.concat(v.modules);
} }
@ -437,11 +442,84 @@ RED.palette.editor = (function() {
return -1 * (A.info.timestamp-B.info.timestamp); return -1 * (A.info.timestamp-B.info.timestamp);
} }
var installAllowList = ['*'];
var installDenyList = [];
function parseModuleList(list) {
list = list || ["*"];
return list.map(function(rule) {
var m = /^(.+?)(?:@(.*))?$/.exec(rule);
var wildcardPos = m[1].indexOf("*");
wildcardPos = wildcardPos===-1?Infinity:wildcardPos;
return {
module: new RegExp("^"+m[1].replace(/\*/g,".*")+"$"),
version: m[2],
wildcardPos: wildcardPos
}
})
}
function checkAgainstList(module,version,list) {
for (var i=0;i<list.length;i++) {
var rule = list[i];
if (rule.module.test(module)) {
// Without a full semver library in the editor,
// we skip the version check.
// Not ideal - but will get caught in the runtime
// if the user tries to install.
return rule;
}
}
}
function checkModuleAllowed(module,version,allowList,denyList) {
if (!allowList && !denyList) {
// Default to allow
return true;
}
if (allowList.length === 0 && denyList.length === 0) {
return true;
}
var allowedRule = checkAgainstList(module,version,allowList);
var deniedRule = checkAgainstList(module,version,denyList);
// console.log("A",allowedRule)
// console.log("D",deniedRule)
if (allowedRule && !deniedRule) {
return true;
}
if (!allowedRule && deniedRule) {
return false;
}
if (!allowedRule && !deniedRule) {
return true;
}
if (allowedRule.wildcardPos !== deniedRule.wildcardPos) {
return allowedRule.wildcardPos > deniedRule.wildcardPos
} else {
// First wildcard in same position.
// Go with the longer matching rule. This isn't going to be 100%
// right, but we are deep into edge cases at this point.
return allowedRule.module.toString().length > deniedRule.module.toString().length
}
return false;
}
function init() { function init() {
if (RED.settings.theme('palette.editable') === false) { if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
return; return;
} }
var settingsAllowList = RED.settings.get("externalModules.palette.allowList")
var settingsDenyList = RED.settings.get("externalModules.palette.denyList")
if (settingsAllowList || settingsDenyList) {
installAllowList = settingsAllowList;
installDenyList = settingsDenyList
}
installAllowList = parseModuleList(installAllowList);
installDenyList = parseModuleList(installDenyList);
createSettingsPane(); createSettingsPane();
RED.userSettings.add({ RED.userSettings.add({
@ -880,7 +958,7 @@ RED.palette.editor = (function() {
} }
}); });
if (RED.settings.theme('palette.upload') !== false) { if (RED.settings.get('externalModules.palette.allowUpload', true) !== false) {
var uploadSpan = $('<span class="button-group">').prependTo(toolBar); var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan); var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
@ -962,7 +1040,7 @@ RED.palette.editor = (function() {
} }
function update(entry,version,url,container,done) { function update(entry,version,url,container,done) {
if (RED.settings.theme('palette.editable') === false) { if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
done(new Error('Palette not editable')); done(new Error('Palette not editable'));
return; return;
} }
@ -1021,7 +1099,7 @@ RED.palette.editor = (function() {
}) })
} }
function remove(entry,container,done) { function remove(entry,container,done) {
if (RED.settings.theme('palette.editable') === false) { if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
done(new Error('Palette not editable')); done(new Error('Palette not editable'));
return; return;
} }
@ -1078,7 +1156,7 @@ RED.palette.editor = (function() {
}) })
} }
function install(entry,container,done) { function install(entry,container,done) {
if (RED.settings.theme('palette.editable') === false) { if (RED.settings.get('externalModules.palette.allowInstall', true) === false) {
done(new Error('Palette not editable')); done(new Error('Palette not editable'));
return; return;
} }

View File

@ -97,13 +97,18 @@ RED.palette = (function() {
label = RED.utils.sanitize(label); label = RED.utils.sanitize(label);
var words = label.split(/[ -]/); var words = label.split(/([ -]|\\n )/);
var displayLines = []; var displayLines = [];
var currentLine = ""; var currentLine = "";
for (var i=0;i<words.length;i++) { for (var i=0;i<words.length;i++) {
var word = words[i]; var word = words[i];
if (word === "\\n ") {
displayLines.push(currentLine);
currentLine = "";
continue;
}
var sep = (i == 0) ? "" : " "; var sep = (i == 0) ? "" : " ";
var newWidth = RED.view.calculateTextWidth(currentLine+sep+word, "red-ui-palette-label"); var newWidth = RED.view.calculateTextWidth(currentLine+sep+word, "red-ui-palette-label");
if (newWidth < nodeWidth) { if (newWidth < nodeWidth) {
@ -147,7 +152,7 @@ RED.palette = (function() {
var popOverContent; var popOverContent;
try { try {
var l = "<p><b>"+RED.text.bidi.enforceTextDirectionWithUCC(label)+"</b></p>"; var l = "<p><b>"+RED.text.bidi.enforceTextDirectionWithUCC(label)+"</b></p>";
popOverContent = $('<div></div>').append($(l+(info?info:$("script[data-help-name='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim()) popOverContent = $('<div></div>').append($(l+(info?info:RED.nodes.getNodeHelp(type)||"<p>"+RED._("palette.noInfo")+"</p>").trim())
.filter(function(n) { .filter(function(n) {
return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0) return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0)
}).slice(0,2)); }).slice(0,2));
@ -165,7 +170,16 @@ RED.palette = (function() {
metaData = typeInfo.set.module+" : "; metaData = typeInfo.set.module+" : ";
} }
metaData += type; metaData += type;
$('<button type="button" onclick="RED.sidebar.help.show(\''+type+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
if (/^subflow:/.test(type)) {
$('<button type="button" onclick="RED.workspaces.show(\''+type.substring(8).replace(/'/g,"\\'")+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-pencil"></i></button>').appendTo(popOverContent)
}
var safeType = type.replace(/'/g,"\\'");
$('<button type="button" onclick="RED.search.show(\'type:'+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>').appendTo(popOverContent)
$('<button type="button" onclick="RED.sidebar.help.show(\''+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent); $('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
} }
} catch(err) { } catch(err) {
@ -264,27 +278,6 @@ RED.palette = (function() {
d.data('popover',popover); d.data('popover',popover);
// $(d).popover({
// title:d.type,
// placement:"right",
// trigger: "hover",
// delay: { show: 750, hide: 50 },
// html: true,
// container:'body'
// });
// d.on("click", function() {
// RED.view.focus();
// var helpText;
// if (nt.indexOf("subflow:") === 0) {
// helpText = RED.utils.renderMarkdown(RED.nodes.subflow(nt.substring(8)).info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// } else {
// helpText = $("script[data-help-name='"+d.attr("data-palette-type")+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// }
// // Don't look too closely. RED.sidebar.info.set will set the 'Description'
// // section of the sidebar. Pass in the title of the Help section so it looks
// // right.
// RED.sidebar.type.show(helpText,RED._("sidebar.info.nodeHelp"));
// });
var chart = $("#red-ui-workspace-chart"); var chart = $("#red-ui-workspace-chart");
var chartSVG = $("#red-ui-workspace-chart>svg").get(0); var chartSVG = $("#red-ui-workspace-chart>svg").get(0);
var activeSpliceLink; var activeSpliceLink;
@ -417,7 +410,8 @@ RED.palette = (function() {
RED.workspaces.show(nt.substring(8)); RED.workspaces.show(nt.substring(8));
e.preventDefault(); e.preventDefault();
}); });
nodeInfo = RED.utils.renderMarkdown(def.info||""); var subflow = RED.nodes.subflow(nt.substring(8));
nodeInfo = RED.utils.renderMarkdown(subflow.info||"");
} }
setLabel(nt,d,label,nodeInfo); setLabel(nt,d,label,nodeInfo);

View File

@ -465,7 +465,7 @@ RED.projects.settings = (function() {
metaRow = $('<div class="red-ui-palette-module-meta"></div>').appendTo(headerRow); metaRow = $('<div class="red-ui-palette-module-meta"></div>').appendTo(headerRow);
var buttons = $('<div class="red-ui-palette-module-button-group"></div>').appendTo(metaRow); var buttons = $('<div class="red-ui-palette-module-button-group"></div>').appendTo(metaRow);
if (RED.user.hasPermission("projects.write")) { if (RED.user.hasPermission("projects.write")) {
if (!entry.installed && RED.settings.theme('palette.editable') !== false) { if (!entry.installed && RED.settings.get('externalModules.palette.allowInstall', true) !== false) {
$('<a href="#" class="red-ui-button red-ui-button-small">' + RED._("sidebar.project.projectSettings.install") + '</a>').appendTo(buttons) $('<a href="#" class="red-ui-button red-ui-button-small">' + RED._("sidebar.project.projectSettings.install") + '</a>').appendTo(buttons)
.on("click", function(evt) { .on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();

View File

@ -43,9 +43,11 @@ RED.projects.userSettings = (function() {
function createWorkflowSection(pane) { function createWorkflowSection(pane) {
var defaultWorkflowMode = RED.settings.theme("projects.workflow.mode","manual");
var currentGitSettings = RED.settings.get('git') || {}; var currentGitSettings = RED.settings.get('git') || {};
currentGitSettings.workflow = currentGitSettings.workflow || {}; currentGitSettings.workflow = currentGitSettings.workflow || {};
currentGitSettings.workflow.mode = currentGitSettings.workflow.mode || "manual"; currentGitSettings.workflow.mode = currentGitSettings.workflow.mode || defaultWorkflowMode;
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.workflow")).appendTo(pane); var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.workflow")).appendTo(pane);

View File

@ -294,7 +294,10 @@ RED.sidebar.versionControl = (function() {
// TODO: this is a full refresh of the files - should be able to // TODO: this is a full refresh of the files - should be able to
// just do an incremental refresh // just do an incremental refresh
var workflowMode = ((RED.settings.get('git') || {}).workflow || {}).mode || "manual"; // Get the default workflow mode from theme settings
var defaultWorkflowMode = RED.settings.theme("projects.workflow.mode","manual");
// Check for the user-defined choice of mode
var workflowMode = ((RED.settings.get('git') || {}).workflow || {}).mode || defaultWorkflowMode;
if (workflowMode === 'auto') { if (workflowMode === 'auto') {
refresh(true); refresh(true);
} else { } else {

View File

@ -47,6 +47,37 @@ RED.subflow = (function() {
'</div>'+ '</div>'+
'</script>'; '</script>';
var _subflowModulePaneTemplate = '<form class="dialog-form form-horizontal" autocomplete="off">'+
'<div class="form-row">'+
'<label for="subflow-input-module-module" data-i18n="[append]editor:subflow.module"><i class="fa fa-cube"></i> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-module" data-i18n="[placeholder]common.label.name">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-type" data-i18n="[append]editor:subflow.type"> </label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-type">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-version" data-i18n="[append]editor:subflow.version"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-version" data-i18n="[placeholder]editor:subflow.versionPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-desc" data-i18n="[append]editor:subflow.desc"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-desc">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-license" data-i18n="[append]editor:subflow.license"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-license">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-author" data-i18n="[append]editor:subflow.author"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-author" data-i18n="[placeholder]editor:subflow.authorPlaceholder">'+
'</div>'+
'<div class="form-row">'+
'<label for="subflow-input-module-keywords" data-i18n="[append]editor:subflow.keys"></label>'+
'<input style="width: calc(100% - 110px)" type="text" id="subflow-input-module-keywords" data-i18n="[placeholder]editor:subflow.keysPlaceholder">'+
'</div>'+
'</form>';
function findAvailableSubflowIOPosition(subflow,isInput) { function findAvailableSubflowIOPosition(subflow,isInput) {
var pos = {x:50,y:30}; var pos = {x:50,y:30};
if (!isInput) { if (!isInput) {
@ -433,12 +464,43 @@ RED.subflow = (function() {
$("#red-ui-subflow-delete").on("click", function(event) { $("#red-ui-subflow-delete").on("click", function(event) {
event.preventDefault(); event.preventDefault();
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (subflow.instances.length > 0) {
var msg = $('<div>')
$('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
$('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
var confirmDeleteNotification = RED.notify(msg, {
modal: true,
fixed: true,
buttons: [
{
text: RED._('common.label.cancel'),
click: function() {
confirmDeleteNotification.close();
}
},
{
text: RED._('workspace.confirmDelete'),
class: "primary",
click: function() {
confirmDeleteNotification.close();
completeDelete();
}
}
]
});
return;
} else {
completeDelete();
}
function completeDelete() {
var startDirty = RED.nodes.dirty(); var startDirty = RED.nodes.dirty();
var historyEvent = removeSubflow(RED.workspaces.active()); var historyEvent = removeSubflow(RED.workspaces.active());
historyEvent.t = 'delete'; historyEvent.t = 'delete';
historyEvent.dirty = startDirty; historyEvent.dirty = startDirty;
RED.history.push(historyEvent); RED.history.push(historyEvent);
}
}); });
@ -993,6 +1055,7 @@ RED.subflow = (function() {
icon: "", icon: "",
type: "cred" type: "cred"
} }
opt.ui.type = "cred";
} else { } else {
opt.ui = opt.ui || { opt.ui = opt.ui || {
icon: "", icon: "",
@ -1488,6 +1551,7 @@ RED.subflow = (function() {
var locale = RED.i18n.lang(); var locale = RED.i18n.lang();
var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, locale); var labelText = lookupLabel(labels, labels["en-US"]||tenv.name, locale);
var label = $('<label>').appendTo(row); var label = $('<label>').appendTo(row);
$('<span>&nbsp;</span>').appendTo(row);
var labelContainer = $('<span></span>').appendTo(label); var labelContainer = $('<span></span>').appendTo(label);
if (ui.icon) { if (ui.icon) {
var newPath = RED.utils.separateIconPath(ui.icon); var newPath = RED.utils.separateIconPath(ui.icon);
@ -1723,8 +1787,6 @@ RED.subflow = (function() {
parentEnv[env.name] = item; parentEnv[env.name] = item;
}) })
} }
}
if (node.env) { if (node.env) {
for (var i = 0; i < node.env.length; i++) { for (var i = 0; i < node.env.length; i++) {
var env = node.env[i]; var env = node.env[i];
@ -1740,6 +1802,40 @@ RED.subflow = (function() {
} }
} }
} }
} else if (node._def.subflowModule) {
var keys = Object.keys(node._def.defaults);
keys.forEach(function(name) {
if (name !== 'name') {
var prop = node._def.defaults[name];
var nodeProp = node[name];
var nodePropType;
var nodePropValue = nodeProp;
if (prop.ui && prop.ui.type === "cred") {
nodePropType = "cred";
} else {
switch(typeof nodeProp) {
case "string": nodePropType = "str"; break;
case "number": nodePropType = "num"; break;
case "boolean": nodePropType = "bool"; nodePropValue = nodeProp?"true":"false"; break;
default:
nodePropType = nodeProp.type;
nodePropValue = nodeProp.value;
}
}
var item = {
name: name,
type: nodePropType,
value: nodePropValue,
parent: {
type: prop.type,
value: prop.value
},
ui: $.extend(true,{},prop.ui)
}
envList.push(item);
}
})
}
return envList; return envList;
} }
@ -1859,6 +1955,126 @@ RED.subflow = (function() {
buildPropertiesList(list, node); buildPropertiesList(list, node);
} }
function setupInputValidation(input,validator) {
var errorTip;
var validateTimeout;
var validateFunction = function() {
if (validateTimeout) {
return;
}
validateTimeout = setTimeout(function() {
var error = validator(input.val());
// if (!error && errorTip) {
// errorTip.close();
// errorTip = null;
// } else if (error && !errorTip) {
// errorTip = RED.popover.create({
// tooltip: true,
// target:input,
// size: "small",
// direction: "bottom",
// content: error,
// }).open();
// }
input.toggleClass("input-error",!!error);
validateTimeout = null;
})
}
input.on("change keyup paste", validateFunction);
}
function buildModuleForm(container, node) {
$(_subflowModulePaneTemplate).appendTo(container);
var moduleProps = node.meta || {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords',
'license'
].forEach(function(property) {
$("#subflow-input-module-"+property).val(moduleProps[property]||"")
})
$("#subflow-input-module-type").attr("placeholder",node.id);
setupInputValidation($("#subflow-input-module-module"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue.length < 215;
isValid = isValid && !/^[._]/.test(newValue);
isValid = isValid && !/[A-Z]/.test(newValue);
if (newValue !== encodeURIComponent(newValue)) {
var m = /^@([^\/]+)\/([^\/]+)$/.exec(newValue);
if (m) {
isValid = isValid && (m[1] === encodeURIComponent(m[1]) && m[2] === encodeURIComponent(m[2]))
} else {
isValid = false;
}
}
return isValid?"":"Invalid module name"
})
setupInputValidation($("#subflow-input-module-version"), function(newValue) {
newValue = newValue.trim();
var isValid = newValue === "" ||
/^(\d|[1-9]\d*)\.(\d|[1-9]\d*)\.(\d|[1-9]\d*)(-(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9A-Za-z-][0-9A-Za-z-]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(newValue);
return isValid?"":"Invalid version number"
})
var licenses = ["none", "Apache-2.0", "BSD-3-Clause", "BSD-2-Clause", "GPL-2.0", "GPL-3.0", "MIT", "MPL-2.0", "CDDL-1.0", "EPL-2.0"];
var typedLicenses = {
types: licenses.map(function(l) {
return {
value: l,
label: l === "none" ? RED._("editor:subflow.licenseNone") : l,
hasValue: false
};
})
}
typedLicenses.types.push({
value:"_custom_", label:RED._("editor:subflow.licenseOther"), icon:"red/images/typedInput/az.svg"
})
if (!moduleProps.license) {
typedLicenses.default = "none";
} else if (licenses.indexOf(moduleProps.license) > -1) {
typedLicenses.default = moduleProps.license;
} else {
typedLicenses.default = "_custom_";
}
$("#subflow-input-module-license").typedInput(typedLicenses)
}
function exportSubflowModuleProperties(node) {
var value;
var moduleProps = {};
[
'module',
'type',
'version',
'author',
'desc',
'keywords'
].forEach(function(property) {
value = $("#subflow-input-module-"+property).val().trim();
if (value) {
moduleProps[property] = value;
}
})
var selectedLicenseType = $("#subflow-input-module-license").typedInput("type");
if (selectedLicenseType === '_custom_') {
value = $("#subflow-input-module-license").val();
if (value) {
moduleProps.license = value;
}
} else if (selectedLicenseType !== "none") {
moduleProps.license = selectedLicenseType;
}
return moduleProps;
}
return { return {
init: init, init: init,
createSubflow: createSubflow, createSubflow: createSubflow,
@ -1872,9 +2088,11 @@ RED.subflow = (function() {
buildEditForm: buildEditForm, buildEditForm: buildEditForm,
buildPropertiesForm: buildPropertiesForm, buildPropertiesForm: buildPropertiesForm,
buildModuleForm: buildModuleForm,
exportSubflowTemplateEnv: exportEnvList, exportSubflowTemplateEnv: exportEnvList,
exportSubflowInstanceEnv: exportSubflowInstanceEnv exportSubflowInstanceEnv: exportSubflowInstanceEnv,
exportSubflowModuleProperties: exportSubflowModuleProperties
} }
})(); })();

View File

@ -247,7 +247,7 @@ RED.sidebar.help = (function() {
helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>')); helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
title = subflowNode.name || nodeType; title = subflowNode.name || nodeType;
} else { } else {
helpText = $("script[data-help-name='"+nodeType+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'); helpText = RED.nodes.getNodeHelp(nodeType)||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
title = nodeType; title = nodeType;
} }
setInfoText(title, helpText, helpSection); setInfoText(title, helpText, helpSection);

View File

@ -119,34 +119,17 @@ RED.sidebar.info.outliner = (function() {
return div; return div;
} }
function getSubflowLabel(n) {
var div = $('<div>',{class:"red-ui-info-outline-item"});
RED.utils.createNodeIcon(n).appendTo(div);
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
var labelText = getNodeLabelText(n);
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
if (labelText) {
label.text(labelText)
} else {
label.html("&nbsp;")
}
addControls(n, div);
return div;
// var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
// var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
// contentDiv.text(n.name || n.id);
// addControls(n, div);
// return div;
}
function addControls(n,div) { function addControls(n,div) {
var controls = $('<div>',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div); var controls = $('<div>',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div);
if (n.type === "subflow") {
var subflowInstanceBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.instances.length).appendTo(controls).on("click",function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.search.show("type:subflow:"+n.id);
})
// RED.popover.tooltip(userCountBadge,function() { return RED._('editor.nodesUse',{count:n.users.length})});
}
if (n._def.category === "config" && n.type !== "group") { if (n._def.category === "config" && n.type !== "group") {
var userCountBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.users.length).appendTo(controls).on("click",function(evt) { var userCountBadge = $('<button type="button" class="red-ui-info-outline-item-control-users red-ui-button red-ui-button-small"><i class="fa fa-toggle-right"></i></button>').text(n.users.length).appendTo(controls).on("click",function(evt) {
evt.preventDefault(); evt.preventDefault();
@ -169,7 +152,7 @@ RED.sidebar.info.outliner = (function() {
// evt.stopPropagation(); // evt.stopPropagation();
// RED.view.reveal(n.id); // RED.view.reveal(n.id);
// }) // })
if (n.type !== 'group' && n.type !== 'subflow') { if (n.type !== 'subflow') {
var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) { var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
@ -179,6 +162,46 @@ RED.sidebar.info.outliner = (function() {
} else { } else {
RED.workspaces.disable(n.id) RED.workspaces.disable(n.id)
} }
} else if (n.type === 'group') {
var groupNodes = RED.group.getNodes(n,true);
var groupHistoryEvent = {
t:'multi',
events:[],
dirty: RED.nodes.dirty()
}
var targetState;
groupNodes.forEach(function(n) {
if (n.type !== 'group') {
if (targetState === undefined) {
targetState = !n.d;
}
var state = !!n.d;
if (state !== targetState) {
var historyEvent = {
t: "edit",
node: n,
changed: n.changed,
changes: {
d: n.d
}
}
if (n.d) {
delete n.d;
} else {
n.d = true;
}
n.dirty = true;
n.changed = true;
RED.events.emit("nodes:change",n);
groupHistoryEvent.events.push(historyEvent);
}
}
if (groupHistoryEvent.events.length > 0) {
RED.history.push(groupHistoryEvent);
RED.nodes.dirty(true)
RED.view.redraw();
}
})
} else { } else {
// TODO: this ought to be a utility function in RED.nodes // TODO: this ought to be a utility function in RED.nodes
var historyEvent = { var historyEvent = {
@ -198,11 +221,15 @@ RED.sidebar.info.outliner = (function() {
n.dirty = true; n.dirty = true;
n.changed = true; n.changed = true;
RED.events.emit("nodes:change",n); RED.events.emit("nodes:change",n);
RED.history.push(historyEvent);
RED.nodes.dirty(true) RED.nodes.dirty(true)
RED.view.redraw(); RED.view.redraw();
} }
}); });
RED.popover.tooltip(toggleButton,function() { RED.popover.tooltip(toggleButton,function() {
if (n.type === "group") {
return RED._("common.label.enable")+" / "+RED._("common.label.disable")
}
return RED._("common.label."+(((n.type==='tab' && n.disabled) || (n.type!=='tab' && n.d))?"enable":"disable")); return RED._("common.label."+(((n.type==='tab' && n.disabled) || (n.type!=='tab' && n.d))?"enable":"disable"));
}); });
} else { } else {
@ -486,6 +513,13 @@ RED.sidebar.info.outliner = (function() {
existingObject.treeList.remove(); existingObject.treeList.remove();
delete objects[n.id] delete objects[n.id]
if (/^subflow:/.test(n.type)) {
var sfType = n.type.substring(8);
if (objects[sfType]) {
objects[sfType].element.find(".red-ui-info-outline-item-control-users").text(RED.nodes.subflow(sfType).instances.length);
}
}
// If this is a group being removed, it may have an empty item // If this is a group being removed, it may have an empty item
if (empties[n.id]) { if (empties[n.id]) {
delete empties[n.id]; delete empties[n.id];
@ -587,6 +621,12 @@ RED.sidebar.info.outliner = (function() {
configNodeTypes[parent].types[n.type].treeList.addChild(objects[n.id]); configNodeTypes[parent].types[n.type].treeList.addChild(objects[n.id]);
} }
objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d)
if (/^subflow:/.test(n.type)) {
var sfType = n.type.substring(8);
if (objects[sfType]) {
objects[sfType].element.find(".red-ui-info-outline-item-control-users").text(RED.nodes.subflow(sfType).instances.length);
}
}
updateSearch(); updateSearch();
} }

View File

@ -338,7 +338,7 @@ RED.sidebar.info = (function() {
count++; count++;
propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td></td><td></td></tr>').appendTo(tableBody); propRow = $('<tr class="red-ui-help-info-property-row'+(expandedSections.property?"":" hide")+'"><td></td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[0]).text(n); $(propRow.children()[0]).text(n);
if (defaults[n].type) { if (defaults[n].type && !defaults[n]._type.array) {
var configNode = RED.nodes.node(val); var configNode = RED.nodes.node(val);
if (!configNode) { if (!configNode) {
RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]); RED.utils.createObjectElement(undefined).appendTo(propRow.children()[1]);
@ -382,20 +382,13 @@ RED.sidebar.info = (function() {
var category = subflowNode.category||"subflows"; var category = subflowNode.category||"subflows";
$(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category})) $(propRow.children()[1]).text(RED._("palette.label."+category,{defaultValue:category}))
$('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody); $('<tr class="node-info-subflow-row"><td>'+RED._("sidebar.info.instances")+"</td><td>"+subflowUserCount+'</td></tr>').appendTo(tableBody);
if (subflowNode.meta) {
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("subflow.module")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).text(subflowNode.meta.module||"")
propRow = $('<tr class="red-ui-help-info-row"><td>'+RED._("subflow.version")+'</td><td></td></tr>').appendTo(tableBody);
$(propRow.children()[1]).text(subflowNode.meta.version||"")
}
} }
// var helpText = "";
// if (node.type === "tab" || node.type === "subflow") {
// } else {
// if (subflowNode && node.type !== "subflow") {
// // Selected a subflow instance node.
// // - The subflow template info goes into help
// helpText = (RED.utils.renderMarkdown(subflowNode.info||"")||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>'));
// } else {
// helpText = $("script[data-help-name='"+node.type+"']").html()||('<span class="red-ui-help-info-none">'+RED._("sidebar.info.none")+'</span>');
// }
// setInfoText(helpText, helpSection.content);
// }
var infoText = ""; var infoText = "";
@ -409,23 +402,6 @@ RED.sidebar.info = (function() {
} }
var infoSectionContainer = $("<div>").css("padding","0 6px 6px").appendTo(propertiesPanelContent) var infoSectionContainer = $("<div>").css("padding","0 6px 6px").appendTo(propertiesPanelContent)
// var editInfo = $('<button class="red-ui-button red-ui-button-small" style="float: right"><i class="fa fa-file-text-o"></button>').appendTo(infoSectionContainer).on("click", function(evt) {
// //.text(RED._("sidebar.info.editDescription"))
// evt.preventDefault();
// evt.stopPropagation();
// if (node.type === 'tab') {
//
// } else if (node.type === 'subflow') {
//
// } else if (node.type === 'group') {
//
// } else if (node._def.category !== 'config') {
// RED.editor.edit(node,"editor-tab-description");
// } else {
//
// }
// })
setInfoText(infoText, infoSectionContainer); setInfoText(infoText, infoSectionContainer);
$(".red-ui-sidebar-info-stack").scrollTop(0); $(".red-ui-sidebar-info-stack").scrollTop(0);

View File

@ -115,7 +115,13 @@ RED.userSettings = (function() {
options: [ options: [
{setting:"editor-language",local: true, label:"menu.label.view.language",options:function(done){ done([{val:'',text:RED._('menu.label.view.browserDefault')}].concat(RED.settings.theme("languages").map(localeToName).sort(compText))) }}, {setting:"editor-language",local: true, label:"menu.label.view.language",options:function(done){ done([{val:'',text:RED._('menu.label.view.browserDefault')}].concat(RED.settings.theme("languages").map(localeToName).sort(compText))) }},
] ]
},{ },
// {
// options: [
// {setting:"theme", label:"Theme",options:function(done){ done([{val:'',text:'default'}].concat(RED.settings.theme("themes"))) }},
// ]
// },
{
title: "menu.label.view.grid", title: "menu.label.view.grid",
options: [ options: [
{setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid", default: true, toggle:true,onchange:"core:toggle-show-grid"}, {setting:"view-show-grid",oldSetting:"menu-menu-item-view-show-grid",label:"menu.label.view.showGrid", default: true, toggle:true,onchange:"core:toggle-show-grid"},

View File

@ -615,18 +615,25 @@ RED.utils = (function() {
return element; return element;
} }
function normalisePropertyExpression(str) { function createError(code, message) {
var e = new Error(message);
e.code = code;
return e;
}
function normalisePropertyExpression(str,msg) {
// This must be kept in sync with validatePropertyExpression // This must be kept in sync with validatePropertyExpression
// in editor/js/ui/utils.js // in editor/js/ui/utils.js
var length = str.length; var length = str.length;
if (length === 0) { if (length === 0) {
throw new Error("Invalid property expression: zero-length"); throw createError("INVALID_EXPR","Invalid property expression: zero-length");
} }
var parts = []; var parts = [];
var start = 0; var start = 0;
var inString = false; var inString = false;
var inBox = false; var inBox = false;
var boxExpression = false;
var quoteChar; var quoteChar;
var v; var v;
for (var i=0;i<length;i++) { for (var i=0;i<length;i++) {
@ -634,14 +641,14 @@ RED.utils = (function() {
if (!inString) { if (!inString) {
if (c === "'" || c === '"') { if (c === "'" || c === '"') {
if (i != start) { if (i != start) {
throw new Error("Invalid property expression: unexpected "+c+" at position "+i); throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
} }
inString = true; inString = true;
quoteChar = c; quoteChar = c;
start = i+1; start = i+1;
} else if (c === '.') { } else if (c === '.') {
if (i===0) { if (i===0) {
throw new Error("Invalid property expression: unexpected . at position 0"); throw createError("INVALID_EXPR","Invalid property expression: unexpected . at position 0");
} }
if (start != i) { if (start != i) {
v = str.substring(start,i); v = str.substring(start,i);
@ -652,57 +659,99 @@ RED.utils = (function() {
} }
} }
if (i===length-1) { if (i===length-1) {
throw new Error("Invalid property expression: unterminated expression"); throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
} }
// Next char is first char of an identifier: a-z 0-9 $ _ // Next char is first char of an identifier: a-z 0-9 $ _
if (!/[a-z0-9\$\_]/i.test(str[i+1])) { if (!/[a-z0-9\$\_]/i.test(str[i+1])) {
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1)); throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
} }
start = i+1; start = i+1;
} else if (c === '[') { } else if (c === '[') {
if (i === 0) { if (i === 0) {
throw new Error("Invalid property expression: unexpected "+c+" at position "+i); throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
} }
if (start != i) { if (start != i) {
parts.push(str.substring(start,i)); parts.push(str.substring(start,i));
} }
if (i===length-1) { if (i===length-1) {
throw new Error("Invalid property expression: unterminated expression"); throw createError("INVALID_EXPR","Invalid property expression: unterminated expression");
} }
// Start of a new expression. If it starts with msg it is a nested expression
// Need to scan ahead to find the closing bracket
if (/^msg[.\[]/.test(str.substring(i+1))) {
var depth = 1;
var inLocalString = false;
var localStringQuote;
for (var j=i+1;j<length;j++) {
if (/["']/.test(str[j])) {
if (inLocalString) {
if (str[j] === localStringQuote) {
inLocalString = false
}
} else {
inLocalString = true;
localStringQuote = str[j]
}
}
if (str[j] === '[') {
depth++;
} else if (str[j] === ']') {
depth--;
}
if (depth === 0) {
try {
if (msg) {
parts.push(getMessageProperty(msg, str.substring(i+1,j)))
} else {
parts.push(normalisePropertyExpression(str.substring(i+1,j), msg));
}
inBox = false;
i = j;
start = j+1;
break;
} catch(err) {
throw createError("INVALID_EXPR","Invalid expression started at position "+(i+1))
}
}
}
if (depth > 0) {
throw createError("INVALID_EXPR","Invalid property expression: unmatched '[' at position "+i);
}
continue;
} else if (!/["'\d]/.test(str[i+1])) {
// Next char is either a quote or a number // Next char is either a quote or a number
if (!/["'\d]/.test(str[i+1])) { throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
throw new Error("Invalid property expression: unexpected "+str[i+1]+" at position "+(i+1));
} }
start = i+1; start = i+1;
inBox = true; inBox = true;
} else if (c === ']') { } else if (c === ']') {
if (!inBox) { if (!inBox) {
throw new Error("Invalid property expression: unexpected "+c+" at position "+i); throw createError("INVALID_EXPR","Invalid property expression: unexpected "+c+" at position "+i);
} }
if (start != i) { if (start != i) {
v = str.substring(start,i); v = str.substring(start,i);
if (/^\d+$/.test(v)) { if (/^\d+$/.test(v)) {
parts.push(parseInt(v)); parts.push(parseInt(v));
} else { } else {
throw new Error("Invalid property expression: unexpected array expression at position "+start); throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
} }
} }
start = i+1; start = i+1;
inBox = false; inBox = false;
} else if (c === ' ') { } else if (c === ' ') {
throw new Error("Invalid property expression: unexpected ' ' at position "+i); throw createError("INVALID_EXPR","Invalid property expression: unexpected ' ' at position "+i);
} }
} else { } else {
if (c === quoteChar) { if (c === quoteChar) {
if (i-start === 0) { if (i-start === 0) {
throw new Error("Invalid property expression: zero-length string at position "+start); throw createError("INVALID_EXPR","Invalid property expression: zero-length string at position "+start);
} }
parts.push(str.substring(start,i)); parts.push(str.substring(start,i));
// If inBox, next char must be a ]. Otherwise it may be [ or . // If inBox, next char must be a ]. Otherwise it may be [ or .
if (inBox && !/\]/.test(str[i+1])) { if (inBox && !/\]/.test(str[i+1])) {
throw new Error("Invalid property expression: unexpected array expression at position "+start); throw createError("INVALID_EXPR","Invalid property expression: unexpected array expression at position "+start);
} else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) { } else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) {
throw new Error("Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1)); throw createError("INVALID_EXPR","Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1));
} }
start = i+1; start = i+1;
inString = false; inString = false;
@ -711,7 +760,7 @@ RED.utils = (function() {
} }
if (inBox || inString) { if (inBox || inString) {
throw new Error("Invalid property expression: unterminated expression"); throw new createError("INVALID_EXPR","Invalid property expression: unterminated expression");
} }
if (start < length) { if (start < length) {
parts.push(str.substring(start)); parts.push(str.substring(start));

View File

@ -2276,7 +2276,7 @@ RED.view = (function() {
} }
function calculateTextWidth(str, className) { function calculateTextWidth(str, className) {
var result=convertLineBreakCharacter(str); var result = convertLineBreakCharacter(str);
var width = 0; var width = 0;
for (var i=0;i<result.length;i++) { for (var i=0;i<result.length;i++) {
var calculateTextW=calculateTextDimensions(result[i],className)[0]; var calculateTextW=calculateTextDimensions(result[i],className)[0];

View File

@ -219,7 +219,7 @@ RED.user = (function() {
function init() { function init() {
if (RED.settings.user) { if (RED.settings.user) {
if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu")) { if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu") || RED.settings.editorTheme.userMenu) {
var userMenu = $('<li><a id="red-ui-header-button-user" class="button hide" href="#"></a></li>') var userMenu = $('<li><a id="red-ui-header-button-user" class="button hide" href="#"></a></li>')
.prependTo(".red-ui-header-toolbar"); .prependTo(".red-ui-header-toolbar");

View File

@ -15,6 +15,9 @@
**/ **/
body {
overflow: hidden;
}
.red-ui-editor { .red-ui-editor {
font-size: $primary-font-size; font-size: $primary-font-size;

View File

@ -765,6 +765,10 @@ button.red-ui-toggleButton.toggle {
width: calc(100% - 10px); width: calc(100% - 10px);
padding-left: 3px; padding-left: 3px;
} }
select {
padding: 0 3px;
font-size: 11px;
}
.placeholder-input { .placeholder-input {
span:first-child { span:first-child {
display:inline-block; display:inline-block;

View File

@ -139,6 +139,9 @@
stroke-width: 2; stroke-width: 2;
} }
.red-ui-flow-node-icon-group { .red-ui-flow-node-icon-group {
text {
@include disable-selection;
}
.fa-lg { .fa-lg {
@include disable-selection; @include disable-selection;
stroke: none; stroke: none;

View File

@ -29,8 +29,30 @@
} }
} }
} }
.red-ui-clipboard-dialog-tab-clipboard {
#red-ui-clipboard-dialog-export-tab-clipboard-preview {
.red-ui-treeList-container,.red-ui-editableList-border {
border: none;
border-radius: 0;
}
}
#red-ui-clipboard-dialog-export-tab-clipboard-json {
padding: 10px 10px 0;
}
#red-ui-clipboard-dialog-import-tab-clipboard {
padding: 10px; padding: 10px;
}
.red-ui-clipboard-dialog-export-tab-clipboard-tab {
position: absolute;
top: 40px;
right: 0;
left: 0;
bottom: 0;
}
.red-ui-clipboard-dialog-tab-clipboard {
textarea { textarea {
resize: none; resize: none;
width: 100%; width: 100%;

View File

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

View File

@ -326,7 +326,7 @@ div.red-ui-info-table {
border-bottom: 1px solid $secondary-border-color; border-bottom: 1px solid $secondary-border-color;
} }
} }
.red-ui-info-outline,.red-ui-sidebar-help-toc, #red-ui-clipboard-dialog-import-conflicts-list { .red-ui-info-outline,.red-ui-sidebar-help-toc, #red-ui-clipboard-dialog-import-conflicts-list, #red-ui-clipboard-dialog-export-tab-clipboard-preview {
.red-ui-info-outline-item { .red-ui-info-outline-item {
display: inline-block; display: inline-block;
padding: 0; padding: 0;

View File

@ -26,6 +26,14 @@
box-sizing: border-box; box-sizing: border-box;
overflow:visible; overflow:visible;
position: relative; position: relative;
&[disabled] {
input, button {
background: $secondary-background-inactive;
pointer-events: none;
cursor: not-allowed;
}
}
.red-ui-typedInput-input-wrap { .red-ui-typedInput-input-wrap {
flex-grow: 1; flex-grow: 1;
} }

View File

@ -18,7 +18,7 @@
color:"#c0edc0", color:"#c0edc0",
defaults: { defaults: {
name: {value:""}, name: {value:""},
scope: {value:[]}, scope: {value:[], type:"*[]"},
uncaught: {value:false} uncaught: {value:false}
}, },
inputs:0, inputs:0,

View File

@ -30,7 +30,7 @@
color:"#e49191", color:"#e49191",
defaults: { defaults: {
name: {value:""}, name: {value:""},
scope: {value:null}, scope: {value:null, type:"*[]"},
uncaught: {value:false} uncaught: {value:false}
}, },
inputs:0, inputs:0,

View File

@ -26,7 +26,7 @@
color:"#94c1d0", color:"#94c1d0",
defaults: { defaults: {
name: {value:""}, name: {value:""},
scope: {value:null} scope: {value:null, type:"*[]"}
}, },
inputs:0, inputs:0,
outputs:1, outputs:1,

View File

@ -187,7 +187,7 @@
color:"#ddd",//"#87D8CF", color:"#ddd",//"#87D8CF",
defaults: { defaults: {
name: {value:""}, name: {value:""},
links: { value: [] } links: { value: [], type:"link out[]" }
}, },
inputs:0, inputs:0,
outputs:1, outputs:1,
@ -216,7 +216,7 @@
color:"#ddd",//"#87D8CF", color:"#ddd",//"#87D8CF",
defaults: { defaults: {
name: {value:""}, name: {value:""},
links: { value: []} links: { value: [], type:"link in[]"}
}, },
align:"right", align:"right",
inputs:1, inputs:1,

View File

@ -168,6 +168,10 @@ module.exports = function(RED) {
return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done); return getFromValueType(RED.util.getMessageProperty(msg,rule.from),done);
} else if (rule.fromt === 'flow' || rule.fromt === 'global') { } else if (rule.fromt === 'flow' || rule.fromt === 'global') {
var contextKey = RED.util.parseContextStore(rule.from); var contextKey = RED.util.parseContextStore(rule.from);
if (/\[msg\./.test(context.key)) {
// The key has a nest msg. reference to evaluate first
context.key = RED.util.normalisePropertyExpression(contextKey.key,msg,true);
}
node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => { node.context()[rule.fromt].get(contextKey.key, contextKey.store, (err,fromValue) => {
if (err) { if (err) {
done(err) done(err)
@ -243,6 +247,10 @@ module.exports = function(RED) {
return done(undefined,msg); return done(undefined,msg);
} else if (rule.pt === 'flow' || rule.pt === 'global') { } else if (rule.pt === 'flow' || rule.pt === 'global') {
var contextKey = RED.util.parseContextStore(property); var contextKey = RED.util.parseContextStore(property);
if (/\[msg/.test(contextKey.key)) {
// The key has a nest msg. reference to evaluate first
contextKey.key = RED.util.normalisePropertyExpression(contextKey.key, msg, true)
}
var target = node.context()[rule.pt]; var target = node.context()[rule.pt];
var callback = err => { var callback = err => {
if (err) { if (err) {

View File

@ -80,10 +80,10 @@ module.exports = function(RED) {
this.drop = n.drop; this.drop = n.drop;
var node = this; var node = this;
function ourTimeout(handler, delay) { function ourTimeout(handler, delay, clearHandler) {
var toutID = setTimeout(handler, delay); var toutID = setTimeout(handler, delay);
return { return {
clear: function() { clearTimeout(toutID); }, clear: function() { clearTimeout(toutID); clearHandler(); },
trigger: function() { clearTimeout(toutID); return handler(); } trigger: function() { clearTimeout(toutID); return handler(); }
}; };
} }
@ -113,14 +113,15 @@ module.exports = function(RED) {
} }
if (node.pauseType === "delay") { if (node.pauseType === "delay") {
node.on("input", function(msg) { node.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("flush")) { flushDelayList(); } if (msg.hasOwnProperty("flush")) { flushDelayList(); done(); }
else { else {
var id = ourTimeout(function() { var id = ourTimeout(function() {
node.idList.splice(node.idList.indexOf(id),1); node.idList.splice(node.idList.indexOf(id),1);
if (node.idList.length === 0) { node.status({}); } if (node.idList.length === 0) { node.status({}); }
node.send(msg); send(msg);
}, node.timeout); done();
}, node.timeout, () => done());
node.idList.push(id); node.idList.push(id);
if ((node.timeout > 1000) && (node.idList.length !== 0)) { if ((node.timeout > 1000) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:" "}); node.status({fill:"blue",shape:"dot",text:" "});
@ -131,7 +132,7 @@ module.exports = function(RED) {
node.on("close", function() { clearDelayList(); }); node.on("close", function() { clearDelayList(); });
} }
else if (node.pauseType === "delayv") { else if (node.pauseType === "delayv") {
node.on("input", function(msg) { node.on("input", function(msg, send, done) {
var delayvar = Number(node.timeout); var delayvar = Number(node.timeout);
if (msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) { if (msg.hasOwnProperty("delay") && !isNaN(parseFloat(msg.delay))) {
delayvar = parseFloat(msg.delay); delayvar = parseFloat(msg.delay);
@ -140,8 +141,9 @@ module.exports = function(RED) {
var id = ourTimeout(function() { var id = ourTimeout(function() {
node.idList.splice(node.idList.indexOf(id),1); node.idList.splice(node.idList.indexOf(id),1);
if (node.idList.length === 0) { node.status({}); } if (node.idList.length === 0) { node.status({}); }
node.send(msg); send(msg);
}, delayvar); done();
}, delayvar, () => done());
node.idList.push(id); node.idList.push(id);
if ((delayvar >= 0) && (node.idList.length !== 0)) { if ((delayvar >= 0) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:delayvar/1000+"s"}); node.status({fill:"blue",shape:"dot",text:delayvar/1000+"s"});
@ -152,7 +154,7 @@ module.exports = function(RED) {
node.on("close", function() { clearDelayList(); }); node.on("close", function() { clearDelayList(); });
} }
else if (node.pauseType === "rate") { else if (node.pauseType === "rate") {
node.on("input", function(msg) { node.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
if (node.intervalID !== -1 ) { if (node.intervalID !== -1 ) {
clearInterval(node.intervalID); clearInterval(node.intervalID);
@ -161,17 +163,18 @@ module.exports = function(RED) {
delete node.lastSent; delete node.lastSent;
node.buffer = []; node.buffer = [];
node.status({text:"reset"}); node.status({text:"reset"});
done();
return; return;
} }
if (!node.drop) { if (!node.drop) {
var m = RED.util.cloneMessage(msg); var m = RED.util.cloneMessage(msg);
delete m.flush; delete m.flush;
if (node.intervalID !== -1) { if (node.intervalID !== -1) {
node.buffer.push(m); node.buffer.push({msg: m, send: send, done: done});
node.reportDepth(); node.reportDepth();
} }
else { else {
node.send(m); send(m);
node.reportDepth(); node.reportDepth();
node.intervalID = setInterval(function() { node.intervalID = setInterval(function() {
if (node.buffer.length === 0) { if (node.buffer.length === 0) {
@ -179,16 +182,22 @@ module.exports = function(RED) {
node.intervalID = -1; node.intervalID = -1;
} }
if (node.buffer.length > 0) { if (node.buffer.length > 0) {
node.send(node.buffer.shift()); const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
} }
node.reportDepth(); node.reportDepth();
}, node.rate); }, node.rate);
done();
} }
if (msg.hasOwnProperty("flush")) { if (msg.hasOwnProperty("flush")) {
while (node.buffer.length > 0) { while (node.buffer.length > 0) {
node.send(node.buffer.shift()); const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
} }
node.status({}); node.status({});
done();
} }
} }
else { else {
@ -198,17 +207,19 @@ module.exports = function(RED) {
} }
if (!node.lastSent) { // ensuring that we always send the first message if (!node.lastSent) { // ensuring that we always send the first message
node.lastSent = process.hrtime(); node.lastSent = process.hrtime();
node.send(msg); send(msg);
} }
else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) { else if ( ( (timeSinceLast[0] * SECONDS_TO_NANOS) + timeSinceLast[1] ) > (node.rate * MILLIS_TO_NANOS) ) {
node.lastSent = process.hrtime(); node.lastSent = process.hrtime();
node.send(msg); send(msg);
} }
done();
} }
}); });
node.on("close", function() { node.on("close", function() {
clearInterval(node.intervalID); clearInterval(node.intervalID);
clearTimeout(node.busy); clearTimeout(node.busy);
node.buffer.forEach((msgInfo) => msgInfo.done());
node.buffer = []; node.buffer = [];
node.status({}); node.status({});
}); });
@ -217,57 +228,75 @@ module.exports = function(RED) {
node.intervalID = setInterval(function() { node.intervalID = setInterval(function() {
if (node.pauseType === "queue") { if (node.pauseType === "queue") {
if (node.buffer.length > 0) { if (node.buffer.length > 0) {
node.send(node.buffer.shift()); // send the first on the queue const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg); // send the first on the queue
msgInfo.done();
} }
} }
else { else {
while (node.buffer.length > 0) { // send the whole queue while (node.buffer.length > 0) { // send the whole queue
node.send(node.buffer.shift()); const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
} }
} }
node.reportDepth(); node.reportDepth();
},node.rate); },node.rate);
var hit; var hit;
node.on("input", function(msg) { node.on("input", function(msg, send, done) {
if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; } if (!msg.hasOwnProperty("topic")) { msg.topic = "_none_"; }
hit = false; hit = false;
for (var b in node.buffer) { // check if already in queue for (var b in node.buffer) { // check if already in queue
if (msg.topic === node.buffer[b].topic) { if (msg.topic === node.buffer[b].msg.topic) {
node.buffer[b] = msg; // if so - replace existing entry node.buffer[b].done();
node.buffer[b] = {msg, send, done}; // if so - replace existing entry
hit = true; hit = true;
break; break;
} }
} }
if (!hit) { if (!hit) {
node.buffer.push(msg); // if not add to end of queue node.buffer.push({msg, send, done}); // if not add to end of queue
node.reportDepth(); node.reportDepth();
} }
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
while (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.done();
}
node.buffer = []; node.buffer = [];
node.status({text:"reset"}); node.status({text:"reset"});
done();
} }
if (msg.hasOwnProperty("flush")) { if (msg.hasOwnProperty("flush")) {
while (node.buffer.length > 0) { while (node.buffer.length > 0) {
node.send(node.buffer.shift()); const msgInfo = node.buffer.shift();
msgInfo.send(msgInfo.msg);
msgInfo.done();
} }
node.status({}); node.status({});
done();
} }
}); });
node.on("close", function() { node.on("close", function() {
clearInterval(node.intervalID); clearInterval(node.intervalID);
while (node.buffer.length > 0) {
const msgInfo = node.buffer.shift();
msgInfo.done();
}
node.buffer = []; node.buffer = [];
node.status({}); node.status({});
}); });
} }
else if (node.pauseType === "random") { else if (node.pauseType === "random") {
node.on("input", function(msg) { node.on("input", function(msg, send, done) {
var wait = node.randomFirst + (node.diff * Math.random()); var wait = node.randomFirst + (node.diff * Math.random());
var id = ourTimeout(function() { var id = ourTimeout(function() {
node.idList.splice(node.idList.indexOf(id),1); node.idList.splice(node.idList.indexOf(id),1);
node.send(msg); send(msg);
node.status({}); node.status({});
}, wait); done();
}, wait, () => done());
node.idList.push(id); node.idList.push(id);
if ((node.timeout >= 1000) && (node.idList.length !== 0)) { if ((node.timeout >= 1000) && (node.idList.length !== 0)) {
node.status({fill:"blue",shape:"dot",text:parseInt(wait/10)/100+"s"}); node.status({fill:"blue",shape:"dot",text:parseInt(wait/10)/100+"s"});

View File

@ -82,10 +82,10 @@ module.exports = function(RED) {
var npay = {}; var npay = {};
var pendingMessages = []; var pendingMessages = [];
var activeMessagePromise = null; var activeMessagePromise = null;
var processMessageQueue = function(msg) { var processMessageQueue = function(msgInfo) {
if (msg) { if (msgInfo) {
// A new message has arrived - add it to the message queue // A new message has arrived - add it to the message queue
pendingMessages.push(msg); pendingMessages.push(msgInfo);
if (activeMessagePromise !== null) { if (activeMessagePromise !== null) {
// The node is currently processing a message, so do nothing // The node is currently processing a message, so do nothing
// more with this message // more with this message
@ -101,17 +101,17 @@ module.exports = function(RED) {
// There are more messages to process. Get the next message and // There are more messages to process. Get the next message and
// start processing it. Recurse back in to check for any more // start processing it. Recurse back in to check for any more
var nextMsg = pendingMessages.shift(); var nextMsgInfo = pendingMessages.shift();
activeMessagePromise = processMessage(nextMsg) activeMessagePromise = processMessage(nextMsgInfo)
.then(processMessageQueue) .then(processMessageQueue)
.catch((err) => { .catch((err) => {
node.error(err,nextMsg); nextMsgInfo.done(err);
return processMessageQueue(); return processMessageQueue();
}); });
} }
this.on('input', function(msg) { this.on('input', function(msg, send, done) {
processMessageQueue(msg); processMessageQueue({msg, send, done});
}); });
var stat = function() { var stat = function() {
@ -121,7 +121,8 @@ module.exports = function(RED) {
else return {fill:"blue",shape:"dot",text:l}; else return {fill:"blue",shape:"dot",text:l};
} }
var processMessage = function(msg) { var processMessage = function(msgInfo) {
let msg = msgInfo.msg;
var topic = RED.util.getMessageProperty(msg,node.topic) || "_none"; var topic = RED.util.getMessageProperty(msg,node.topic) || "_none";
var promise; var promise;
var delayDuration = node.duration; var delayDuration = node.duration;
@ -179,7 +180,10 @@ module.exports = function(RED) {
/* istanbul ignore else */ /* istanbul ignore else */
if (node.op1type !== "nul") { if (node.op1type !== "nul") {
var msg2 = RED.util.cloneMessage(msg); var msg2 = RED.util.cloneMessage(msg);
node.topics[topic].tout = setInterval(function() { node.send(RED.util.cloneMessage(msg2)); }, delayDuration); node.topics[topic].tout = setInterval(function() {
if (node.op1type === "date") { msg2.payload = Date.now(); }
msgInfo.send(RED.util.cloneMessage(msg2));
}, delayDuration);
} }
} }
else { else {
@ -203,14 +207,15 @@ module.exports = function(RED) {
} }
promise.then(() => { promise.then(() => {
if (node.op2type === "payl") { if (node.op2type === "payl") {
if (node.second === true) { node.send([null,npay[topic]]); } if (node.second === true) { msgInfo.send([null,npay[topic]]); }
else { node.send(npay[topic]); } else { msgInfo.send(npay[topic]); }
delete npay[topic]; delete npay[topic];
} }
else { else {
msg2.payload = node.topics[topic].m2; msg2.payload = node.topics[topic].m2;
if (node.second === true) { node.send([null,msg2]); } if (node.op2type === "date") { msg2.payload = Date.now(); }
else { node.send(msg2); } if (node.second === true) { msgInfo.send([null,msg2]); }
else { msgInfo.send(msg2); }
} }
delete node.topics[topic]; delete node.topics[topic];
node.status(stat()); node.status(stat());
@ -225,8 +230,9 @@ module.exports = function(RED) {
}, delayDuration); }, delayDuration);
} }
} }
msgInfo.done();
node.status(stat()); node.status(stat());
if (node.op1type !== "nul") { node.send(RED.util.cloneMessage(msg)); } if (node.op1type !== "nul") { msgInfo.send(RED.util.cloneMessage(msg)); }
}); });
}); });
} }
@ -262,8 +268,8 @@ module.exports = function(RED) {
} }
delete node.topics[topic]; delete node.topics[topic];
node.status(stat()); node.status(stat());
if (node.second === true) { node.send([null,msg2]); } if (node.second === true) { msgInfo.send([null,msg2]); }
else { node.send(msg2); } else { msgInfo.send(msg2); }
}).catch(err => { }).catch(err => {
node.error(err); node.error(err);
}); });
@ -273,6 +279,7 @@ module.exports = function(RED) {
// if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); } // if (node.op2type === "payl") {node.topics[topic].m2 = RED.util.cloneMessage(msg.payload); }
// } // }
} }
msgInfo.done();
return Promise.resolve(); return Promise.resolve();
} }
this.on("close", function() { this.on("close", function() {

View File

@ -52,7 +52,7 @@
color:"darksalmon", color:"darksalmon",
defaults: { defaults: {
command: {value:""}, command: {value:""},
addpay: {value:true}, addpay: {value:false},
append: {value:""}, append: {value:""},
useSpawn: {value:"false"}, useSpawn: {value:"false"},
timer: {value:""}, timer: {value:""},

View File

@ -31,7 +31,7 @@ module.exports = function(RED) {
this.timer = Number(n.timer || 0)*1000; this.timer = Number(n.timer || 0)*1000;
this.activeProcesses = {}; this.activeProcesses = {};
this.oldrc = (n.oldrc || false).toString(); this.oldrc = (n.oldrc || false).toString();
this.execOpt = {encoding:'binary', maxBuffer:10000000}; this.execOpt = {encoding:'binary', maxBuffer:RED.settings.execMaxBufferSize||10000000};
var node = this; var node = this;
if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; } if (process.platform === 'linux' && fs.existsSync('/bin/bash')) { node.execOpt.shell = '/bin/bash'; }

View File

@ -210,7 +210,7 @@ module.exports = function(RED) {
var httpMiddleware = function(req,res,next) { next(); } var httpMiddleware = function(req,res,next) { next(); }
if (RED.settings.httpNodeMiddleware) { if (RED.settings.httpNodeMiddleware) {
if (typeof RED.settings.httpNodeMiddleware === "function") { if (typeof RED.settings.httpNodeMiddleware === "function" || Array.isArray(RED.settings.httpNodeMiddleware)) {
httpMiddleware = RED.settings.httpNodeMiddleware; httpMiddleware = RED.settings.httpNodeMiddleware;
} }
} }

View File

@ -235,6 +235,7 @@
oneditprepare: function() { oneditprepare: function() {
var previous = null; var previous = null;
$("#node-input-out").on('focus', function () { previous = this.value; }).on("change", function() { $("#node-input-out").on('focus', function () { previous = this.value; }).on("change", function() {
$("#node-input-splitc").show();
if (previous === null) { previous = $("#node-input-out").val(); } if (previous === null) { previous = $("#node-input-out").val(); }
if ($("#node-input-out").val() == "char") { if ($("#node-input-out").val() == "char") {
if (previous != "char") { $("#node-input-splitc").val("\\n"); } if (previous != "char") { $("#node-input-splitc").val("\\n"); }
@ -247,6 +248,7 @@
else if ($("#node-input-out").val() == "immed") { else if ($("#node-input-out").val() == "immed") {
if (previous != "immed") { $("#node-input-splitc").val(" "); } if (previous != "immed") { $("#node-input-splitc").val(" "); }
$("#node-units").text(""); $("#node-units").text("");
$("#node-input-splitc").hide();
} }
else if ($("#node-input-out").val() == "count") { else if ($("#node-input-out").val() == "count") {
if (previous != "count") { $("#node-input-splitc").val("12"); } if (previous != "count") { $("#node-input-splitc").val("12"); }
@ -255,6 +257,7 @@
else { else {
if (previous != "sit") { $("#node-input-splitc").val(" "); } if (previous != "sit") { $("#node-input-splitc").val(" "); }
$("#node-units").text(""); $("#node-units").text("");
$("#node-input-splitc").hide();
} }
}); });
} }

View File

@ -18,7 +18,7 @@ module.exports = function(RED) {
"use strict"; "use strict";
function CSVNode(n) { function CSVNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.template = (n.temp || "").split(","); this.template = (n.temp || "");
this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r"); this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r");
this.quo = '"'; this.quo = '"';
this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r"); this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r");
@ -38,16 +38,12 @@ module.exports = function(RED) {
if (this.hdrout === true) { this.hdrout = "all"; } if (this.hdrout === true) { this.hdrout = "all"; }
var tmpwarn = true; var tmpwarn = true;
var node = this; var node = this;
var re = new RegExp(',(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
// pass in an array of column names to be trimed, de-quoted and retrimed // pass in an array of column names to be trimed, de-quoted and retrimed
var clean = function(col) { var clean = function(col) {
for (var t = 0; t < col.length; t++) { col = col.trim().split(re) || [""];
col[t] = col[t].trim(); // remove leading and trailing whitespace col = col.map(x => x.replace(/"/g,'').trim());
if (col[t].charAt(0) === '"' && col[t].charAt(col[t].length -1) === '"') {
// remove leading and trailing quotes (if they exist) - and remove whitepace again.
col[t] = col[t].substr(1,col[t].length -2).trim();
}
}
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; } if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
else { node.goodtmpl = true; } else { node.goodtmpl = true; }
return col; return col;
@ -55,7 +51,7 @@ module.exports = function(RED) {
node.template = clean(node.template); node.template = clean(node.template);
node.hdrSent = false; node.hdrSent = false;
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
node.hdrSent = false; node.hdrSent = false;
} }
@ -67,13 +63,14 @@ module.exports = function(RED) {
if (node.hdrout !== "none" && node.hdrSent === false) { if (node.hdrout !== "none" && node.hdrSent === false) {
if ((node.template.length === 1) && (node.template[0] === '')) { if ((node.template.length === 1) && (node.template[0] === '')) {
if (msg.hasOwnProperty("columns")) { if (msg.hasOwnProperty("columns")) {
node.template = clean((msg.columns || "").split(",")); node.template = clean(msg.columns || "");
} }
else { else {
node.template = Object.keys(msg.payload[0]); node.template = Object.keys(msg.payload[0]);
} }
} }
ou += node.template.join(node.sep) + node.ret; // ou += node.template.join(node.sep) + node.ret;
ou += node.template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret;
if (node.hdrout === "once") { node.hdrSent = true; } if (node.hdrout === "once") { node.hdrSent = true; }
} }
for (var s = 0; s < msg.payload.length; s++) { for (var s = 0; s < msg.payload.length; s++) {
@ -93,7 +90,7 @@ module.exports = function(RED) {
} }
else { else {
if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) { if ((node.template.length === 1) && (node.template[0] === '') && (msg.hasOwnProperty("columns"))) {
node.template = clean((msg.columns || "").split(",")); node.template = clean(msg.columns || "")//.split(","));
} }
if ((node.template.length === 1) && (node.template[0] === '')) { if ((node.template.length === 1) && (node.template[0] === '')) {
/* istanbul ignore else */ /* istanbul ignore else */
@ -144,10 +141,11 @@ module.exports = function(RED) {
} }
} }
msg.payload = ou; msg.payload = ou;
msg.columns = node.template.join(','); msg.columns = node.template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
if (msg.payload !== '') { node.send(msg); } if (msg.payload !== '') { send(msg); }
done();
} }
catch(e) { node.error(e,msg); } catch(e) { done(e); }
} }
else if (typeof msg.payload == "string") { // convert CSV string to object else if (typeof msg.payload == "string") { // convert CSV string to object
try { try {
@ -178,7 +176,7 @@ module.exports = function(RED) {
if ((node.hdrin === true) && first) { // if the template is in the first line if ((node.hdrin === true) && first) { // if the template is in the first line
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
if (line.length - i === 1) { tmp += line[i]; } if (line.length - i === 1) { tmp += line[i]; }
node.template = clean(tmp.split(node.sep)); node.template = clean(tmp);
first = false; first = false;
} }
else { tmp += line[i]; } else { tmp += line[i]; }
@ -254,22 +252,22 @@ module.exports = function(RED) {
} }
if (msg.parts.index + 1 === msg.parts.count) { if (msg.parts.index + 1 === msg.parts.count) {
msg.payload = node.store; msg.payload = node.store;
msg.columns = node.template.filter(val => val).join(','); msg.columns = node.template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
delete msg.parts; delete msg.parts;
node.send(msg); send(msg);
node.store = []; node.store = [];
} }
} }
else { else {
msg.columns = node.template.filter(val => val).join(','); msg.columns = node.template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
node.send(msg); // finally send the array send(msg); // finally send the array
} }
} }
else { else {
var len = a.length; var len = a.length;
for (var i = 0; i < len; i++) { for (var i = 0; i < len; i++) {
var newMessage = RED.util.cloneMessage(msg); var newMessage = RED.util.cloneMessage(msg);
newMessage.columns = node.template.filter(val => val).join(','); newMessage.columns = node.template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
newMessage.payload = a[i]; newMessage.payload = a[i];
if (!has_parts) { if (!has_parts) {
newMessage.parts = { newMessage.parts = {
@ -286,19 +284,21 @@ module.exports = function(RED) {
newMessage.parts.count -= 1; newMessage.parts.count -= 1;
} }
} }
node.send(newMessage); send(newMessage);
} }
} }
node.linecount = 0; node.linecount = 0;
done();
} }
catch(e) { node.error(e,msg); } catch(e) { done(e); }
} }
else { node.warn(RED._("csv.errors.csv_js")); } else { node.warn(RED._("csv.errors.csv_js")); done(); }
} }
else { else {
if (!msg.hasOwnProperty("reset")) { if (!msg.hasOwnProperty("reset")) {
node.send(msg); // If no payload and not reset - just pass it on. send(msg); // If no payload and not reset - just pass it on.
} }
done();
} }
}); });
} }

View File

@ -17,18 +17,18 @@
module.exports = function(RED) { module.exports = function(RED) {
"use strict"; "use strict";
function sendArray(node,msg,array) { function sendArray(node,msg,array,send) {
for (var i = 0; i < array.length-1; i++) { for (var i = 0; i < array.length-1; i++) {
msg.payload = array[i]; msg.payload = array[i];
msg.parts.index = node.c++; msg.parts.index = node.c++;
if (node.stream !== true) { msg.parts.count = array.length; } if (node.stream !== true) { msg.parts.count = array.length; }
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
if (node.stream !== true) { if (node.stream !== true) {
msg.payload = array[i]; msg.payload = array[i];
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = array.length; msg.parts.count = array.length;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.c = 0; node.c = 0;
} }
else { node.remainder = array[i]; } else { node.remainder = array[i]; }
@ -67,7 +67,8 @@ module.exports = function(RED) {
} }
node.c = 0; node.c = 0;
node.buffer = Buffer.from([]); node.buffer = Buffer.from([]);
this.on("input", function(msg) { node.pendingDones = [];
this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("payload")) { if (msg.hasOwnProperty("payload")) {
if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack if (msg.hasOwnProperty("parts")) { msg.parts = { parts:msg.parts }; } // push existing parts to a stack
else { msg.parts = {}; } else { msg.parts = {}; }
@ -93,14 +94,23 @@ module.exports = function(RED) {
msg.payload = data.substring(pos,pos+node.splt); msg.payload = data.substring(pos,pos+node.splt);
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
}
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
node.remainder = data.substring(pos); node.remainder = data.substring(pos);
if ((node.stream !== true) || (node.remainder.length === node.splt)) { if ((node.stream !== true) || (node.remainder.length === node.splt)) {
msg.payload = node.remainder; msg.payload = node.remainder;
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
done();
node.remainder = ""; node.remainder = "";
} else {
node.pendingDones.push(done);
} }
} }
else { else {
@ -115,7 +125,8 @@ module.exports = function(RED) {
a = msg.payload.split(node.splt); a = msg.payload.split(node.splt);
msg.parts.ch = node.splt; // pass the split char to other end for rejoin msg.parts.ch = node.splt; // pass the split char to other end for rejoin
} }
sendArray(node,msg,a); sendArray(node,msg,a,send);
done();
} }
} }
else if (Array.isArray(msg.payload)) { // then split array into messages else if (Array.isArray(msg.payload)) { // then split array into messages
@ -135,8 +146,9 @@ module.exports = function(RED) {
} }
msg.parts.index = i; msg.parts.index = i;
pos += node.arraySplt; pos += node.arraySplt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
} }
done();
} }
else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) { else if ((typeof msg.payload === "object") && !Buffer.isBuffer(msg.payload)) {
var j = 0; var j = 0;
@ -152,10 +164,11 @@ module.exports = function(RED) {
msg.parts.key = p; msg.parts.key = p;
msg.parts.index = j; msg.parts.index = j;
msg.parts.count = l; msg.parts.count = l;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
j += 1; j += 1;
} }
} }
done();
} }
else if (Buffer.isBuffer(msg.payload)) { else if (Buffer.isBuffer(msg.payload)) {
var len = node.buffer.length + msg.payload.length; var len = node.buffer.length + msg.payload.length;
@ -176,14 +189,23 @@ module.exports = function(RED) {
msg.payload = buff.slice(pos,pos+node.splt); msg.payload = buff.slice(pos,pos+node.splt);
msg.parts.index = node.c++; msg.parts.index = node.c++;
pos += node.splt; pos += node.splt;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
}
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
node.buffer = buff.slice(pos); node.buffer = buff.slice(pos);
if ((node.stream !== true) || (node.buffer.length === node.splt)) { if ((node.stream !== true) || (node.buffer.length === node.splt)) {
msg.payload = node.buffer; msg.payload = node.buffer;
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
done();
node.buffer = Buffer.from([]); node.buffer = Buffer.from([]);
} else {
node.pendingDones.push(done);
} }
} }
else { else {
@ -210,23 +232,34 @@ module.exports = function(RED) {
while (pos > -1) { while (pos > -1) {
msg.payload = buff.slice(p,pos); msg.payload = buff.slice(p,pos);
msg.parts.index = node.c++; msg.parts.index = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
i++; i++;
p = pos+node.splt.length; p = pos+node.splt.length;
pos = buff.indexOf(node.splt,p); pos = buff.indexOf(node.splt,p);
} }
if (count > 1) {
node.pendingDones.forEach(d => d());
node.pendingDones = [];
}
if ((node.stream !== true) && (p < buff.length)) { if ((node.stream !== true) && (p < buff.length)) {
msg.payload = buff.slice(p,buff.length); msg.payload = buff.slice(p,buff.length);
msg.parts.index = node.c++; msg.parts.index = node.c++;
msg.parts.count = node.c++; msg.parts.count = node.c++;
node.send(RED.util.cloneMessage(msg)); send(RED.util.cloneMessage(msg));
node.pendingDones.forEach(d => d());
node.pendingDones = [];
} }
else { else {
node.buffer = buff.slice(p,buff.length); node.buffer = buff.slice(p,buff.length);
node.pendingDones.push(done);
}
if (node.buffer.length == 0) {
done();
} }
} }
} else { // otherwise drop the message.
done();
} }
//else { } // otherwise drop the message.
} }
}); });
} }
@ -264,16 +297,16 @@ module.exports = function(RED) {
} }
function reduceMessageGroup(node,msgs,exp,fixup,count,accumulator,done) { function reduceMessageGroup(node,msgInfos,exp,fixup,count,accumulator,done) {
var msg = msgs.shift(); var msgInfo = msgInfos.shift();
exp.assign("I", msg.parts.index); exp.assign("I", msgInfo.msg.parts.index);
exp.assign("N", count); exp.assign("N", count);
exp.assign("A", accumulator); exp.assign("A", accumulator);
RED.util.evaluateJSONataExpression(exp, msg, (err,result) => { RED.util.evaluateJSONataExpression(exp, msgInfo.msg, (err,result) => {
if (err) { if (err) {
return done(err); return done(err);
} }
if (msgs.length === 0) { if (msgInfos.length === 0) {
if (fixup) { if (fixup) {
fixup.assign("N", count); fixup.assign("N", count);
fixup.assign("A", result); fixup.assign("A", result);
@ -281,39 +314,43 @@ module.exports = function(RED) {
if (err) { if (err) {
return done(err); return done(err);
} }
node.send({payload: result}); msgInfo.send({payload: result});
done(); done();
}); });
} else { } else {
node.send({payload: result}); msgInfo.send({payload: result});
done(); done();
} }
} else { } else {
reduceMessageGroup(node,msgs,exp,fixup,count,result,done); reduceMessageGroup(node,msgInfos,exp,fixup,count,result,done);
} }
}); });
} }
function reduceAndSendGroup(node, group, done) { function reduceAndSendGroup(node, group, done) {
var is_right = node.reduce_right; var is_right = node.reduce_right;
var flag = is_right ? -1 : 1; var flag = is_right ? -1 : 1;
var msgs = group.msgs; var msgInfos = group.msgs;
const preservedMsgInfos = [...msgInfos];
try { try {
RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => { RED.util.evaluateNodeProperty(node.exp_init, node.exp_init_type, node, {}, (err,accum) => {
var reduceExpression = node.reduceExpression; var reduceExpression = node.reduceExpression;
var fixupExpression = node.fixupExpression; var fixupExpression = node.fixupExpression;
var count = group.count; var count = group.count;
msgs.sort(function(x,y) { msgInfos.sort(function(x,y) {
var ix = x.parts.index; var ix = x.msg.parts.index;
var iy = y.parts.index; var iy = y.msg.parts.index;
if (ix < iy) {return -flag;} if (ix < iy) {return -flag;}
if (ix > iy) {return flag;} if (ix > iy) {return flag;}
return 0; return 0;
}); });
reduceMessageGroup(node, msgs,reduceExpression,fixupExpression,count,accum,(err,result) => { reduceMessageGroup(node, msgInfos,reduceExpression,fixupExpression,count,accum,(err,result) => {
if (err) { if (err) {
preservedMsgInfos.pop(); // omit last message to emit error message
preservedMsgInfos.forEach(mInfo => mInfo.done());
done(err); done(err);
return; return;
} else { } else {
preservedMsgInfos.forEach(mInfo => mInfo.done());
done(); done();
} }
}) })
@ -323,7 +360,8 @@ module.exports = function(RED) {
} }
} }
function reduceMessage(node, msg, done) { function reduceMessage(node, msgInfo, done) {
let msg = msgInfo.msg;
if (msg.hasOwnProperty('parts')) { if (msg.hasOwnProperty('parts')) {
var parts = msg.parts; var parts = msg.parts;
var pending = node.pending; var pending = node.pending;
@ -344,7 +382,7 @@ module.exports = function(RED) {
if (parts.hasOwnProperty('count') && (group.count === undefined)) { if (parts.hasOwnProperty('count') && (group.count === undefined)) {
group.count = parts.count; group.count = parts.count;
} }
msgs.push(msg); msgs.push(msgInfo);
pending_count++; pending_count++;
var completeProcess = function(err) { var completeProcess = function(err) {
if (err) { if (err) {
@ -353,6 +391,13 @@ module.exports = function(RED) {
node.pending_count = pending_count; node.pending_count = pending_count;
var max_msgs = maxKeptMsgsCount(node); var max_msgs = maxKeptMsgsCount(node);
if ((max_msgs > 0) && (pending_count > max_msgs)) { if ((max_msgs > 0) && (pending_count > max_msgs)) {
Object.values(node.pending).forEach(group => {
group.msgs.forEach(mInfo => {
if (mInfo.msg._msgid !== msgInfo.msg._msgid) {
mInfo.done();
}
});
});
node.pending = {}; node.pending = {};
node.pending_count = 0; node.pending_count = 0;
done(RED._("join.too-many")); done(RED._("join.too-many"));
@ -368,7 +413,8 @@ module.exports = function(RED) {
completeProcess(); completeProcess();
} }
} else { } else {
node.send(msg); msgInfo.send(msg);
msgInfo.done();
done(); done();
} }
} }
@ -480,7 +526,9 @@ module.exports = function(RED) {
delete group.msg.parts; delete group.msg.parts;
} }
delete group.msg.complete; delete group.msg.complete;
node.send(RED.util.cloneMessage(group.msg)); group.send(RED.util.cloneMessage(group.msg));
group.dones.forEach(f => f());
group.dones = [];
} }
var pendingMessages = []; var pendingMessages = [];
@ -489,10 +537,10 @@ module.exports = function(RED) {
// groups may overlap and cause unexpected results. The use of JSONata // groups may overlap and cause unexpected results. The use of JSONata
// means some async processing *might* occur if flow/global context is // means some async processing *might* occur if flow/global context is
// accessed. // accessed.
var processReduceMessageQueue = function(msg) { var processReduceMessageQueue = function(msgInfo) {
if (msg) { if (msgInfo) {
// A new message has arrived - add it to the message queue // A new message has arrived - add it to the message queue
pendingMessages.push(msg); pendingMessages.push(msgInfo);
if (activeMessage !== null) { if (activeMessage !== null) {
// The node is currently processing a message, so do nothing // The node is currently processing a message, so do nothing
// more with this message // more with this message
@ -508,22 +556,23 @@ module.exports = function(RED) {
// There are more messages to process. Get the next message and // There are more messages to process. Get the next message and
// start processing it. Recurse back in to check for any more // start processing it. Recurse back in to check for any more
var nextMsg = pendingMessages.shift(); var nextMsgInfo = pendingMessages.shift();
activeMessage = true; activeMessage = true;
reduceMessage(node, nextMsg, err => { reduceMessage(node, nextMsgInfo, err => {
if (err) { if (err) {
node.error(err,nextMsg); nextMsgInfo.done(err);//.error(err,nextMsg);
} }
activeMessage = null; activeMessage = null;
processReduceMessageQueue(); processReduceMessageQueue();
}) })
} }
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
try { try {
var property; var property;
if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) { if (node.mode === 'auto' && (!msg.hasOwnProperty("parts")||!msg.parts.hasOwnProperty("id"))) {
node.warn("Message missing msg.parts property - cannot join in 'auto' mode") node.warn("Message missing msg.parts property - cannot join in 'auto' mode")
done();
return; return;
} }
@ -535,6 +584,7 @@ module.exports = function(RED) {
property = RED.util.getMessageProperty(msg,node.property); property = RED.util.getMessageProperty(msg,node.property);
} catch(err) { } catch(err) {
node.warn("Message property "+node.property+" not found"); node.warn("Message property "+node.property+" not found");
done();
return; return;
} }
} }
@ -557,7 +607,7 @@ module.exports = function(RED) {
propertyIndex = msg.parts.index; propertyIndex = msg.parts.index;
} }
else if (node.mode === 'reduce') { else if (node.mode === 'reduce') {
return processReduceMessageQueue(msg); return processReduceMessageQueue({msg, send, done});
} }
else { else {
// Use the node configuration to identify all of the group information // Use the node configuration to identify all of the group information
@ -578,9 +628,11 @@ module.exports = function(RED) {
if (inflight[partId].timeout) { if (inflight[partId].timeout) {
clearTimeout(inflight[partId].timeout); clearTimeout(inflight[partId].timeout);
} }
inflight[partId].dones.forEach(f => f());
delete inflight[partId] delete inflight[partId]
} }
return done();
return;
} }
if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) { if ((payloadType === 'object') && (propertyKey === null || propertyKey === undefined || propertyKey === "")) {
@ -591,6 +643,7 @@ module.exports = function(RED) {
if (msg.hasOwnProperty('complete')) { if (msg.hasOwnProperty('complete')) {
if (inflight[partId]) { if (inflight[partId]) {
inflight[partId].msg.complete = msg.complete; inflight[partId].msg.complete = msg.complete;
inflight[partId].send = send;
completeSend(partId); completeSend(partId);
} }
} }
@ -598,6 +651,7 @@ module.exports = function(RED) {
node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object") node.warn("Message missing key property 'msg."+node.key+"' - cannot add to object")
} }
} }
done();
return; return;
} }
@ -608,7 +662,9 @@ module.exports = function(RED) {
payload:{}, payload:{},
targetCount:targetCount, targetCount:targetCount,
type:"object", type:"object",
msg:RED.util.cloneMessage(msg) msg:RED.util.cloneMessage(msg),
send: send,
dones: []
}; };
} }
else { else {
@ -617,7 +673,9 @@ module.exports = function(RED) {
payload:[], payload:[],
targetCount:targetCount, targetCount:targetCount,
type:payloadType, type:payloadType,
msg:RED.util.cloneMessage(msg) msg:RED.util.cloneMessage(msg),
send: send,
dones: []
}; };
if (payloadType === 'string') { if (payloadType === 'string') {
inflight[partId].joinChar = joinChar; inflight[partId].joinChar = joinChar;
@ -634,6 +692,7 @@ module.exports = function(RED) {
}, node.timer) }, node.timer)
} }
} }
inflight[partId].dones.push(done);
var group = inflight[partId]; var group = inflight[partId];
if (payloadType === 'buffer') { if (payloadType === 'buffer') {
@ -642,7 +701,7 @@ module.exports = function(RED) {
inflight[partId].bufferLen += property.length; inflight[partId].bufferLen += property.length;
} }
else { else {
node.error(RED._("join.errors.invalid-type",{error:(typeof property)}),msg); done(RED._("join.errors.invalid-type",{error:(typeof property)}));
return; return;
} }
} }
@ -676,13 +735,18 @@ module.exports = function(RED) {
} }
} }
group.msg = Object.assign(group.msg, msg); group.msg = Object.assign(group.msg, msg);
group.send = send;
var tcnt = group.targetCount; var tcnt = group.targetCount;
if (msg.hasOwnProperty("parts")) { tcnt = group.targetCount || msg.parts.count; } if (msg.hasOwnProperty("parts")) {
tcnt = group.targetCount || msg.parts.count;
group.targetCount = tcnt;
}
if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) { if ((tcnt > 0 && group.currentCount >= tcnt) || msg.hasOwnProperty('complete')) {
completeSend(partId); completeSend(partId);
} }
} }
catch(err) { catch(err) {
done(err);
console.log(err.stack); console.log(err.stack);
} }
}); });
@ -691,9 +755,11 @@ module.exports = function(RED) {
for (var i in inflight) { for (var i in inflight) {
if (inflight.hasOwnProperty(i)) { if (inflight.hasOwnProperty(i)) {
clearTimeout(inflight[i].timeout); clearTimeout(inflight[i].timeout);
inflight[i].dones.forEach(d => d());
} }
} }
}); });
} }
RED.nodes.registerType("join",JoinNode); RED.nodes.registerType("join",JoinNode);
} }

View File

@ -81,16 +81,16 @@ module.exports = function(RED) {
function sortMessageGroup(group) { function sortMessageGroup(group) {
var promise; var promise;
var msgs = group.msgs; var msgInfos = group.msgInfos;
if (key_is_exp) { if (key_is_exp) {
var evaluatedDataPromises = msgs.map(msg => { var evaluatedDataPromises = msgInfos.map(mInfo => {
return new Promise((resolve,reject) => { return new Promise((resolve,reject) => {
RED.util.evaluateJSONataExpression(key_exp, msg, (err, result) => { RED.util.evaluateJSONataExpression(key_exp, mInfo.msg, (err, result) => {
if (err) { if (err) {
reject(RED._("sort.invalid-exp",{message:err.toString()})); reject(RED._("sort.invalid-exp",{message:err.toString()}));
} else { } else {
resolve({ resolve({
item: msg, item: mInfo,
sortValue: result sortValue: result
}) })
} }
@ -106,20 +106,21 @@ module.exports = function(RED) {
var key = function(msg) { var key = function(msg) {
return ; return ;
} }
var comp = generateComparisonFunction(msg => RED.util.getMessageProperty(msg, key_prop)); var comp = generateComparisonFunction(mInfo => RED.util.getMessageProperty(mInfo.msg, key_prop));
try { try {
msgs.sort(comp); msgInfos.sort(comp);
} }
catch (e) { catch (e) {
return; // not send when error return; // not send when error
} }
promise = Promise.resolve(msgs); promise = Promise.resolve(msgInfos);
} }
return promise.then(msgs => { return promise.then(msgInfos => {
for (var i = 0; i < msgs.length; i++) { for (let i = 0; i < msgInfos.length; i++) {
var msg = msgs[i]; const msg = msgInfos[i].msg;
msg.parts.index = i; msg.parts.index = i;
node.send(msg); msgInfos[i].send(msg);
msgInfos[i].done();
} }
}); });
} }
@ -181,65 +182,79 @@ module.exports = function(RED) {
} }
} }
if(oldest !== undefined) { if(oldest !== undefined) {
oldest.msgInfos[oldest.msgInfos.length - 1].done(RED._("sort.too-many"));
for (let i = 0; i < oldest.msgInfos.length - 1; i++) {
oldest.msgInfos[i].done();
}
delete pending[oldest_key]; delete pending[oldest_key];
return oldest.msgs.length; return oldest.msgInfos.length;
} }
return 0; return 0;
} }
function processMessage(msg) { function processMessage(msgInfo) {
const msg = msgInfo.msg;
if (target_is_prop) { if (target_is_prop) {
sortMessageProperty(msg).then(send => { sortMessageProperty(msg).then(send => {
if (send) { if (send) {
node.send(msg); msgInfo.send(msg);
} }
msgInfo.done();
}).catch(err => { }).catch(err => {
node.error(err,msg); msgInfo.done(err);
}); });
return; return;
} }
var parts = msg.parts; var parts = msg.parts;
if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) { if (!parts || !parts.hasOwnProperty("id") || !parts.hasOwnProperty("index")) {
msgInfo.done();
return; return;
} }
var gid = parts.id; var gid = parts.id;
if (!pending.hasOwnProperty(gid)) { if (!pending.hasOwnProperty(gid)) {
pending[gid] = { pending[gid] = {
count: undefined, count: undefined,
msgs: [], msgInfos: [],
seq_no: pending_id++ seq_no: pending_id++
}; };
} }
var group = pending[gid]; var group = pending[gid];
var msgs = group.msgs; var msgInfos = group.msgInfos;
msgs.push(msg); msgInfos.push(msgInfo);
if (parts.hasOwnProperty("count")) { if (parts.hasOwnProperty("count")) {
group.count = parts.count; group.count = parts.count;
} }
pending_count++; pending_count++;
if (group.count === msgs.length) { if (group.count === msgInfos.length) {
delete pending[gid] delete pending[gid]
sortMessageGroup(group).catch(err => { sortMessageGroup(group).catch(err => {
node.error(err,msg); // throw an error for last message, and just call done() for remaining messages
msgInfos[msgInfos.length-1].done(err);
for (let i = 0; i < msgInfos.length - 1; i++) {
msgInfos[i].done()
};
}); });
pending_count -= msgs.length; pending_count -= msgInfos.length;
} else { } else {
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (pending_count > max_msgs)) { if ((max_msgs > 0) && (pending_count > max_msgs)) {
pending_count -= removeOldestPending(); pending_count -= removeOldestPending();
node.error(RED._("sort.too-many"), msg);
} }
} }
} }
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
processMessage(msg); processMessage({msg, send, done});
}); });
this.on("close", function() { this.on("close", function() {
for(var key in pending) { for(var key in pending) {
if (pending.hasOwnProperty(key)) { if (pending.hasOwnProperty(key)) {
node.log(RED._("sort.clear"), pending[key].msgs[0]); node.log(RED._("sort.clear"), pending[key].msgInfos[0]);
const group = pending[key];
group.msgInfos.forEach(mInfo => {
mInfo.done();
});
delete pending[key]; delete pending[key];
} }
} }

View File

@ -32,11 +32,11 @@ module.exports = function(RED) {
return _max_kept_msgs_count; return _max_kept_msgs_count;
} }
function send_msgs(node, msgs, clone_msg) { function send_msgs(node, msgInfos, clone_msg) {
var count = msgs.length; var count = msgInfos.length;
var msg_id = msgs[0]._msgid; var msg_id = msgInfos[0].msg._msgid;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
var msg = clone_msg ? RED.util.cloneMessage(msgs[i]) : msgs[i]; var msg = clone_msg ? RED.util.cloneMessage(msgInfos[i].msg) : msgInfos[i].msg;
if (!msg.hasOwnProperty("parts")) { if (!msg.hasOwnProperty("parts")) {
msg.parts = {}; msg.parts = {};
} }
@ -44,14 +44,16 @@ module.exports = function(RED) {
parts.id = msg_id; parts.id = msg_id;
parts.index = i; parts.index = i;
parts.count = count; parts.count = count;
node.send(msg); msgInfos[i].send(msg);
//msgInfos[i].done();
} }
} }
function send_interval(node, allow_empty_seq) { function send_interval(node, allow_empty_seq) {
let msgs = node.pending; let msgInfos = node.pending;
if (msgs.length > 0) { if (msgInfos.length > 0) {
send_msgs(node, msgs, false); send_msgs(node, msgInfos, false);
msgInfos.forEach(e => e.done());
node.pending = []; node.pending = [];
} }
else { else {
@ -108,19 +110,20 @@ module.exports = function(RED) {
return; return;
} }
} }
var msgs = []; var msgInfos = [];
for (var topic of topics) { for (var topic of topics) {
var t_msgs = get_msgs_of_topic(pending, topic); var t_msgInfos = get_msgs_of_topic(pending, topic);
msgs = msgs.concat(t_msgs); msgInfos = msgInfos.concat(t_msgInfos);
} }
for (var topic of topics) { for (var topic of topics) {
remove_topic(pending, topic); remove_topic(pending, topic);
} }
send_msgs(node, msgs, true); send_msgs(node, msgInfos, true);
node.pending_count -= msgs.length; msgInfos.forEach(e => e.done() );
node.pending_count -= msgInfos.length;
} }
function add_to_topic_group(pending, topic, gid, msg) { function add_to_topic_group(pending, topic, gid, msgInfo) {
if (!pending.hasOwnProperty(topic)) { if (!pending.hasOwnProperty(topic)) {
pending[topic] = { groups: {}, gids: [] }; pending[topic] = { groups: {}, gids: [] };
} }
@ -132,32 +135,43 @@ module.exports = function(RED) {
gids.push(gid); gids.push(gid);
} }
var group = groups[gid]; var group = groups[gid];
group.msgs.push(msg); group.msgs.push(msgInfo);
if ((group.count === undefined) && if ((group.count === undefined) &&
msg.parts.hasOwnProperty('count')) { msgInfo.msg.parts.hasOwnProperty('count')) {
group.count = msg.parts.count; group.count = msgInfo.msg.parts.count;
} }
} }
function concat_msg(node, msg) { function concat_msg(node, msg, send, done) {
var topic = msg.topic; var topic = msg.topic;
if(node.topics.indexOf(topic) >= 0) { if(node.topics.indexOf(topic) >= 0) {
if (!msg.hasOwnProperty("parts") || if (!msg.hasOwnProperty("parts") ||
!msg.parts.hasOwnProperty("id") || !msg.parts.hasOwnProperty("id") ||
!msg.parts.hasOwnProperty("index") || !msg.parts.hasOwnProperty("index") ||
!msg.parts.hasOwnProperty("count")) { !msg.parts.hasOwnProperty("count")) {
node.error(RED._("batch.no-parts"), msg); done(RED._("batch.no-parts"));
return; return;
} }
var gid = msg.parts.id; var gid = msg.parts.id;
var pending = node.pending; var pending = node.pending;
add_to_topic_group(pending, topic, gid, msg); add_to_topic_group(pending, topic, gid, {msg, send, done});
node.pending_count++; node.pending_count++;
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (node.pending_count > max_msgs)) { if ((max_msgs > 0) && (node.pending_count > max_msgs)) {
Object.values(node.pending).forEach(p_topic => {
Object.values(p_topic.groups).forEach(group => {
group.msgs.forEach(msgInfo => {
if (msgInfo.msg.id === msg.id) {
// the message that caused the overflow
msgInfo.done(RED._("batch.too-many"));
} else {
msgInfo.done();
}
})
})
});
node.pending = {}; node.pending = {};
node.pending_count = 0; node.pending_count = 0;
node.error(RED._("batch.too-many"), msg);
} }
try_concat(node, pending); try_concat(node, pending);
} }
@ -178,29 +192,37 @@ module.exports = function(RED) {
return; return;
} }
node.pending = []; node.pending = [];
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
node.pending.forEach(e => e.done());
node.pending = []; node.pending = [];
node.pending_count = 0; node.pending_count = 0;
done();
return; return;
} }
var queue = node.pending; var queue = node.pending;
queue.push(msg); queue.push({msg, send, done});
node.pending_count++; node.pending_count++;
if (queue.length === count) { if (queue.length === count) {
send_msgs(node, queue, is_overlap); send_msgs(node, queue, is_overlap);
for (let i = 0; i < queue.length-overlap; i++) {
queue[i].done();
}
node.pending = node.pending =
(overlap === 0) ? [] : queue.slice(-overlap); (overlap === 0) ? [] : queue.slice(-overlap);
node.pending_count = 0; node.pending_count = 0;
} }
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (node.pending_count > max_msgs)) { if ((max_msgs > 0) && (node.pending_count > max_msgs)) {
let lastMInfo = node.pending.pop();
lastMInfo.done(RED._("batch.too-many"));
node.pending.forEach(e => e.done());
node.pending = []; node.pending = [];
node.pending_count = 0; node.pending_count = 0;
node.error(RED._("batch.too-many"), msg);
} }
}); });
this.on("close", function() { this.on("close", function() {
node.pending.forEach(e=> e.done());
node.pending_count = 0; node.pending_count = 0;
node.pending = []; node.pending = [];
}); });
@ -217,31 +239,36 @@ module.exports = function(RED) {
if (interval > 0) { if (interval > 0) {
timer = setInterval(msgHandler, interval); timer = setInterval(msgHandler, interval);
} }
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
if (timer !== undefined) { if (timer !== undefined) {
clearInterval(timer); clearInterval(timer);
} }
node.pending.forEach(e => e.done());
node.pending = []; node.pending = [];
node.pending_count = 0; node.pending_count = 0;
done();
if (interval > 0) { if (interval > 0) {
timer = setInterval(msgHandler, interval); timer = setInterval(msgHandler, interval);
} }
return; return;
} }
node.pending.push(msg); node.pending.push({msg, send, done});
node.pending_count++; node.pending_count++;
var max_msgs = max_kept_msgs_count(node); var max_msgs = max_kept_msgs_count(node);
if ((max_msgs > 0) && (node.pending_count > max_msgs)) { if ((max_msgs > 0) && (node.pending_count > max_msgs)) {
let lastMInfo = node.pending.pop();
lastMInfo.done(RED._("batch.too-many"));
node.pending.forEach(e => e.done());
node.pending = []; node.pending = [];
node.pending_count = 0; node.pending_count = 0;
node.error(RED._("batch.too-many"), msg);
} }
}); });
this.on("close", function() { this.on("close", function() {
if (timer !== undefined) { if (timer !== undefined) {
clearInterval(timer); clearInterval(timer);
} }
node.pending.forEach(e => e.done());
node.pending = []; node.pending = [];
node.pending_count = 0; node.pending_count = 0;
}); });
@ -251,15 +278,26 @@ module.exports = function(RED) {
return x.topic; return x.topic;
}); });
node.pending = {}; node.pending = {};
this.on("input", function(msg) { this.on("input", function(msg, send, done) {
if (msg.hasOwnProperty("reset")) { if (msg.hasOwnProperty("reset")) {
Object.values(node.pending).forEach(p_topic => {
Object.values(p_topic.groups).forEach(group => {
group.msgs.forEach(e => e.done());
});
});
node.pending = {}; node.pending = {};
node.pending_count = 0; node.pending_count = 0;
done();
return; return;
} }
concat_msg(node, msg); concat_msg(node, msg, send, done);
}); });
this.on("close", function() { this.on("close", function() {
Object.values(node.pending).forEach(p_topic => {
Object.values(p_topic.groups).forEach(group => {
group.msgs.forEach(e => e.done());
});
});
node.pending = {}; node.pending = {};
node.pending_count = 0; node.pending_count = 0;
}); });

View File

@ -0,0 +1,99 @@
[
{
"id": "330f4888.cccb28",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 180,
"wires": [
[
"ed11f8d6.5e3c88"
]
]
},
{
"id": "a0288b44.71d488",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": "",
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 180,
"wires": [
[
"369cbe42.4af9f2"
]
]
},
{
"id": "ed11f8d6.5e3c88",
"type": "template",
"z": "4b63452d.672afc",
"name": "CSV data",
"field": "payload",
"fieldType": "msg",
"format": "text",
"syntax": "mustache",
"template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines",
"output": "str",
"x": 430,
"y": 180,
"wires": [
[
"a0288b44.71d488"
]
]
},
{
"id": "369cbe42.4af9f2",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 180,
"wires": []
},
{
"id": "783cfaa6.52fbe4",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Parse CSV with default column name as messages",
"info": "CSV node can parse input CSV data.\nParsed CSV record can be sent as a message sequence.\nEach message payload points to an object with `col`*N* as a key and CSV value as a value.\n",
"x": 330,
"y": 120,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "98c9d44d.4457b8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 360,
"wires": [
[
"65476517.3d760c"
]
]
},
{
"id": "76df98f7.0dcd08",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": "",
"hdrout": "none",
"multi": "mult",
"ret": "\\n",
"temp": "",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 360,
"wires": [
[
"557979e0.e6b588"
]
]
},
{
"id": "65476517.3d760c",
"type": "template",
"z": "4b63452d.672afc",
"name": "CSV data",
"field": "payload",
"fieldType": "msg",
"format": "text",
"syntax": "mustache",
"template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines",
"output": "str",
"x": 430,
"y": 360,
"wires": [
[
"76df98f7.0dcd08"
]
]
},
{
"id": "557979e0.e6b588",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 360,
"wires": []
},
{
"id": "187f4ab3.4c9ab5",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Parse CSV with default column name as array",
"info": "CSV node can send a single message with array of parsed CSV records.\nEach element of the array consists of objects with key-value pair.",
"x": 320,
"y": 300,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "1216e95b.1b1e87",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 560,
"wires": [
[
"e41ffbbc.de2ed8"
]
]
},
{
"id": "286828bc.9233c8",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": "",
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "kind,price,origin",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 560,
"wires": [
[
"9d8218c.5550ee8"
]
]
},
{
"id": "e41ffbbc.de2ed8",
"type": "template",
"z": "4b63452d.672afc",
"name": "CSV data",
"field": "payload",
"fieldType": "msg",
"format": "text",
"syntax": "mustache",
"template": "Apple,100,Canada\nOrange,120,USA\nBanana,80,Philippines",
"output": "str",
"x": 430,
"y": 560,
"wires": [
[
"286828bc.9233c8"
]
]
},
{
"id": "9d8218c.5550ee8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 560,
"wires": []
},
{
"id": "aaa1ee8f.21e2c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Parse CSV with specified column name as messages",
"info": "CSV node can specify column name of parsed objects in its settings panel.",
"x": 340,
"y": 500,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "24093558.0315aa",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 740,
"wires": [
[
"80abaee1.5fa7f"
]
]
},
{
"id": "d4d2ca3f.1d9488",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": true,
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 740,
"wires": [
[
"b52791c3.08967"
]
]
},
{
"id": "80abaee1.5fa7f",
"type": "template",
"z": "4b63452d.672afc",
"name": "CSV data",
"field": "payload",
"fieldType": "msg",
"format": "text",
"syntax": "mustache",
"template": "kind,price,origin\nApple,100,Canada\nOrange,120,USA\nBanana,80,Philippines",
"output": "str",
"x": 430,
"y": 740,
"wires": [
[
"d4d2ca3f.1d9488"
]
]
},
{
"id": "b52791c3.08967",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 740,
"wires": []
},
{
"id": "85091361.85644",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Parse CSV with column name in first row as messages",
"info": "CSV node can use first row of input CSV text as a column name of each record object.\n",
"x": 340,
"y": 680,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "9e93169c.b763a8",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert JavaScript object to CSV",
"info": "CSV node can convert a JavaScript object to CSV text.\nEach object contains key-value pair of specified properties.\n",
"x": 270,
"y": 860,
"wires": []
},
{
"id": "8ca41fee.3303d",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 920,
"wires": [
[
"c466905b.e8c61"
]
]
},
{
"id": "65146d20.d78204",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 920,
"wires": [
[
"92e99e67.a37d8"
]
]
},
{
"id": "c466905b.e8c61",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "json",
"x": 430,
"y": 920,
"wires": [
[
"65146d20.d78204"
]
]
},
{
"id": "92e99e67.a37d8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 920,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "e89019c5.70ae78",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert array of JavaScript objects to CSV",
"info": "CSV node can convert an array of JavaScript objects to multi-line CSV text.",
"x": 300,
"y": 1020,
"wires": []
},
{
"id": "bd0d82ed.7b28",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 1080,
"wires": [
[
"1d857b8d.3a4014"
]
]
},
{
"id": "66a37667.16ebd8",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 1080,
"wires": [
[
"859725fd.dc93d8"
]
]
},
{
"id": "1d857b8d.3a4014",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]",
"output": "json",
"x": 430,
"y": 1080,
"wires": [
[
"66a37667.16ebd8"
]
]
},
{
"id": "859725fd.dc93d8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 1080,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "2ebdd51e.c5d17a",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert array of JavaScript objects to CSV with column name header",
"info": "CSV node can convert an array of JavaScript objects to multi-line CSV text with column name header at first line.",
"x": 390,
"y": 1200,
"wires": []
},
{
"id": "2b4d538d.ada07c",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 1260,
"wires": [
[
"3e5c9e8.5065b62"
]
]
},
{
"id": "db02c7be.0984e8",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "all",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 1260,
"wires": [
[
"61f8b772.ddb1f8"
]
]
},
{
"id": "3e5c9e8.5065b62",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]",
"output": "json",
"x": 430,
"y": 1260,
"wires": [
[
"db02c7be.0984e8"
]
]
},
{
"id": "61f8b772.ddb1f8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 1260,
"wires": []
}
]

View File

@ -0,0 +1,99 @@
[
{
"id": "2ebdd51e.c5d17a",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert array of JavaScript objects to CSV with column name header",
"info": "CSV node can convert an array of JavaScript objects to multi-line CSV text with column name header at first line.",
"x": 390,
"y": 1200,
"wires": []
},
{
"id": "2b4d538d.ada07c",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 1260,
"wires": [
[
"3e5c9e8.5065b62"
]
]
},
{
"id": "db02c7be.0984e8",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "all",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 1260,
"wires": [
[
"61f8b772.ddb1f8"
]
]
},
{
"id": "3e5c9e8.5065b62",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "[\n {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n },\n {\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n },\n {\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n }\n]",
"output": "json",
"x": 430,
"y": 1260,
"wires": [
[
"db02c7be.0984e8"
]
]
},
{
"id": "61f8b772.ddb1f8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 780,
"y": 1260,
"wires": []
}
]

View File

@ -0,0 +1,200 @@
[
{
"id": "1ae28939.9f5fc7",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Send column name when reset property set",
"info": "CSV node can send column names at first or `reset` property exists in input message.",
"x": 310,
"y": 1540,
"wires": []
},
{
"id": "c16ad95b.4f9ac8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "Apple",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 250,
"y": 1600,
"wires": [
[
"7f7bfc72.aed104"
]
]
},
{
"id": "870620b9.95343",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": false,
"hdrout": "once",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 650,
"y": 1720,
"wires": [
[
"d960de42.619c7"
]
]
},
{
"id": "7f7bfc72.aed104",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "json",
"x": 470,
"y": 1600,
"wires": [
[
"870620b9.95343"
]
]
},
{
"id": "d960de42.619c7",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 830,
"y": 1720,
"wires": []
},
{
"id": "6f8296e.f95ca68",
"type": "inject",
"z": "4b63452d.672afc",
"name": "Orange",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 250,
"y": 1660,
"wires": [
[
"c37d0dfa.ec1ab"
]
]
},
{
"id": "c37d0dfa.ec1ab",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Orange\",\n \"price\": 120,\n \"origin\": \"USA\"\n}\n",
"output": "json",
"x": 470,
"y": 1660,
"wires": [
[
"870620b9.95343"
]
]
},
{
"id": "35209fe2.16926",
"type": "inject",
"z": "4b63452d.672afc",
"name": "Banana & reset",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
},
{
"p": "reset",
"v": "",
"vt": "date"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 280,
"y": 1720,
"wires": [
[
"afd4e6b3.624a28"
]
]
},
{
"id": "afd4e6b3.624a28",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Banana\",\n \"price\": 80,\n \"origin\": \"Philippines\"\n}",
"output": "json",
"x": 470,
"y": 1720,
"wires": [
[
"870620b9.95343"
]
]
}
]

View File

@ -0,0 +1,150 @@
[
{
"id": "195c168c.44f149",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 1900,
"wires": [
[
"b270564c.171908"
]
]
},
{
"id": "8ec8cf9e.103fa",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": true,
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 600,
"y": 1900,
"wires": [
[
"5c5254a8.bc562c"
]
]
},
{
"id": "b270564c.171908",
"type": "template",
"z": "4b63452d.672afc",
"name": "CSV data",
"field": "payload",
"fieldType": "msg",
"format": "text",
"syntax": "mustache",
"template": "kind,price,origin\nApple,100,Canada\nOrange,120,USA\nBanana,80,Philippines",
"output": "str",
"x": 430,
"y": 1900,
"wires": [
[
"8ec8cf9e.103fa"
]
]
},
{
"id": "1c7be442.6a4bdc",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1090,
"y": 1900,
"wires": []
},
{
"id": "d3da7cfb.cf596",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Join parsed CSV message sequence using join node",
"info": "Parset CSV message sequence can be joined by join node.",
"x": 330,
"y": 1840,
"wires": []
},
{
"id": "a07c9e26.c84fd",
"type": "csv",
"z": "4b63452d.672afc",
"name": "",
"sep": ",",
"hdrin": "",
"hdrout": "none",
"multi": "one",
"ret": "\\n",
"temp": "kind,price",
"skip": "0",
"strings": true,
"include_empty_strings": "",
"include_null_values": "",
"x": 910,
"y": 1900,
"wires": [
[
"1c7be442.6a4bdc"
]
]
},
{
"id": "5c5254a8.bc562c",
"type": "join",
"z": "4b63452d.672afc",
"name": "",
"mode": "auto",
"build": "string",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": false,
"timeout": "",
"count": "",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 750,
"y": 1900,
"wires": [
[
"a07c9e26.c84fd"
]
]
}
]

View File

@ -0,0 +1,94 @@
[
{
"id": "8c5224a6.201b88",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 180,
"wires": [
[
"d6c67e51.0d709"
]
]
},
{
"id": "d6c67e51.0d709",
"type": "template",
"z": "4b63452d.672afc",
"name": "HTML text",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "<html>\n <head>\n <title>List of Fruits</title>\n </head>\n <body>\n <ul>\n <li class=\"Item\">Apple</li>\n <li class=\"Item\">Orange</li>\n <li class=\"Item\">Banana</li>\n </ul>\n </body>\n</html>\n",
"output": "str",
"x": 390,
"y": 180,
"wires": [
[
"599a1155.61a5c"
]
]
},
{
"id": "b0d5cd89.338df",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Extract array of HTML element by CSS selector",
"info": "HTML node can be used to extract elements in HTML document as an array using CSS selector.",
"x": 280,
"y": 120,
"wires": []
},
{
"id": "599a1155.61a5c",
"type": "html",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"outproperty": "payload",
"tag": ".Item",
"ret": "html",
"as": "single",
"x": 550,
"y": 180,
"wires": [
[
"942b23d1.cce09"
]
]
},
{
"id": "942b23d1.cce09",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 710,
"y": 180,
"wires": []
}
]

View File

@ -0,0 +1,94 @@
[
{
"id": "a44973e8.6319b",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 360,
"wires": [
[
"de1b012e.96ec3"
]
]
},
{
"id": "de1b012e.96ec3",
"type": "template",
"z": "4b63452d.672afc",
"name": "HTML text",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "<html>\n <head>\n <title>List of Fruits</title>\n </head>\n <body>\n <ul>\n <li class=\"Item\">Apple</li>\n <li class=\"Item\">Orange</li>\n <li class=\"Item\">Banana</li>\n </ul>\n </body>\n</html>\n",
"output": "str",
"x": 390,
"y": 360,
"wires": [
[
"cee70712.6f3538"
]
]
},
{
"id": "99e32bc7.c8e508",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Extract sequence of HTML element by CSS selector",
"info": "HTML node can be used to extract elements in HTML document as a messege sequence using CSS selector.",
"x": 290,
"y": 300,
"wires": []
},
{
"id": "cee70712.6f3538",
"type": "html",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"outproperty": "payload",
"tag": ".Item",
"ret": "html",
"as": "multi",
"x": 550,
"y": 360,
"wires": [
[
"17f25482.d4b56b"
]
]
},
{
"id": "17f25482.d4b56b",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 710,
"y": 360,
"wires": []
}
]

View File

@ -0,0 +1,121 @@
[
{
"id": "653ce9aa.b6a1c8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 560,
"wires": [
[
"52a16f7f.447d8"
]
]
},
{
"id": "52a16f7f.447d8",
"type": "template",
"z": "4b63452d.672afc",
"name": "HTML text",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "<html>\n <head>\n <title>List of Fruits</title>\n </head>\n <body>\n <ul>\n <li class=\"Item\">Apple</li>\n <li class=\"Item\">Orange</li>\n <li class=\"Item\">Banana</li>\n </ul>\n </body>\n</html>\n",
"output": "str",
"x": 390,
"y": 560,
"wires": [
[
"a52319c3.89b008"
]
]
},
{
"id": "8bc35379.31d99",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Extract array of HTML element by CSS selector specified in message",
"info": "CSS selector for HTML node can be specified by `select` property of input message.",
"x": 350,
"y": 500,
"wires": []
},
{
"id": "9c49de8a.bad25",
"type": "html",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"outproperty": "payload",
"tag": "",
"ret": "html",
"as": "single",
"x": 730,
"y": 560,
"wires": [
[
"d4f4b987.278a68"
]
]
},
{
"id": "d4f4b987.278a68",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 890,
"y": 560,
"wires": []
},
{
"id": "a52319c3.89b008",
"type": "change",
"z": "4b63452d.672afc",
"name": "",
"rules": [
{
"t": "set",
"p": "select",
"pt": "msg",
"to": ".Item",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 560,
"y": 560,
"wires": [
[
"9c49de8a.bad25"
]
]
}
]

View File

@ -0,0 +1,122 @@
[
{
"id": "66cff4ee.f2761c",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 760,
"wires": [
[
"2baaf6bf.0a02ca"
]
]
},
{
"id": "2baaf6bf.0a02ca",
"type": "template",
"z": "4b63452d.672afc",
"name": "HTML text",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "<html>\n <head>\n <title>List of Fruits</title>\n </head>\n <body>\n <ul>\n <li class=\"Item\">Apple</li>\n <li class=\"Item\">Orange</li>\n <li class=\"Item\">Banana</li>\n </ul>\n </body>\n</html>\n",
"output": "str",
"x": 390,
"y": 760,
"wires": [
[
"bbb22e6b.0fa25"
]
]
},
{
"id": "a57d35d0.8aa538",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Join extracted sequence of HTML element using join node",
"info": "Message sequence extracted by HTML node can be combined using join node.",
"x": 310,
"y": 700,
"wires": []
},
{
"id": "bbb22e6b.0fa25",
"type": "html",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"outproperty": "payload",
"tag": ".Item",
"ret": "html",
"as": "multi",
"x": 550,
"y": 760,
"wires": [
[
"bd01ca4.966ad38"
]
]
},
{
"id": "4d2616a8.84de88",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 850,
"y": 760,
"wires": []
},
{
"id": "bd01ca4.966ad38",
"type": "join",
"z": "4b63452d.672afc",
"name": "",
"mode": "custom",
"build": "string",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": ",",
"joinerType": "str",
"accumulate": false,
"timeout": "",
"count": "",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 690,
"y": 760,
"wires": [
[
"4d2616a8.84de88"
]
]
}
]

View File

@ -0,0 +1,92 @@
[
{
"id": "9976e95d.2f8398",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"y": 180,
"wires": [
[
"d94fc083.49d87"
]
]
},
{
"id": "6684abb1.8eb454",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert JSON string to JS object",
"info": "JSON node can convert JSON string to JavaScript object.",
"x": 250,
"y": 120,
"wires": []
},
{
"id": "d94fc083.49d87",
"type": "template",
"z": "4b63452d.672afc",
"name": "JSON string",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "str",
"x": 410,
"y": 180,
"wires": [
[
"1a3dc54a.78598b"
]
]
},
{
"id": "8950a55d.023988",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 730,
"y": 180,
"wires": []
},
{
"id": "1a3dc54a.78598b",
"type": "json",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 570,
"y": 180,
"wires": [
[
"8950a55d.023988"
]
]
}
]

View File

@ -0,0 +1,92 @@
[
{
"id": "cb13761f.56c328",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 240,
"y": 380,
"wires": [
[
"c607642a.78c3c8"
]
]
},
{
"id": "180b1e22.0074e2",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert JS object to JSON string",
"info": "JSON node can convert JavaScript object to JSON string.",
"x": 250,
"y": 320,
"wires": []
},
{
"id": "c607642a.78c3c8",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "json",
"x": 400,
"y": 380,
"wires": [
[
"bf309844.fa12e8"
]
]
},
{
"id": "5b6b130b.72a14c",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 730,
"y": 380,
"wires": []
},
{
"id": "bf309844.fa12e8",
"type": "json",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 570,
"y": 380,
"wires": [
[
"5b6b130b.72a14c"
]
]
}
]

View File

@ -0,0 +1,160 @@
[
{
"id": "2b18621b.e2670e",
"type": "inject",
"z": "4b63452d.672afc",
"name": "OK",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 230,
"y": 580,
"wires": [
[
"5986faee.aef954"
]
]
},
{
"id": "59acf99.9a92308",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Validate input JSON string",
"info": "JSON node can validate input JSON string using [JSON schema](https://json-schema.org/) when converting to JavaScript object.",
"x": 230,
"y": 520,
"wires": []
},
{
"id": "5986faee.aef954",
"type": "template",
"z": "4b63452d.672afc",
"name": "JSON string",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "str",
"x": 410,
"y": 580,
"wires": [
[
"f8a67c6d.4f1f1"
]
]
},
{
"id": "ca27c92c.ad7cb8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 910,
"y": 580,
"wires": []
},
{
"id": "2fad9978.ea1916",
"type": "json",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"action": "",
"pretty": false,
"x": 750,
"y": 580,
"wires": [
[
"ca27c92c.ad7cb8"
]
]
},
{
"id": "f8a67c6d.4f1f1",
"type": "template",
"z": "4b63452d.672afc",
"name": "Schema",
"field": "schema",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"type\": \"object\",\n \"properties\": {\n \"kind\": {\n \"type\": \"string\"\n },\n \"price\": {\n \"type\": \"number\"\n },\n \"origin\": {\n \"type\": \"string\"\n }\n }\n}",
"output": "json",
"x": 590,
"y": 580,
"wires": [
[
"2fad9978.ea1916"
]
]
},
{
"id": "8337e847.ac18d8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "NG",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 230,
"y": 660,
"wires": [
[
"fa14d8bf.1ac938"
]
]
},
{
"id": "fa14d8bf.1ac938",
"type": "template",
"z": "4b63452d.672afc",
"name": "JSON string",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": \"100\",\n \"origin\": \"Canada\"\n}",
"output": "str",
"x": 410,
"y": 660,
"wires": [
[
"f8a67c6d.4f1f1"
]
]
}
]

View File

@ -0,0 +1,92 @@
[
{
"id": "82f1bd0b.43474",
"type": "xml",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"attr": "",
"chr": "",
"x": 530,
"y": 180,
"wires": [
[
"1cd4ad02.9a5423"
]
]
},
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 180,
"wires": [
[
"cdd1c154.3a655"
]
]
},
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert JavaScript object to XML",
"info": "XML node can convert JavaScript object to XML string.",
"x": 240,
"y": 120,
"wires": []
},
{
"id": "1cd4ad02.9a5423",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 690,
"y": 180,
"wires": []
},
{
"id": "cdd1c154.3a655",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n}",
"output": "json",
"x": 360,
"y": 180,
"wires": [
[
"82f1bd0b.43474"
]
]
}
]

View File

@ -0,0 +1,92 @@
[
{
"id": "93e423a9.a407d",
"type": "xml",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"attr": "",
"chr": "",
"x": 530,
"y": 360,
"wires": [
[
"2d0dde7e.a50082"
]
]
},
{
"id": "ba1dab90.8d1da8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 360,
"wires": [
[
"16617f26.14ced1"
]
]
},
{
"id": "a9f97b00.57d658",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert XML to JavaScript object",
"info": "XML node can convert XML string to JavaScript object.",
"x": 240,
"y": 300,
"wires": []
},
{
"id": "2d0dde7e.a50082",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 690,
"y": 360,
"wires": []
},
{
"id": "16617f26.14ced1",
"type": "template",
"z": "4b63452d.672afc",
"name": "XML string",
"field": "payload",
"fieldType": "msg",
"format": "html",
"syntax": "plain",
"template": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<fruit id=\"100\">\n <kind>Apple</kind>\n <price>100</price>\n <origin>Canada</origin>\n</fruit>",
"output": "str",
"x": 370,
"y": 360,
"wires": [
[
"93e423a9.a407d"
]
]
}
]

View File

@ -0,0 +1,119 @@
[
{
"id": "581bd648.636628",
"type": "xml",
"z": "4b63452d.672afc",
"name": "",
"property": "payload",
"attr": "",
"chr": "",
"x": 710,
"y": 540,
"wires": [
[
"b74237dc.1e5028"
]
]
},
{
"id": "d0899f9b.f1ac6",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 540,
"wires": [
[
"f04ffb9a.68edb8"
]
]
},
{
"id": "8a214c05.dc61f",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Control conversion using options property",
"info": "XML node can control conversion by setting `options` property (defined by [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js/blob/master/README.md#options)) in input message.",
"x": 260,
"y": 480,
"wires": []
},
{
"id": "b74237dc.1e5028",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 870,
"y": 540,
"wires": []
},
{
"id": "f04ffb9a.68edb8",
"type": "template",
"z": "4b63452d.672afc",
"name": "XML string",
"field": "payload",
"fieldType": "msg",
"format": "html",
"syntax": "plain",
"template": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<fruit id=\"100\">\n <kind>Apple</kind>\n <price>100</price>\n <origin>Canada</origin>\n</fruit>",
"output": "str",
"x": 370,
"y": 540,
"wires": [
[
"fedf79.5889c088"
]
]
},
{
"id": "fedf79.5889c088",
"type": "change",
"z": "4b63452d.672afc",
"name": "set options",
"rules": [
{
"t": "set",
"p": "options",
"pt": "msg",
"to": "{\"explicitArray\":false}",
"tot": "json"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 550,
"y": 540,
"wires": [
[
"581bd648.636628"
]
]
}
]

View File

@ -0,0 +1,90 @@
[
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 180,
"wires": [
[
"cdd1c154.3a655"
]
]
},
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert JavaScript object to YAML",
"info": "YAML node can convert JavaScript object to YAML string.",
"x": 240,
"y": 120,
"wires": []
},
{
"id": "1cd4ad02.9a5423",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 670,
"y": 180,
"wires": []
},
{
"id": "cdd1c154.3a655",
"type": "template",
"z": "4b63452d.672afc",
"name": "JS object",
"field": "payload",
"fieldType": "msg",
"format": "json",
"syntax": "plain",
"template": "{\n \"fruits\" : {\n \"kind\": \"Apple\",\n \"price\": 100,\n \"origin\": \"Canada\"\n }\n}",
"output": "json",
"x": 360,
"y": 180,
"wires": [
[
"aaf0100b.16628"
]
]
},
{
"id": "aaf0100b.16628",
"type": "yaml",
"z": "4b63452d.672afc",
"property": "payload",
"name": "",
"x": 510,
"y": 180,
"wires": [
[
"1cd4ad02.9a5423"
]
]
}
]

View File

@ -0,0 +1,90 @@
[
{
"id": "ba1dab90.8d1da8",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 200,
"y": 360,
"wires": [
[
"16617f26.14ced1"
]
]
},
{
"id": "a9f97b00.57d658",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Convert YAML to JavaScript object",
"info": "YAML node can convert YAML string to JavaScript object.",
"x": 240,
"y": 300,
"wires": []
},
{
"id": "2d0dde7e.a50082",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 690,
"y": 360,
"wires": []
},
{
"id": "16617f26.14ced1",
"type": "template",
"z": "4b63452d.672afc",
"name": "YAML string",
"field": "payload",
"fieldType": "msg",
"format": "yaml",
"syntax": "plain",
"template": "fruits:\n kind: Apple\n price: 100\n origin: Canada",
"output": "str",
"x": 370,
"y": 360,
"wires": [
[
"e2e4f862.f9d7d8"
]
]
},
{
"id": "e2e4f862.f9d7d8",
"type": "yaml",
"z": "4b63452d.672afc",
"property": "payload",
"name": "",
"x": 530,
"y": 360,
"wires": [
[
"2d0dde7e.a50082"
]
]
}
]

View File

@ -0,0 +1,113 @@
[
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "194a3e4f.a92772",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 220,
"wires": [
[
"b4b9f603.739598"
]
]
},
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "Write string to a file, then read from the file",
"info": "File-in node can read string from a file.",
"x": 260,
"y": 140,
"wires": []
},
{
"id": "b4b9f603.739598",
"type": "file",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 420,
"y": 220,
"wires": [
[
"6dc01cac.5c4bf4"
]
]
},
{
"id": "2587adb9.7e60f2",
"type": "debug",
"z": "194a3e4f.a92772",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 220,
"wires": []
},
{
"id": "6dc01cac.5c4bf4",
"type": "file in",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 220,
"wires": [
[
"2587adb9.7e60f2"
]
]
},
{
"id": "f4b4309a.3b78a",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↑read result from file",
"info": "",
"x": 630,
"y": 260,
"wires": []
},
{
"id": "672d3693.3cabd8",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 440,
"y": 180,
"wires": []
}
]

View File

@ -0,0 +1,113 @@
[
{
"id": "8997398f.c5d628",
"type": "inject",
"z": "194a3e4f.a92772",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "😀",
"payloadType": "str",
"x": 210,
"y": 480,
"wires": [
[
"56e32d23.050f44"
]
]
},
{
"id": "4e598e65.1799d",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "Read data in specified encoding",
"info": "File-in node can specify encoding of data read from a file.",
"x": 230,
"y": 400,
"wires": []
},
{
"id": "56e32d23.050f44",
"type": "file",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 380,
"y": 480,
"wires": [
[
"38fa0579.f2cd8a"
]
]
},
{
"id": "d28c8994.99c0a8",
"type": "debug",
"z": "194a3e4f.a92772",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 770,
"y": 480,
"wires": []
},
{
"id": "38fa0579.f2cd8a",
"type": "file in",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "base64",
"x": 580,
"y": 480,
"wires": [
[
"d28c8994.99c0a8"
]
]
},
{
"id": "fa22ca20.ae4528",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↑read data from file as base64 string",
"info": "",
"x": 640,
"y": 520,
"wires": []
},
{
"id": "148e25ad.98891a",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 400,
"y": 440,
"wires": []
}
]

View File

@ -0,0 +1,132 @@
[
{
"id": "6a0b1d03.d4cee4",
"type": "inject",
"z": "194a3e4f.a92772",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 740,
"wires": [
[
"d4b00cb7.a5a23"
]
]
},
{
"id": "f17ea1d1.8ecc3",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "Read data breaking lines into individual messages",
"info": "File-in node can break read text into messages with individual lines",
"x": 290,
"y": 660,
"wires": []
},
{
"id": "99ae7806.1d6428",
"type": "file",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 540,
"y": 740,
"wires": [
[
"70d7892f.d27db8"
]
]
},
{
"id": "7ed8282c.92b338",
"type": "debug",
"z": "194a3e4f.a92772",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 800,
"wires": []
},
{
"id": "70d7892f.d27db8",
"type": "file in",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"format": "lines",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 800,
"wires": [
[
"7ed8282c.92b338"
]
]
},
{
"id": "c1b7e05.1d94b2",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↑read data from file breaking lines into messages",
"info": "",
"x": 720,
"y": 840,
"wires": []
},
{
"id": "a5f647b2.cf27a8",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 560,
"y": 700,
"wires": []
},
{
"id": "d4b00cb7.a5a23",
"type": "template",
"z": "194a3e4f.a92772",
"name": "data",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "one\ntwo\nthree!",
"output": "str",
"x": 370,
"y": 740,
"wires": [
[
"99ae7806.1d6428"
]
]
}
]

View File

@ -0,0 +1,201 @@
[
{
"id": "bdd57acc.2edc48",
"type": "inject",
"z": "194a3e4f.a92772",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 1040,
"wires": [
[
"7a069b01.0c2324"
]
]
},
{
"id": "1fd12220.33953e",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "Creating a message stream from lines of data",
"info": "File-in node can break read text into messages with individual lines. The messages creates a stream of messages.",
"x": 270,
"y": 960,
"wires": []
},
{
"id": "ab6eb213.2a08d",
"type": "file",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 540,
"y": 1040,
"wires": [
[
"b7ed49b0.649fb8"
]
]
},
{
"id": "c48d8ae0.9ff3a8",
"type": "debug",
"z": "194a3e4f.a92772",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 1140,
"wires": []
},
{
"id": "b7ed49b0.649fb8",
"type": "file in",
"z": "194a3e4f.a92772",
"name": "",
"filename": "/tmp/hello.txt",
"format": "lines",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 280,
"y": 1140,
"wires": [
[
"83073ebe.fcce4"
]
]
},
{
"id": "3c33e69f.6a04ba",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↑read data from file breaking lines into messages",
"info": "",
"x": 380,
"y": 1180,
"wires": []
},
{
"id": "3598bf7d.5712a",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 560,
"y": 1000,
"wires": []
},
{
"id": "7a069b01.0c2324",
"type": "template",
"z": "194a3e4f.a92772",
"name": "data",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "plain",
"template": "Apple\nBanana\nGrape\nOrange",
"output": "str",
"x": 370,
"y": 1040,
"wires": [
[
"ab6eb213.2a08d"
]
]
},
{
"id": "8d4ed1d0.821fe",
"type": "join",
"z": "194a3e4f.a92772",
"name": "",
"mode": "auto",
"build": "string",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": "false",
"timeout": "",
"count": "",
"reduceRight": false,
"x": 630,
"y": 1140,
"wires": [
[
"c48d8ae0.9ff3a8"
]
]
},
{
"id": "83073ebe.fcce4",
"type": "switch",
"z": "194a3e4f.a92772",
"name": "< D",
"property": "payload",
"propertyType": "msg",
"rules": [
{
"t": "lt",
"v": "D",
"vt": "str"
}
],
"checkall": "true",
"repair": true,
"outputs": 1,
"x": 470,
"y": 1140,
"wires": [
[
"8d4ed1d0.821fe"
]
]
},
{
"id": "2088e195.f7aebe",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↓filter data before \"D\"",
"info": "",
"x": 520,
"y": 1100,
"wires": []
},
{
"id": "b848cdc7.61e06",
"type": "comment",
"z": "194a3e4f.a92772",
"name": "↑join to single string",
"info": "",
"x": 670,
"y": 1180,
"wires": []
}
]

View File

@ -0,0 +1,113 @@
[
{
"id": "84222b92.d65d18",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 200,
"wires": [
[
"b4b9f603.739598"
]
]
},
{
"id": "7b014430.dfd94c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Write string to a file, then read from the file",
"info": "File node can write string to a file.",
"x": 260,
"y": 120,
"wires": []
},
{
"id": "b4b9f603.739598",
"type": "file",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 420,
"y": 200,
"wires": [
[
"6dc01cac.5c4bf4"
]
]
},
{
"id": "2587adb9.7e60f2",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 810,
"y": 200,
"wires": []
},
{
"id": "6dc01cac.5c4bf4",
"type": "file in",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 620,
"y": 200,
"wires": [
[
"2587adb9.7e60f2"
]
]
},
{
"id": "f4b4309a.3b78a",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↑read result from file",
"info": "",
"x": 630,
"y": 240,
"wires": []
},
{
"id": "672d3693.3cabd8",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↓write to /tmp/hello.txt",
"info": "",
"x": 440,
"y": 160,
"wires": []
}
]

View File

@ -0,0 +1,118 @@
[
{
"id": "704479e1.399388",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "filename",
"v": "/tmp/hello.txt",
"vt": "str"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Hello, World!",
"payloadType": "str",
"x": 230,
"y": 400,
"wires": [
[
"402f3b7e.988014"
]
]
},
{
"id": "8e876a75.e9beb8",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Write string to a file specied by filename property, the read from the file",
"info": "File node can target file using `filename` property.",
"x": 350,
"y": 320,
"wires": []
},
{
"id": "402f3b7e.988014",
"type": "file",
"z": "4b63452d.672afc",
"name": "",
"filename": "",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 390,
"y": 400,
"wires": [
[
"26e077d6.bbcd98"
]
]
},
{
"id": "97b6b6b2.a54b38",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 770,
"y": 400,
"wires": []
},
{
"id": "26e077d6.bbcd98",
"type": "file in",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 580,
"y": 400,
"wires": [
[
"97b6b6b2.a54b38"
]
]
},
{
"id": "85062297.da79",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↑read result from file",
"info": "",
"x": 590,
"y": 440,
"wires": []
},
{
"id": "7316c4fc.b1dcdc",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↓write to file specified by filename property",
"info": "",
"x": 500,
"y": 360,
"wires": []
}
]

View File

@ -0,0 +1,85 @@
[
{
"id": "4ac00fb0.d5f52",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 220,
"y": 600,
"wires": [
[
"542cc2f4.92857c"
]
]
},
{
"id": "671f8295.0e6f6c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Delete a file",
"info": "File node can delete a file.",
"x": 170,
"y": 540,
"wires": []
},
{
"id": "542cc2f4.92857c",
"type": "file",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "delete",
"encoding": "none",
"x": 420,
"y": 600,
"wires": [
[
"a24da523.5babe8"
]
]
},
{
"id": "a24da523.5babe8",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 630,
"y": 600,
"wires": []
},
{
"id": "51157051.2f62",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↓delete a file",
"info": "",
"x": 390,
"y": 560,
"wires": []
}
]

View File

@ -0,0 +1,113 @@
[
{
"id": "e4ef1f5e.7cd82",
"type": "inject",
"z": "4b63452d.672afc",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "8J+YgA==",
"payloadType": "str",
"x": 220,
"y": 820,
"wires": [
[
"72b37cc8.177054"
]
]
},
{
"id": "f5997af4.5a9298",
"type": "comment",
"z": "4b63452d.672afc",
"name": "Specify encoding of written data",
"info": "File node can specify encoding of data.",
"x": 230,
"y": 740,
"wires": []
},
{
"id": "72b37cc8.177054",
"type": "file",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "base64",
"x": 400,
"y": 820,
"wires": [
[
"2da33ec.f45cac2"
]
]
},
{
"id": "2e814354.278c8c",
"type": "debug",
"z": "4b63452d.672afc",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 790,
"y": 820,
"wires": []
},
{
"id": "2da33ec.f45cac2",
"type": "file in",
"z": "4b63452d.672afc",
"name": "",
"filename": "/tmp/hello.txt",
"format": "utf8",
"chunk": false,
"sendError": false,
"encoding": "none",
"x": 600,
"y": 820,
"wires": [
[
"2e814354.278c8c"
]
]
},
{
"id": "ec754c99.84bfd",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↓write string with base64 encoding",
"info": "",
"x": 460,
"y": 780,
"wires": []
},
{
"id": "3e6704ff.4ce25c",
"type": "comment",
"z": "4b63452d.672afc",
"name": "↑read result from file",
"info": "",
"x": 610,
"y": 860,
"wires": []
}
]

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