Merge branch 'dev' into tab-context-menu

This commit is contained in:
Nick O'Leary 2023-02-02 11:33:06 +00:00
commit 4624e28675
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
172 changed files with 1938 additions and 454 deletions

View File

@ -5,6 +5,9 @@ on:
release: release:
types: [published] types: [published]
permissions:
contents: read
jobs: jobs:
generate: generate:
name: 'Update node-red-docker image' name: 'Update node-red-docker image'

View File

@ -6,8 +6,14 @@ on:
pull_request: pull_request:
branches: [ master, dev ] branches: [ master, dev ]
permissions:
contents: read
jobs: jobs:
build: build:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:

View File

@ -1,3 +1,127 @@
#### 3.1.0-beta.1: Beta Release
Editor
- NEW: Locking Flows (#3938) @knolleary
- NEW: Improve UX around hiding flows via context menu (#3930) @knolleary
- NEW: Add support for inline image in markdown editor by drag and drop of an image file (#4006) @HiroyasuNishiyama
- NEW: Add support for mermaid diagram to markdown editor (#4007) @HiroyasuNishiyama
- NEW: Support uri fragments for nodes and groups including edit support (#3870) @knolleary
- Remember compact/pretty flow export user choice (#3974) @Steve-Mcl
- fix .red-ui-notification class (#4035) @xiaobinqt
- Fix border radius on Modules list header (#4038) @bonanitech
- fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama
- Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama
- Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama
- fix hide subflow tooltip (#4033) @HiroyasuNishiyama
- Fix disabled menu items in project feature (#4027) @kazuhitoyokoi
- Let themes change radialMenu text colors (#3995) @bonanitech
- Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi
- Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi
- Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi
- Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi
- Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary
- Handle replacing unknown node inside group or subflow (#3921) @knolleary
- Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo
- i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama
- add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama
- Fix autocomplete entry for responseUrl (#3884) @knolleary
- Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama
- Fix search type with spaces (#3841) @Steve-Mcl
- Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama
- Add button type to the adding SSH key button (#3866) @kazuhitoyokoi
- Check radio button as default in project dialog (#3879) @kazuhitoyokoi
- Add $clone as supported function (#3874) @HiroyasuNishiyama
- Env var jsonata (#3807) @HiroyasuNishiyama
- Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi
Runtime
- NEW: Add global environment variable feature (#3941) @HiroyasuNishiyama
- Force IPv4 name resolution to have priority (#4019) @dceejay
- Fix async loading of modules containing both nodes and plugins (#3999) @knolleary
- Use main branch as default in project feature (#4036) @kazuhitoyokoi
- Rename package var to avoid strict mode error (#4020) @knolleary
- Fix typos in settings.js (#4013) @ypid
- Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary
- Ignore commit error in project feature (#3987) @kazuhitoyokoi
- Update dependencies (#3969) @knolleary
- Add check that node sends object rather than primitive type (#3909) @knolleary
- Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary
- Fix nodesDir scan when node package has js/html in sub dir to package.json (#3867) @Steve-Mcl
- Fix file permissions (#3917) @kazuhitoyokoi
- ci: add minimum GitHub token permissions for workflows (#3907) @boahc077
Nodes
- Catch: fix typo in catch.html (#3965) @we11adam
- Change: Fix change node overwriting msg with itself (#3899) @dceejay
- Comment node: Clarify where the text will appear (#4004) @dirkjanfaber
- CSV: change replace to replaceAll (#3990) @dceejay
- CSV node: check header properties for ' and " (#3920) @dceejay
- CSV: Fix for CSV undefined property (#3906) @dceejay
- Delay: let delay node handle both flush then reset (#3898) @dceejay
- Function: Limit number of ports in function node (#3886) @kazuhitoyokoi
- Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi
- Function: add function node monaco types util and promisify (#3868) @Steve-Mcl
- HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary
- HTTP Request: Support form-data arrays (#3991) @hardillb
- HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary
- HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb
- HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi
- HTTP Response: Ensure statusCode is a number (#3894) @hardillb
- Inject: Allow Inject node to work with async context stores (#4021) @knolleary
- Join/Batch: Add count to join and batch node labels (#4028) @dceejay
- MQTT: Fix birth topic handling in MQTT node (#3905) @Steve-Mcl
- MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi
- MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl
- MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl
- MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary
- MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl
- Range: Add drop mode to range node (#3935) @dceejay
- Remove done from describe (#3873) @HiroyasuNishiyama
- Split node: avoid duplicate done call for buffer split (#4000) @knolleary
- Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi
- TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay
- Template: Add information about environment variable to template node (#3882) @kazuhitoyokoi
- Trigger: Hide trigger node repeat send option if sending nothing (#4023) @dceejay
- Watch: fix watch node test on MacOS/ARM (#3942) @HiroyasuNishiyama
#### 3.0.2: Maintenance Release
Editor
- Fix workspace chart bottom property (#3812) @bonanitech
- Update german translation (#3802) @Dennis14e
- Support color reset to the default in subflow and group (#3801) @kazuhitoyokoi
- Allow generateNodeNames to handle names containing regex control chars (#3817) @knolleary
- Hide scrollbars until they're needed (#3808) @bonanitech
- Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary
- remove console.log (#3820) @Steve-Mcl
Runtime
- Register subflow module instance node with parent flow (#3818) @knolleary
Nodes
- HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb
#### 3.0.1: Maintenance Release
Editor
- Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl
- Sys info (diagnostics report) amendments (#3793) @Steve-Mcl
- Allow `mode` and `title` to be omitted in `options` argument for `createEditor` (#3791) @Steve-Mcl
- Fix focus issues (#3789) @Steve-Mcl
- Ensure all typedInput buttons have button type set (#3788) @knolleary
- Do not flag hasUsers=false nodes as unused in search (#3787) @knolleary
- Properly position quick-add dialog in all cases (#3786) @knolleary
- Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary
- Remove use of Object.hasOwn (#3784) @knolleary
#### 3.0.0: Milestone Release #### 3.0.0: Milestone Release
Editor Editor

View File

@ -151,6 +151,7 @@ module.exports = function(grunt) {
"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",
"packages/node_modules/@node-red/editor-client/src/js/validators.js", "packages/node_modules/@node-red/editor-client/src/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js", "packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js", "packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", "packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
@ -169,6 +170,7 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", "packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", "packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", "packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js", "packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
@ -224,7 +226,7 @@ module.exports = function(grunt) {
"node_modules/jsonata/jsonata-es5.min.js", "node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js", "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js", "packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js", "packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js"
], ],
// "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [ // "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
// // TODO: resolve relative resource paths in // // TODO: resolve relative resource paths in
@ -233,6 +235,9 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [ "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js", "node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js" "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
],
"packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js": [
"node_modules/mermaid/dist/mermaid.min.js"
] ]
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "node-red", "name": "node-red",
"version": "3.1.0-beta.0", "version": "3.1.0-beta.1",
"description": "Low-code programming for event-driven applications", "description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org", "homepage": "http://nodered.org",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -26,13 +26,13 @@
} }
], ],
"dependencies": { "dependencies": {
"acorn": "8.7.1", "acorn": "8.8.1",
"acorn-walk": "8.2.0", "acorn-walk": "8.2.0",
"ajv": "8.11.0", "ajv": "8.11.2",
"async-mutex": "0.3.2", "async-mutex": "0.4.0",
"basic-auth": "2.0.1", "basic-auth": "2.0.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.20.0", "body-parser": "1.20.1",
"cheerio": "1.0.0-rc.10", "cheerio": "1.0.0-rc.10",
"clone": "2.1.2", "clone": "2.1.2",
"content-type": "1.0.4", "content-type": "1.0.4",
@ -40,16 +40,16 @@
"cookie-parser": "1.4.6", "cookie-parser": "1.4.6",
"cors": "2.8.5", "cors": "2.8.5",
"cronosjs": "1.7.1", "cronosjs": "1.7.1",
"denque": "2.0.1", "denque": "2.1.0",
"express": "4.18.1", "express": "4.18.2",
"express-session": "1.17.3", "express-session": "1.17.3",
"form-data": "4.0.0", "form-data": "4.0.0",
"fs-extra": "10.1.0", "fs-extra": "10.1.0",
"got": "11.8.5", "got": "11.8.5",
"hash-sum": "2.0.0", "hash-sum": "2.0.0",
"hpagent": "1.0.0", "hpagent": "1.2.0",
"https-proxy-agent": "5.0.1", "https-proxy-agent": "5.0.1",
"i18next": "21.8.14", "i18next": "21.10.0",
"iconv-lite": "0.6.3", "iconv-lite": "0.6.3",
"is-utf8": "0.2.1", "is-utf8": "0.2.1",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
@ -60,7 +60,7 @@
"memorystore": "1.6.7", "memorystore": "1.6.7",
"mime": "3.0.0", "mime": "3.0.0",
"moment": "2.29.4", "moment": "2.29.4",
"moment-timezone": "0.5.34", "moment-timezone": "0.5.39",
"mqtt": "4.3.7", "mqtt": "4.3.7",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"mustache": "4.2.0", "mustache": "4.2.0",
@ -73,19 +73,19 @@
"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",
"raw-body": "2.5.1", "raw-body": "2.5.1",
"semver": "7.3.7", "semver": "7.3.8",
"tar": "6.1.11", "tar": "6.1.12",
"tough-cookie": "4.0.0", "tough-cookie": "4.1.2",
"uglify-js": "3.16.2", "uglify-js": "3.17.4",
"uuid": "8.3.2", "uuid": "8.3.2",
"ws": "7.5.6", "ws": "7.5.6",
"xml2js": "0.4.23" "xml2js": "0.4.23"
}, },
"optionalDependencies": { "optionalDependencies": {
"bcrypt": "5.0.1" "bcrypt": "5.1.0"
}, },
"devDependencies": { "devDependencies": {
"dompurify": "2.3.9", "dompurify": "2.4.1",
"grunt": "1.5.3", "grunt": "1.5.3",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
"grunt-cli": "~1.4.3", "grunt-cli": "~1.4.3",
@ -108,13 +108,14 @@
"i18next-http-backend": "1.4.1", "i18next-http-backend": "1.4.1",
"jquery-i18next": "1.2.1", "jquery-i18next": "1.2.1",
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
"marked": "4.0.18", "marked": "4.2.3",
"mermaid": "^9.3.0",
"minami": "1.2.3", "minami": "1.2.3",
"mocha": "9.2.2", "mocha": "9.2.2",
"node-red-node-test-helper": "^0.3.0", "node-red-node-test-helper": "^0.3.0",
"nodemon": "2.0.19", "nodemon": "2.0.20",
"proxy": "^1.0.2", "proxy": "^1.0.2",
"sass": "1.53.0", "sass": "1.56.1",
"should": "13.2.3", "should": "13.2.3",
"sinon": "11.1.2", "sinon": "11.1.2",
"stoppable": "^1.1.0", "stoppable": "^1.1.0",

View File

@ -327,9 +327,8 @@ module.exports = {
themeContext.header.url = themePlugin.header.url || themeContext.header.url themeContext.header.url = themePlugin.header.url || themeContext.header.url
} }
} }
if(theme.codeEditor) { theme.codeEditor = theme.codeEditor || {}
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options); theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
}
} }
activeThemeInitialised = true; activeThemeInitialised = true;
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@node-red/editor-api", "name": "@node-red/editor-api",
"version": "3.1.0-beta.0", "version": "3.1.0-beta.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@ -16,14 +16,14 @@
} }
], ],
"dependencies": { "dependencies": {
"@node-red/util": "3.1.0-beta.0", "@node-red/util": "3.1.0-beta.1",
"@node-red/editor-client": "3.1.0-beta.0", "@node-red/editor-client": "3.1.0-beta.1",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.20.0", "body-parser": "1.20.1",
"clone": "2.1.2", "clone": "2.1.2",
"cors": "2.8.5", "cors": "2.8.5",
"express-session": "1.17.3", "express-session": "1.17.3",
"express": "4.18.1", "express": "4.18.2",
"memorystore": "1.6.7", "memorystore": "1.6.7",
"mime": "3.0.0", "mime": "3.0.0",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
@ -35,6 +35,6 @@
"ws": "7.5.6" "ws": "7.5.6"
}, },
"optionalDependencies": { "optionalDependencies": {
"bcrypt": "5.0.1" "bcrypt": "5.1.0"
} }
} }

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

@ -105,7 +105,7 @@
"search": "Flows durchsuchen", "search": "Flows durchsuchen",
"searchInput": "Flows durchsuchen", "searchInput": "Flows durchsuchen",
"subflows": "Subflow", "subflows": "Subflow",
"createSubflow": "Subflow", "createSubflow": "Hinzufügen",
"selectionToSubflow": "Auswahl in Subflow umwandeln", "selectionToSubflow": "Auswahl in Subflow umwandeln",
"flows": "Flow", "flows": "Flow",
"add": "Hinzufügen", "add": "Hinzufügen",
@ -152,7 +152,8 @@
"zoom-in": "Vergrößern", "zoom-in": "Vergrößern",
"search-flows": "Flows durchsuchen", "search-flows": "Flows durchsuchen",
"search-prev": "Vorherige", "search-prev": "Vorherige",
"search-next": "Nächste" "search-next": "Nächste",
"search-counter": "\"__term__\" __result__ von __count__"
}, },
"user": { "user": {
"loggedInAs": "Angemeldet als __name__", "loggedInAs": "Angemeldet als __name__",
@ -168,7 +169,11 @@
} }
}, },
"notification": { "notification": {
"warning": "<strong>Warnung:</strong> __message__", "state": {
"flowsStopped": "Flows gestoppt",
"flowsStarted": "Flows gestartet"
},
"warning": "<strong>Warnung</strong>: __message__",
"warnings": { "warnings": {
"undeployedChanges": "Node hat nicht übernommene (deploy) Änderungen", "undeployedChanges": "Node hat nicht übernommene (deploy) Änderungen",
"nodeActionDisabled": "Node-Aktionen deaktiviert", "nodeActionDisabled": "Node-Aktionen deaktiviert",
@ -177,15 +182,15 @@
"missing-modules": "<p>Flows angehalten aufgrund fehlender Module</p>", "missing-modules": "<p>Flows angehalten aufgrund fehlender Module</p>",
"safe-mode": "<p>Flows sind im abgesicherten Modus gestoppt.</p><p>Flows können bearbeitet und übernommen (deploy) werden, um sie neu zu starten.</p>", "safe-mode": "<p>Flows sind im abgesicherten Modus gestoppt.</p><p>Flows können bearbeitet und übernommen (deploy) werden, um sie neu zu starten.</p>",
"restartRequired": "Node-RED muss neu gestartet werden, damit die Module nach Upgrade aktiviert werden", "restartRequired": "Node-RED muss neu gestartet werden, damit die Module nach Upgrade aktiviert werden",
"credentials_load_failed": "<p>Flows gestoppt, da die Berechtigungen nicht entschlüsselt werden konnten.</p><p>Die Datei mit dem Flow-Berechtigungen ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.</p>", "credentials_load_failed": "<p>Flows gestoppt, da die Credentials nicht entschlüsselt werden konnten.</p><p>Die Datei mit den Flow-Credentials ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.</p>",
"credentials_load_failed_reset": "<p>Die Berechtigungen konnten nicht entschlüsselt werden.</p><p>Die Datei mit den Flow-Berechtigungen ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.</p><p>Die Datei mit den Flow-Berechtigungen wird bei der nächsten Übernahme (deploy) zurückgesetzt. Alle vorhandenen Flow-Berechtigungen werden gelöscht.</p>", "credentials_load_failed_reset": "<p>Die Credentials konnten nicht entschlüsselt werden.</p><p>Die Datei mit den Flow-Credentials ist verschlüsselt, aber der Schlüssel des Projekts fehlt oder ist ungültig.</p><p>Die Datei mit den Flow-Credentials wird bei der nächsten Übernahme (deploy) zurückgesetzt. Alle vorhandenen Flow-Credentials werden gelöscht.</p>",
"missing_flow_file": "<p>Die Flow-Datei des Projekts wurde nicht gefunden.</p><p>Das Projekt ist nicht mit einer Flow-Datei konfiguriert.</p>", "missing_flow_file": "<p>Die Flow-Datei des Projekts wurde nicht gefunden.</p><p>Das Projekt ist nicht mit einer Flow-Datei konfiguriert.</p>",
"missing_package_file": "<p>Die Paket-Datei des Projekts wurde nicht gefunden.</p><p>In dem Projekt fehlt die 'package.json'-Datei.</p>", "missing_package_file": "<p>Die Paket-Datei des Projekts wurde nicht gefunden.</p><p>In dem Projekt fehlt die 'package.json'-Datei.</p>",
"project_empty": "<p>Das Projekt ist leer.</p><p>Soll ein Standardsatz an Projektdateien erstellen werden?<br/>Andernfalls müssen die Dateien manuell außerhalb des Editors dem Projekt hinzugefügt werden.</p>", "project_empty": "<p>Das Projekt ist leer.</p><p>Soll ein Standardsatz an Projektdateien erstellen werden?<br/>Andernfalls müssen die Dateien manuell außerhalb des Editors dem Projekt hinzugefügt werden.</p>",
"project_not_found": "<p>Das Projekt '__project__' wurde nicht gefunden.</p>", "project_not_found": "<p>Das Projekt '__project__' wurde nicht gefunden.</p>",
"git_merge_conflict": "<p>Der automatische Merge der Änderungen ist fehlgeschlagen.</p><p>Die Merge-Konflikte müssen behoben und die Ergebnisse ins Repository übertragen werden (commit).</p>" "git_merge_conflict": "<p>Der automatische Merge der Änderungen ist fehlgeschlagen.</p><p>Die Merge-Konflikte müssen behoben und die Ergebnisse ins Repository übertragen werden (commit).</p>"
}, },
"error": "<strong>Fehler:</strong> __message__", "error": "<strong>Fehler</strong>: __message__",
"errors": { "errors": {
"lostConnection": "Verbindung zum Server verloren. Verbindung wird erneut hergestellt ...", "lostConnection": "Verbindung zum Server verloren. Verbindung wird erneut hergestellt ...",
"lostConnectionReconnect": "Verbindung zum Server verloren. Wiederherstellung der Verbindung in __time__s.", "lostConnectionReconnect": "Verbindung zum Server verloren. Wiederherstellung der Verbindung in __time__s.",
@ -203,7 +208,7 @@
"pull": "Projekt '__project__' erneut geladen", "pull": "Projekt '__project__' erneut geladen",
"revert": "Änderungen im Projekt '__project__' rückgängig gemacht", "revert": "Änderungen im Projekt '__project__' rückgängig gemacht",
"merge-complete": "Git-Merge abgeschlossen", "merge-complete": "Git-Merge abgeschlossen",
"setupCredentials": "Berechtigungen einrichten", "setupCredentials": "Credentials einrichten",
"setupProjectFiles": "Projektdateien einrichten", "setupProjectFiles": "Projektdateien einrichten",
"no": "Nein, Danke", "no": "Nein, Danke",
"createDefault": "Standardprojektdateien erstellen", "createDefault": "Standardprojektdateien erstellen",
@ -211,7 +216,7 @@
}, },
"label": { "label": {
"manage-project-dep": "Projektabhängigkeiten verwalten", "manage-project-dep": "Projektabhängigkeiten verwalten",
"setup-cred": "Berechtigungen einrichten", "setup-cred": "Credentials einrichten",
"setup-project": "Projektdateien einrichten", "setup-project": "Projektdateien einrichten",
"create-default-package": "Standardpaketdatei erstellen", "create-default-package": "Standardpaketdatei erstellen",
"no-thanks": "Nein, Danke", "no-thanks": "Nein, Danke",
@ -295,6 +300,10 @@
"modifiedFlowsDesc": "Übernimmt nur Flows, die geänderte Nodes enthalten", "modifiedFlowsDesc": "Übernimmt nur Flows, die geänderte Nodes enthalten",
"modifiedNodes": "Geänderte Nodes", "modifiedNodes": "Geänderte Nodes",
"modifiedNodesDesc": "Übernimmt nur Nodes, die sich geändert haben", "modifiedNodesDesc": "Übernimmt nur Nodes, die sich geändert haben",
"startFlows": "Start",
"startFlowsDesc": "Flows starten",
"stopFlows": "Stop",
"stopFlowsDesc": "Flows stoppen",
"restartFlows": "Flows neustarten", "restartFlows": "Flows neustarten",
"restartFlowsDesc": "Startet die aktuell übernommenen Flows (ohne vorheriges Deploy)", "restartFlowsDesc": "Startet die aktuell übernommenen Flows (ohne vorheriges Deploy)",
"successfulDeploy": "Erfolgreich übernommen (deploy)", "successfulDeploy": "Erfolgreich übernommen (deploy)",
@ -376,7 +385,7 @@
"confirmDelete": "Sind Sie sicher mit dem Löschen dieses Subflows?", "confirmDelete": "Sind Sie sicher mit dem Löschen dieses Subflows?",
"info": "Beschreibung", "info": "Beschreibung",
"category": "Kategorie", "category": "Kategorie",
"module": "Module", "module": "Modul",
"license": "Lizenz", "license": "Lizenz",
"licenseNone": "Keine", "licenseNone": "Keine",
"licenseOther": "Andere", "licenseOther": "Andere",
@ -434,7 +443,7 @@
"icon": "Icon", "icon": "Icon",
"inputType": "Eingangstyp", "inputType": "Eingangstyp",
"selectType": "Wähle Typen ...", "selectType": "Wähle Typen ...",
"loadCredentials": "Lade Node-Berechtigungen", "loadCredentials": "Lade Node-Credentials",
"inputs": { "inputs": {
"input": "Eingang", "input": "Eingang",
"select": "Auswahl", "select": "Auswahl",
@ -450,7 +459,7 @@
"json": "JSON", "json": "JSON",
"bin": "buffer", "bin": "buffer",
"env": "Umgebungsvariable", "env": "Umgebungsvariable",
"cred": "Berechtigung" "cred": "Credentials"
}, },
"menu": { "menu": {
"input": "Eingang", "input": "Eingang",
@ -470,7 +479,7 @@
"errors": { "errors": {
"scopeChange": "Wenn Sie den Geltungsbereich (scope) ändern, wird er für Nodes in anderen Flows nicht verfügbar sein", "scopeChange": "Wenn Sie den Geltungsbereich (scope) ändern, wird er für Nodes in anderen Flows nicht verfügbar sein",
"invalidProperties": "Ungültige Eigenschaften:", "invalidProperties": "Ungültige Eigenschaften:",
"credentialLoadFailed": "Laden der Node-Berechtigungen fehlgeschlagen" "credentialLoadFailed": "Laden der Node-Credentials fehlgeschlagen"
} }
}, },
"keyboard": { "keyboard": {
@ -683,7 +692,8 @@
"showHelp": "Hilfe zeigen", "showHelp": "Hilfe zeigen",
"showInOutline": "Zeige im Editor", "showInOutline": "Zeige im Editor",
"showTopics": "Zeige Hilfethemen", "showTopics": "Zeige Hilfethemen",
"noHelp": "Kein Hilfethema ausgewählt" "noHelp": "Kein Hilfethema ausgewählt",
"changeLog": "Änderungsprotokoll"
}, },
"config": { "config": {
"name": "Konfigurations-Node", "name": "Konfigurations-Node",
@ -737,7 +747,7 @@
"addToProject": "Zu Projekt hinzufügen", "addToProject": "Zu Projekt hinzufügen",
"files": "Dateien", "files": "Dateien",
"flow": "Flow", "flow": "Flow",
"credentials": "Berechtigungen", "credentials": "Credentials",
"package": "Paket", "package": "Paket",
"packageCreate": "Datei wird erstellt beim Speichern der Änderungen", "packageCreate": "Datei wird erstellt beim Speichern der Änderungen",
"fileNotExist": "Datei existiert nicht", "fileNotExist": "Datei existiert nicht",
@ -750,7 +760,7 @@
"changeTheEncryptionKey": "Schlüssel ändern", "changeTheEncryptionKey": "Schlüssel ändern",
"currentKey": "Aktueller Schlüssel", "currentKey": "Aktueller Schlüssel",
"newKey": "Neuer Schlüssel", "newKey": "Neuer Schlüssel",
"credentialsAlert": "Dadurch werden alle vorhandenen Berechtigungen gelöscht", "credentialsAlert": "Dadurch werden alle vorhandenen Credentials gelöscht",
"versionControl": "Versionsverwaltung (Git)", "versionControl": "Versionsverwaltung (Git)",
"branches": "Branches", "branches": "Branches",
"noBranches": "Keine Branches", "noBranches": "Keine Branches",
@ -886,7 +896,7 @@
"date": "timestamp", "date": "timestamp",
"jsonata": "JSONata", "jsonata": "JSONata",
"env": "Umgebungsvariable", "env": "Umgebungsvariable",
"cred": "Berechtigung" "cred": "Credentials"
} }
}, },
"editableList": { "editableList": {
@ -1026,7 +1036,7 @@
"passphrase": "Passphrase", "passphrase": "Passphrase",
"ssh-key-desc": "Bevor Sie ein Repository über SSH lokal klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zugreifen zu können", "ssh-key-desc": "Bevor Sie ein Repository über SSH lokal klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zugreifen zu können",
"ssh-key-add": "SSH-Schlüssel hinzufügen", "ssh-key-add": "SSH-Schlüssel hinzufügen",
"credential-key": "Schlüssel für Berechtigungen", "credential-key": "Schlüssel für Credentials",
"cant-get-ssh-key": "Fehler! Der ausgewählte SSH-Schlüsselpfad kann nicht abgerufen werden", "cant-get-ssh-key": "Fehler! Der ausgewählte SSH-Schlüsselpfad kann nicht abgerufen werden",
"already-exists2": "bereits vorhanden", "already-exists2": "bereits vorhanden",
"git-error": "Git-Fehler", "git-error": "Git-Fehler",
@ -1038,27 +1048,27 @@
"create": "Erstellen Sie Ihre Projektdateien", "create": "Erstellen Sie Ihre Projektdateien",
"desc0": "Ein Projekt enthält Ihre Flow-Dateien, eine README-Datei und die 'package.json'-Datei.", "desc0": "Ein Projekt enthält Ihre Flow-Dateien, eine README-Datei und die 'package.json'-Datei.",
"desc1": "Es kann alle anderen Dateien enthalten, die im Git-Repository verwaltet werden sollen.", "desc1": "Es kann alle anderen Dateien enthalten, die im Git-Repository verwaltet werden sollen.",
"desc2": "Ihre vorhandenen Flow- und Berechtigungs-Dateien werden in das Projekt kopiert.", "desc2": "Ihre vorhandenen Flow- und Credential-Dateien werden in das Projekt kopiert.",
"flow-file": "Flow-Datei", "flow-file": "Flow-Datei",
"credentials-file": "Datei mit Berechtigungen" "credentials-file": "Datei mit Credentials"
}, },
"encryption-config": { "encryption-config": {
"setup": "Einrichtung der Verschlüsselung Ihrer Datei mit den Berechtigungen", "setup": "Einrichtung der Verschlüsselung Ihrer Datei mit den Credentials",
"desc0": "Die Datei mit den Flow-Berechtigungen kann verschlüsselt werden, um ihren Inhalt zu schützen.", "desc0": "Die Datei mit den Flow-Credentials kann verschlüsselt werden, um ihren Inhalt zu schützen.",
"desc1": "Wenn Sie diese Berechtigungen in einem öffentlichen Repository speichern möchten, müssen Sie sie mit einen geheimen Schlüsselausdruck verschlüsseln.", "desc1": "Wenn Sie diese Credentials in einem öffentlichen Repository speichern möchten, müssen Sie sie mit einen geheimen Schlüsselausdruck verschlüsseln.",
"desc2": "Die Datei mit den Flow-Berechtigungen ist derzeit nicht verschlüsselt.", "desc2": "Die Datei mit den Flow-Credentials ist derzeit nicht verschlüsselt.",
"desc3": "D.h. ihr Inhalt (z.B. Passwörter und Zugriffs-Tokens) kann von jedem mit Zugriff auf die Datei gelesen werden.", "desc3": "D.h. ihr Inhalt (z.B. Passwörter und Zugriffs-Tokens) kann von jedem mit Zugriff auf die Datei gelesen werden.",
"desc4": "Wenn Sie diese Berechtigungen in einen öffentlichen Repository speichern möchten, müssen Sie diese verschlüsseln, indem Sie einen geheimen Schlüsselausdruck eingeben.", "desc4": "Wenn Sie diese Credentials in einen öffentlichen Repository speichern möchten, müssen Sie diese verschlüsseln, indem Sie einen geheimen Schlüsselausdruck eingeben.",
"desc5": "Ihre Datei mit den Flow-Berechtigungen wird derzeit mit dem Eintrag 'credentialSecret' Ihrer Einstellungsdatei als Schlüssel verschlüsselt.", "desc5": "Ihre Datei mit den Flow-Credentials wird derzeit mit dem Eintrag 'credentialSecret' Ihrer Einstellungsdatei als Schlüssel verschlüsselt.",
"desc6": "Die Datei mit den Flow-Berechtigungen wird derzeit mit einem vom System generierten Schlüssel verschlüsselt. Sie sollten einen neuen geheimen Schlüssel für dieses Projekt vorgeben.", "desc6": "Die Datei mit den Flow-Credentials wird derzeit mit einem vom System generierten Schlüssel verschlüsselt. Sie sollten einen neuen geheimen Schlüssel für dieses Projekt vorgeben.",
"desc7": "Der Schlüssel wird separat von den Projektdateien gespeichert. Sie müssen den Schlüssel angeben, damit dieses Projekt auch in einem anderen Node-RED-System verwendet werden kann.", "desc7": "Der Schlüssel wird separat von den Projektdateien gespeichert. Sie müssen den Schlüssel angeben, damit dieses Projekt auch in einem anderen Node-RED-System verwendet werden kann.",
"credentials": "Berechtigung", "credentials": "Credentials",
"enable": "Verschlüsselung aktivieren", "enable": "Verschlüsselung aktivieren",
"disable": "Verschlüsselung deaktivieren", "disable": "Verschlüsselung deaktivieren",
"disabled": "deaktiviert", "disabled": "deaktiviert",
"copy": "Vorhandenen Schlüssel ersetzen", "copy": "Vorhandenen Schlüssel ersetzen",
"use-custom": "Eigenen Schlüssel verwenden", "use-custom": "Eigenen Schlüssel verwenden",
"desc8": "Die Datei mit den Berechtigungen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden", "desc8": "Die Datei mit den Credentials wird nicht verschlüsselt und ihr Inhalt kann leicht gelesen werden",
"create-project-files": "Projektdateien erstellen", "create-project-files": "Projektdateien erstellen",
"create-project": "Projekt erstellen", "create-project": "Projekt erstellen",
"already-exists": "bereits vorhanden", "already-exists": "bereits vorhanden",
@ -1083,12 +1093,12 @@
"desc": "Beschreibung", "desc": "Beschreibung",
"opt": "Optional", "opt": "Optional",
"flow-file": "Flow-Datei", "flow-file": "Flow-Datei",
"credentials": "Berechtigungen", "credentials": "Credentials",
"enable-encryption": "Verschlüsselung aktivieren", "enable-encryption": "Verschlüsselung aktivieren",
"disable-encryption": "Verschlüsselung deaktivieren", "disable-encryption": "Verschlüsselung deaktivieren",
"encryption-key": "Schlüssel", "encryption-key": "Schlüssel",
"desc0": "Eine Floskel, mit der Sie Ihre Berechtigungen schützen", "desc0": "Eine Ausdruck, mit der Sie Ihre Credentials schützen",
"desc1": "Die Datei mit den Berechtigungen wird nicht verschlüsselt, und ihr Inhalt kann leicht gelesen werden", "desc1": "Die Datei mit den Credentials wird nicht verschlüsselt und ihr Inhalt kann leicht gelesen werden",
"git-url": "Git-Repository-URL", "git-url": "Git-Repository-URL",
"protocols": "https://, ssh:// oder file://", "protocols": "https://, ssh:// oder file://",
"auth-failed": "Authentifizierung fehlgeschlagen", "auth-failed": "Authentifizierung fehlgeschlagen",
@ -1098,7 +1108,7 @@
"passphrase": "Passphrase", "passphrase": "Passphrase",
"desc2": "Bevor Sie ein Repository über SSH klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zu zugreifen", "desc2": "Bevor Sie ein Repository über SSH klonen können, müssen Sie einen SSH-Schlüssel hinzufügen, um auf diesen zu zugreifen",
"add-ssh-key": "Einen SSH-Schlüssel hinzufügen", "add-ssh-key": "Einen SSH-Schlüssel hinzufügen",
"credentials-encryption-key": "Schlüssel für Berechtigungen", "credentials-encryption-key": "Schlüssel für Credentials",
"already-exists-2": "bereits vorhanden", "already-exists-2": "bereits vorhanden",
"git-error": "Git-Fehler", "git-error": "Git-Fehler",
"con-failed": "Verbindung fehlgeschlagen", "con-failed": "Verbindung fehlgeschlagen",
@ -1156,7 +1166,8 @@
"tourGuide": { "tourGuide": {
"takeATour": "Tour starten", "takeATour": "Tour starten",
"start": "Start", "start": "Start",
"next": "Nächste" "next": "Nächste",
"welcomeTours": "Welcome Tours"
}, },
"diagnostics": { "diagnostics": {
"title": "System-Informationen" "title": "System-Informationen"

View File

View File

View File

@ -53,8 +53,10 @@
"confirmDelete": "Confirm delete", "confirmDelete": "Confirm delete",
"delete": "Are you sure you want to delete '__label__'?", "delete": "Are you sure you want to delete '__label__'?",
"dropFlowHere": "Drop the flow here", "dropFlowHere": "Drop the flow here",
"dropImageHere": "Drop the image here",
"addFlow": "Add flow", "addFlow": "Add flow",
"addFlowToRight": "Add flow to the right", "addFlowToRight": "Add flow to the right",
"closeFlow": "Close flow",
"hideFlow": "Hide flow", "hideFlow": "Hide flow",
"hideOtherFlows": "Hide other flows", "hideOtherFlows": "Hide other flows",
"showAllFlows": "Show all flows (__count__ hidden)", "showAllFlows": "Show all flows (__count__ hidden)",
@ -692,7 +694,8 @@
"globalConfig": "Global Configuration Nodes", "globalConfig": "Global Configuration Nodes",
"triggerAction": "Trigger action", "triggerAction": "Trigger action",
"find": "Find in workspace", "find": "Find in workspace",
"copyItemUrl": "Copy item url" "copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard"
}, },
"help": { "help": {
"name": "Help", "name": "Help",
@ -945,6 +948,9 @@
"invalid-expr": "Invalid JSONata expression:\n __message__", "invalid-expr": "Invalid JSONata expression:\n __message__",
"invalid-msg": "Invalid example JSON message:\n __message__", "invalid-msg": "Invalid example JSON message:\n __message__",
"context-unsupported": "Cannot test context functions\n $flowContext or $globalContext", "context-unsupported": "Cannot test context functions\n $flowContext or $globalContext",
"env-unsupported": "Cannot test $env function",
"moment-unsupported": "Cannot test $moment function",
"clone-unsupported": "Cannot test $clone function",
"eval": "Error evaluating expression:\n __message__" "eval": "Error evaluating expression:\n __message__"
} }
}, },
@ -990,7 +996,10 @@
"quote": "Quote", "quote": "Quote",
"link": "Link", "link": "Link",
"horizontal-rule": "Horizontal rule", "horizontal-rule": "Horizontal rule",
"toggle-preview": "Toggle preview" "toggle-preview": "Toggle preview",
"mermaid": {
"summary": "Mermaid Diagram"
}
}, },
"bufferEditor": { "bufferEditor": {
"title": "Buffer editor", "title": "Buffer editor",
@ -1212,5 +1221,10 @@
"node": "Node", "node": "Node",
"junction": "Junction", "junction": "Junction",
"linkNodes": "Link Nodes" "linkNodes": "Link Nodes"
},
"env-var": {
"environment": "Environment",
"header": "Global Environment Variables",
"revert": "Revert"
} }
} }

View File

View File

View File

@ -53,8 +53,10 @@
"confirmDelete": "削除の確認", "confirmDelete": "削除の確認",
"delete": "本当に '__label__' を削除しますか?", "delete": "本当に '__label__' を削除しますか?",
"dropFlowHere": "ここにフローをドロップしてください", "dropFlowHere": "ここにフローをドロップしてください",
"dropImageHere": "ここに画像ファイルをドロップしてください",
"addFlow": "フローの追加", "addFlow": "フローの追加",
"addFlowToRight": "右側にフローを追加", "addFlowToRight": "右側にフローを追加",
"closeFlow": "フローを閉じる",
"hideFlow": "フローを非表示", "hideFlow": "フローを非表示",
"hideOtherFlows": "他のフローを非表示", "hideOtherFlows": "他のフローを非表示",
"showAllFlows": "全てのフローを表示", "showAllFlows": "全てのフローを表示",
@ -68,7 +70,11 @@
"enabled": "有効", "enabled": "有効",
"disabled": "無効", "disabled": "無効",
"info": "詳細", "info": "詳細",
"selectNodes": "ノードをクリックして選択" "selectNodes": "ノードをクリックして選択",
"enableFlow": "フローを有効化",
"disableFlow": "フローを無効化",
"moveToStart": "フローを先頭へ移動",
"moveToEnd": "フローを最後へ移動"
}, },
"menu": { "menu": {
"label": { "label": {
@ -683,7 +689,9 @@
"empty": "空", "empty": "空",
"globalConfig": "グローバル設定ノード", "globalConfig": "グローバル設定ノード",
"triggerAction": "アクションを実行", "triggerAction": "アクションを実行",
"find": "ワークスペース内を検索" "find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました"
}, },
"help": { "help": {
"name": "ヘルプ", "name": "ヘルプ",
@ -935,8 +943,11 @@
"errors": { "errors": {
"invalid-expr": "不正なJSONata式:\n __message__", "invalid-expr": "不正なJSONata式:\n __message__",
"invalid-msg": "不正なJSONメッセージ例:\n __message__", "invalid-msg": "不正なJSONメッセージ例:\n __message__",
"context-unsupported": "$flowContext や $globalContextの\nコンテキスト機能をテストできません", "context-unsupported": "$flowContext や $globalContextの\nコンテキスト関数をテストできません",
"eval": "表現評価エラー:\n __message__" "env-unsupported": "$env関数はテストできません",
"moment-unsupported": "$moment関数はテストできません",
"clone-unsupported": "$clone関数はテストできません",
"eval": "式評価エラー:\n __message__"
} }
}, },
"monaco": { "monaco": {
@ -981,7 +992,10 @@
"quote": "引用", "quote": "引用",
"link": "リンク", "link": "リンク",
"horizontal-rule": "区切り線", "horizontal-rule": "区切り線",
"toggle-preview": "プレビュー表示切替え" "toggle-preview": "プレビュー表示切替え",
"mermaid": {
"summary": "Mermaid図"
}
}, },
"bufferEditor": { "bufferEditor": {
"title": "バッファエディタ", "title": "バッファエディタ",
@ -1168,8 +1182,7 @@
"takeATour": "ツアーを開始", "takeATour": "ツアーを開始",
"start": "開始", "start": "開始",
"next": "次へ", "next": "次へ",
"welcomeTours": "ウェルカムツアー", "welcomeTours": "ウェルカムツアー"
"tours": "ツアー"
}, },
"diagnostics": { "diagnostics": {
"title": "システム情報" "title": "システム情報"
@ -1348,6 +1361,15 @@
"show-project-settings": "プロジェクト設定を表示", "show-project-settings": "プロジェクト設定を表示",
"show-version-control-tab": "バージョンコントロールタブを表示", "show-version-control-tab": "バージョンコントロールタブを表示",
"start-flows": "フローを開始", "start-flows": "フローを開始",
"stop-flows": "フローを停止" "stop-flows": "フローを停止",
"copy-item-url": "要素のURLをコピー",
"copy-item-edit-url": "要素の編集URLをコピー",
"move-flow-to-start": "フローを先頭に移動",
"move-flow-to-end": "フローを末尾に移動"
},
"env-var": {
"environment": "環境変数",
"header": "大域環境変数",
"revert": "破棄"
} }
} }

View File

View File

View File

View File

View File

View File

View File

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

View File

@ -902,14 +902,7 @@ RED.nodes = (function() {
var node; var node;
if (allNodes.hasTab(id)) { if (allNodes.hasTab(id)) {
removedNodes = allNodes.getNodes(id).filter(n => { removedNodes = allNodes.getNodes(id).slice()
if (n.type === 'junction') {
removedJunctions.push(n)
return false
} else {
return true
}
})
} }
for (i in configNodes) { for (i in configNodes) {
if (configNodes.hasOwnProperty(i)) { if (configNodes.hasOwnProperty(i)) {
@ -919,6 +912,7 @@ RED.nodes = (function() {
} }
} }
} }
removedJunctions = RED.nodes.junctions(id)
for (i=0;i<removedNodes.length;i++) { for (i=0;i<removedNodes.length;i++) {
var result = removeNode(removedNodes[i].id); var result = removeNode(removedNodes[i].id);
@ -1403,6 +1397,10 @@ RED.nodes = (function() {
exportedConfigNodes[n.id] = true; exportedConfigNodes[n.id] = true;
} }
}); });
subflowSet = subflowSet.concat(RED.nodes.junctions(subflowId))
subflowSet = subflowSet.concat(RED.nodes.groups(subflowId))
var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes); var exportableSubflow = createExportableNodeSet(subflowSet, exportedIds, exportedSubflows, exportedConfigNodes);
nns = exportableSubflow.concat(nns); nns = exportableSubflow.concat(nns);
} }
@ -2004,7 +2002,7 @@ RED.nodes = (function() {
} }
} }
} else { } else {
const keepNodesCurrentZ = reimport && n.z && RED.workspaces.contains(n.z) const keepNodesCurrentZ = reimport && n.z && (RED.workspaces.contains(n.z) || RED.nodes.subflow(n.z))
if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) { if (!keepNodesCurrentZ && n.z && !workspace_map[n.z] && !subflow_map[n.z]) {
n.z = activeWorkspace; n.z = activeWorkspace;
} }
@ -2106,7 +2104,7 @@ RED.nodes = (function() {
node.id = getID(); node.id = getID();
} else { } else {
node.id = n.id; node.id = n.id;
const keepNodesCurrentZ = reimport && node.z && RED.workspaces.contains(node.z) const keepNodesCurrentZ = reimport && node.z && (RED.workspaces.contains(node.z) || RED.nodes.subflow(node.z))
if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) { if (!keepNodesCurrentZ && (node.z == null || (!workspace_map[node.z] && !subflow_map[node.z]))) {
if (createMissingWorkspace) { if (createMissingWorkspace) {
if (missingWorkspace === null) { if (missingWorkspace === null) {
@ -2814,6 +2812,7 @@ RED.nodes = (function() {
} }
}); });
const nodeGroupMap = {}
var replaceNodeIds = Object.keys(replaceNodes); var replaceNodeIds = Object.keys(replaceNodes);
if (replaceNodeIds.length > 0) { if (replaceNodeIds.length > 0) {
var reimportList = []; var reimportList = [];
@ -2824,6 +2823,12 @@ RED.nodes = (function() {
} else { } else {
allNodes.removeNode(n); allNodes.removeNode(n);
} }
if (n.g) {
// reimporting a node *without* including its group object
// will cause the g property to be cleared. Cache it
// here so we can restore it
nodeGroupMap[n.id] = n.g
}
reimportList.push(convertNode(n)); reimportList.push(convertNode(n));
RED.events.emit('nodes:remove',n); RED.events.emit('nodes:remove',n);
}); });
@ -2845,6 +2850,18 @@ RED.nodes = (function() {
var newNodeMap = {}; var newNodeMap = {};
result.nodes.forEach(function(n) { result.nodes.forEach(function(n) {
newNodeMap[n.id] = n; newNodeMap[n.id] = n;
if (nodeGroupMap[n.id]) {
// This node is in a group - need to substitute the
// node reference inside the group
n.g = nodeGroupMap[n.id]
const group = RED.nodes.group(n.g)
if (group) {
var index = group.nodes.findIndex(gn => gn.id === n.id)
if (index > -1) {
group.nodes[index] = n
}
}
}
}); });
RED.nodes.eachLink(function(l) { RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) { if (newNodeMap.hasOwnProperty(l.source.id)) {

View File

@ -348,6 +348,8 @@ var RED = (function() {
loader.end() loader.end()
RED.notify($("<p>").text(message)); RED.notify($("<p>").text(message));
RED.sidebar.info.refresh() RED.sidebar.info.refresh()
RED.menu.setDisabled('menu-item-projects-open',false);
RED.menu.setDisabled('menu-item-projects-settings',false);
}); });
}); });
return; return;
@ -775,6 +777,7 @@ var RED = (function() {
RED.deploy.init(RED.settings.theme("deployButton",null)); RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.init(buildMainMenu); RED.keyboard.init(buildMainMenu);
RED.envVar.init();
RED.nodes.init(); RED.nodes.init();
RED.runtime.init() RED.runtime.init()
@ -793,7 +796,7 @@ var RED = (function() {
$('<div id="red-ui-header-shade" class="hide"></div>').appendTo(header); $('<div id="red-ui-header-shade" class="hide"></div>').appendTo(header);
$('<div id="red-ui-main-container" class="red-ui-sidebar-closed hide">'+ $('<div id="red-ui-main-container" class="red-ui-sidebar-closed hide">'+
'<div id="red-ui-workspace"></div>'+ '<div id="red-ui-workspace"></div>'+
'<div id="red-ui-editor-stack"></div>'+ '<div id="red-ui-editor-stack" tabindex="-1"></div>'+
'<div id="red-ui-palette"></div>'+ '<div id="red-ui-palette"></div>'+
'<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>'+

View File

@ -656,7 +656,12 @@ RED.clipboard = (function() {
$("#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();
var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini"; var format = RED.settings.flowFilePretty ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
const userFormat = RED.settings.get("editor.dialog.export.pretty")
if (userFormat === false || userFormat === true) {
format = userFormat ? "red-ui-clipboard-dialog-export-fmt-full" : "red-ui-clipboard-dialog-export-fmt-mini";
}
$("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) { $("#red-ui-clipboard-dialog-export-fmt-group > a").on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
@ -672,7 +677,8 @@ RED.clipboard = (function() {
var nodes = JSON.parse(flow); var nodes = JSON.parse(flow);
format = $(this).attr('id'); format = $(this).attr('id');
if (format === 'red-ui-clipboard-dialog-export-fmt-full') { const pretty = format === "red-ui-clipboard-dialog-export-fmt-full";
if (pretty) {
flow = JSON.stringify(nodes,null,4); flow = JSON.stringify(nodes,null,4);
} else { } else {
flow = JSON.stringify(nodes); flow = JSON.stringify(nodes);
@ -681,6 +687,7 @@ RED.clipboard = (function() {
setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50); setTimeout(function() { $("#red-ui-clipboard-dialog-export-text").scrollTop(0); },50);
$("#red-ui-clipboard-dialog-export-text").trigger("focus"); $("#red-ui-clipboard-dialog-export-text").trigger("focus");
RED.settings.set("editor.dialog.export.pretty", pretty)
} }
}); });

View File

@ -160,7 +160,7 @@
this.element.css("maxHeight",null); this.element.css("maxHeight",null);
} }
if (this.options.height !== 'auto') { if (this.options.height !== 'auto') {
this.uiContainer.css("overflow-y","scroll"); this.uiContainer.css("overflow-y","auto");
if (!isNaN(this.options.height)) { if (!isNaN(this.options.height)) {
this.uiHeight = this.options.height; this.uiHeight = this.options.height;
} }

View File

@ -829,7 +829,7 @@ RED.tabs = (function() {
event.preventDefault(); event.preventDefault();
removeTab(tab.id); removeTab(tab.id);
}); });
RED.popover.tooltip(closeLink,RED._("workspace.hideFlow")); RED.popover.tooltip(closeLink,RED._("workspace.closeFlow"));
} }
// if (tab.hideable) { // if (tab.hideable) {
// li.addClass("red-ui-tabs-closeable") // li.addClass("red-ui-tabs-closeable")

View File

@ -90,10 +90,10 @@
optEl.append(generateSpans(srcMatch)); optEl.append(generateSpans(srcMatch));
optEl.appendTo(element); optEl.appendTo(element);
} }
matches.push({ matches.push({
value: optVal, value: optVal,
label: element, label: element,
i: (valMatch.found ? valMatch.index : srcMatch.index) i: (valMatch.found ? valMatch.index : srcMatch.index)
}); });
} }
}) })
@ -146,7 +146,7 @@
{ value: "reset", source: ["delay","trigger","join","rbe"] }, { value: "reset", source: ["delay","trigger","join","rbe"] },
{ value: "responseCookies", source: ["http request"] }, { value: "responseCookies", source: ["http request"] },
{ value: "responseTopic", source: ["mqtt"] }, { value: "responseTopic", source: ["mqtt"] },
{ value: "responseURL", source: ["http request"] }, { value: "responseUrl", source: ["http request"] },
{ value: "restartTimeout", source: ["join"] }, { value: "restartTimeout", source: ["join"] },
{ value: "retain", source: ["mqtt"] }, { value: "retain", source: ["mqtt"] },
{ value: "schema", source: ["json"] }, { value: "schema", source: ["json"] },
@ -501,7 +501,7 @@
this.options.types = this.options.types||Object.keys(allOptions); this.options.types = this.options.types||Object.keys(allOptions);
} }
this.selectTrigger = $('<button class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect); this.selectTrigger = $('<button type="button" class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect);
$('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger); $('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
this.selectLabel = $('<span class="red-ui-typedInput-type-label"></span>').appendTo(this.selectTrigger); this.selectLabel = $('<span class="red-ui-typedInput-type-label"></span>').appendTo(this.selectTrigger);
@ -570,7 +570,7 @@
}) })
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline' // explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="red-ui-typedInput-icon fa fa-caret-down"></i></span></button>').appendTo(this.uiSelect); this.optionSelectTrigger = $('<button type="button" tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="red-ui-typedInput-icon fa fa-caret-down"></i></span></button>').appendTo(this.uiSelect);
this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger); this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
// RED.popover.tooltip(this.optionSelectLabel,function() { // RED.popover.tooltip(this.optionSelectLabel,function() {
// return that.optionValue; // return that.optionValue;
@ -591,7 +591,7 @@
that.uiSelect.addClass('red-ui-typedInput-focus'); that.uiSelect.addClass('red-ui-typedInput-focus');
}); });
this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect); this.optionExpandButton = $('<button type="button" tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"></button>').appendTo(this.uiSelect);
this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton); this.optionExpandButtonIcon = $('<i class="red-ui-typedInput-icon fa fa-ellipsis-h"></i>').appendTo(this.optionExpandButton);
this.type(this.typeField.val() || this.options.default||this.typeList[0].value); this.type(this.typeField.val() || this.options.default||this.typeList[0].value);

View File

@ -238,6 +238,7 @@ RED.editor = (function() {
var valid = validateNodeProperty(node, defaults, property,value); var valid = validateNodeProperty(node, defaults, property,value);
if (((typeof valid) === "string") || !valid) { if (((typeof valid) === "string") || !valid) {
input.addClass("input-error"); input.addClass("input-error");
input.next(".red-ui-typedInput-container").addClass("input-error");
if ((typeof valid) === "string") { if ((typeof valid) === "string") {
var tooltip = input.data("tooltip"); var tooltip = input.data("tooltip");
if (tooltip) { if (tooltip) {
@ -250,6 +251,7 @@ RED.editor = (function() {
} }
} else { } else {
input.removeClass("input-error"); input.removeClass("input-error");
input.next(".red-ui-typedInput-container").removeClass("input-error");
var tooltip = input.data("tooltip"); var tooltip = input.data("tooltip");
if (tooltip) { if (tooltip) {
input.data("tooltip", null); input.data("tooltip", null);
@ -1105,6 +1107,10 @@ RED.editor = (function() {
if (editing_node) { if (editing_node) {
RED.sidebar.info.refresh(editing_node); RED.sidebar.info.refresh(editing_node);
RED.sidebar.help.show(editing_node.type, false); RED.sidebar.help.show(editing_node.type, false);
//ensure focused element is NOT body (for keyboard scope to operate correctly)
if (document.activeElement.tagName === 'BODY') {
$('#red-ui-editor-stack').trigger('focus')
}
} }
} }
} }

View File

@ -45,6 +45,9 @@
selectedCodeEditor = RED.editor.codeEditor[defaultEditor]; selectedCodeEditor = RED.editor.codeEditor[defaultEditor];
initialised = selectedCodeEditor.init(); initialised = selectedCodeEditor.init();
} }
$('<div id="red-ui-image-drop-target"><div data-i18n="[append]workspace.dropImageHere"><i class="fa fa-download"></i><br></div></div>').appendTo('#red-ui-editor');
$("#red-ui-image-drop-target").hide();
} }
function create(options) { function create(options) {
@ -64,6 +67,7 @@
options = {}; options = {};
} }
var editor = null;
if (this.editor.type === MONACO) { if (this.editor.type === MONACO) {
// compatibility (see above note) // compatibility (see above note)
if (!options.element && !options.id) { if (!options.element && !options.id) {
@ -74,10 +78,14 @@
console.warn("createEditor() options.element or options.id is not valid", options); console.warn("createEditor() options.element or options.id is not valid", options);
$("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />'); $("#dialog-form").append('<div id="' + options.id + '" style="display: none;" />');
} }
return this.editor.create(options); editor = this.editor.create(options);
} else { } else {
return this.editor.create(options);//fallback to ACE editor = this.editor.create(options);//fallback to ACE
} }
if (options.mode === "ace/mode/markdown") {
RED.editor.customEditTypes['_markdown'].postInit(editor, options);
}
return editor;
} }
return { return {

View File

@ -100,7 +100,7 @@ RED.editor.codeEditor.monaco = (function() {
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" }, "node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" }, "node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
} }
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] ]; const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
const modulesCache = {}; const modulesCache = {};
@ -764,7 +764,7 @@ RED.editor.codeEditor.monaco = (function() {
if(!options.stateId && options.stateId !== false) { if(!options.stateId && options.stateId !== false) {
options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title).split("/").pop()); options.stateId = RED.editor.generateViewStateId("monaco", options, (options.mode || options.title || "").split("/").pop());
} }
var el = options.element || $("#"+options.id)[0]; var el = options.element || $("#"+options.id)[0];
var toolbarRow = $("<div>").appendTo(el); var toolbarRow = $("<div>").appendTo(el);

View File

@ -76,6 +76,9 @@ RED.editor.colorPicker = RED.colorPicker = (function() {
var focusTarget = colorInput; var focusTarget = colorInput;
colorInput.on("change", function (e) { colorInput.on("change", function (e) {
var color = colorInput.val(); var color = colorInput.val();
if (options.defaultValue && !color.match(/^([a-z]+|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3})$/)) {
color = options.defaultValue;
}
colorHiddenInput.val(color).trigger('change'); colorHiddenInput.val(color).trigger('change');
refreshDisplay(color); refreshDisplay(color);
}); });

View File

@ -255,6 +255,9 @@
var currentExpression = expressionEditor.getValue(); var currentExpression = expressionEditor.getValue();
var expr; var expr;
var usesContext = false; var usesContext = false;
var usesEnv = false;
var usesMoment = false;
var usesClone = false;
var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression); var legacyMode = /(^|[^a-zA-Z0-9_'".])msg([^a-zA-Z0-9_'"]|$)/.test(currentExpression);
$(".red-ui-editor-type-expression-legacy").toggle(legacyMode); $(".red-ui-editor-type-expression-legacy").toggle(legacyMode);
try { try {
@ -267,6 +270,18 @@
usesContext = true; usesContext = true;
return null; return null;
}); });
expr.assign("env", function(name) {
usesEnv = true;
return null;
});
expr.assign("moment", function(name) {
usesMoment = true;
return null;
});
expr.assign("clone", function(name) {
usesClone = true;
return null;
});
} catch(err) { } catch(err) {
testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1); testResultEditor.setValue(RED._("expressionEditor.errors.invalid-expr",{message:err.message}),-1);
return; return;
@ -284,6 +299,18 @@
testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1); testResultEditor.setValue(RED._("expressionEditor.errors.context-unsupported"),-1);
return; return;
} }
if (usesEnv) {
testResultEditor.setValue(RED._("expressionEditor.errors.env-unsupported"),-1);
return;
}
if (usesMoment) {
testResultEditor.setValue(RED._("expressionEditor.errors.moment-unsupported"),-1);
return;
}
if (usesClone) {
testResultEditor.setValue(RED._("expressionEditor.errors.clone-unsupported"),-1);
return;
}
var formattedResult; var formattedResult;
if (result !== undefined) { if (result !== undefined) {

View File

@ -14,6 +14,61 @@
* limitations under the License. * limitations under the License.
**/ **/
(function() { (function() {
/**
* Converts dropped image file to date URL
*/
function file2base64Image(file, cb) {
var reader = new FileReader();
reader.onload = (function (fd) {
return function (e) {
cb(e.target.result);
};
})(file);
reader.readAsDataURL(file);
}
var initialized = false;
var currentEditor = null;
/**
* Initialize handler for image file drag events
*/
function initImageDrag(elem, editor) {
$(elem).on("dragenter", function (ev) {
ev.preventDefault();
$("#red-ui-image-drop-target").css({display:'table'}).focus();
currentEditor = editor;
});
if (!initialized) {
initialized = true;
$("#red-ui-image-drop-target").on("dragover", function (ev) {
ev.preventDefault();
}).on("dragleave", function (ev) {
$("#red-ui-image-drop-target").hide();
}).on("drop", function (ev) {
ev.preventDefault();
if ($.inArray("Files",ev.originalEvent.dataTransfer.types) != -1) {
var files = ev.originalEvent.dataTransfer.files;
if (files.length === 1) {
var file = files[0];
var name = file.name.toLowerCase();
if (name.match(/\.(apng|avif|gif|jpeg|png|svg|webp)$/)) {
file2base64Image(file, function (image) {
var session = currentEditor.getSession();
var img = `<img src="${image}"/>\n`;
var pos = session.getCursorPosition();
session.insert(pos, img);
$("#red-ui-image-drop-target").hide();
});
return;
}
}
}
$("#red-ui-image-drop-target").hide();
});
}
}
var toolbarTemplate = '<div style="margin-bottom: 5px">'+ var toolbarTemplate = '<div style="margin-bottom: 5px">'+
'<span class="button-group">'+ '<span class="button-group">'+
@ -114,6 +169,7 @@
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop(); var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue())); $(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop); $(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
mermaid.init();
},200); },200);
}) })
if (options.header) { if (options.header) {
@ -122,6 +178,7 @@
if (value) { if (value) {
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue())); $(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
mermaid.init();
} }
panels = RED.panels.create({ panels = RED.panels.create({
id:"red-ui-editor-type-markdown-panels", id:"red-ui-editor-type-markdown-panels",
@ -148,10 +205,14 @@
}); });
RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview")); RED.popover.tooltip($("#node-btn-markdown-preview"), RED._("markdownEditor.toggle-preview"));
if (options.cursor && !expressionEditor._initState) { if(!expressionEditor._initState) {
expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false); if (options.cursor) {
} expressionEditor.gotoLine(options.cursor.row+1,options.cursor.column,false);
}
else {
expressionEditor.gotoLine(0, 0, false);
}
}
dialogForm.i18n(); dialogForm.i18n();
}, },
close: function() { close: function() {
@ -215,7 +276,11 @@
} }
}) })
return toolbar; return toolbar;
} },
postInit: function (editor, options) {
var elem = $("#"+options.id);
initImageDrag(elem, editor);
}
} }
RED.editor.registerTypeEditor("_markdown", definition); RED.editor.registerTypeEditor("_markdown", definition);
})(); })();

View File

@ -235,6 +235,7 @@
RED.editor.colorPicker.create({ RED.editor.colorPicker.create({
id: "red-ui-editor-node-color", id: "red-ui-editor-node-color",
value: color, value: color,
defaultValue: "#DDAA99",
palette: recommendedColors, palette: recommendedColors,
sortPalette: function (a, b) {return a.l - b.l;} sortPalette: function (a, b) {return a.l - b.l;}
}).appendTo(colorRow); }).appendTo(colorRow);

View File

@ -0,0 +1,175 @@
RED.envVar = (function() {
function saveEnvList(list) {
const items = list.editableList("items")
const new_env = [];
items.each(function (i,el) {
var data = el.data('data');
var item;
if (data.nameField && data.valueField) {
item = {
name: data.nameField.val(),
value: data.valueField.typedInput("value"),
type: data.valueField.typedInput("type")
};
new_env.push(item);
}
});
return new_env;
}
function getGlobalConf(create) {
var gconf = null;
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
gconf = conf;
}
});
if ((gconf === null) && create) {
var cred = {
_ : {},
map: {}
};
gconf = {
id: RED.nodes.id(),
type: "global-config",
env: [],
name: "global-config",
label: "",
hasUsers: false,
users: [],
credentials: cred,
_def: RED.nodes.getType("global-config"),
};
RED.nodes.add(gconf);
}
return gconf;
}
function applyChanges(list) {
var gconf = getGlobalConf(false);
var new_env = [];
var items = list.editableList('items');
var credentials = gconf ? gconf.credentials : null;
if (!credentials) {
credentials = {
_ : {},
map: {}
};
}
items.each(function (i,el) {
var data = el.data('data');
if (data.nameField && data.valueField) {
var item = {
name: data.nameField.val(),
value: data.valueField.typedInput("value"),
type: data.valueField.typedInput("type")
};
if (item.name.trim() !== "") {
new_env.push(item);
if ((item.type === "cred") && (item.value !== "__PWRD__")) {
credentials.map[item.name] = item.value;
credentials.map["has_"+item.name] = (item.value !== "");
item.value = "__PWRD__";
}
}
}
});
if (gconf === null) {
gconf = getGlobalConf(true);
}
if ((JSON.stringify(new_env) !== JSON.stringify(gconf.env)) ||
(JSON.stringify(credentials) !== JSON.stringify(gconf.credentials))) {
gconf.env = new_env;
gconf.credentials = credentials;
RED.nodes.dirty(true);
}
}
function getSettingsPane() {
var gconf = getGlobalConf(false);
var env = gconf ? gconf.env : [];
var cred = gconf ? gconf.credentials : null;
if (!cred) {
cred = {
_ : {},
map: {}
};
}
var pane = $("<div/>", {
id: "red-ui-settings-tab-envvar",
class: "form-horizontal"
});
var content = $("<div/>", {
class: "form-row node-input-env-container-row"
}).css({
"margin": "10px"
}).appendTo(pane);
var label = $("<label></label>").css({
width: "100%"
}).appendTo(content);
$("<i/>", {
class: "fa fa-list"
}).appendTo(label);
$("<span/>").text(" "+RED._("env-var.header")).appendTo(label);
var list = $("<ol/>", {
id: "node-input-env-container"
}).appendTo(content);
var node = {
type: "",
env: env,
credentials: cred.map,
};
RED.editor.envVarList.create(list, node);
var buttons = $("<div/>").css({
"text-align": "right",
}).appendTo(content);
var revertButton = $("<button/>", {
class: "red-ui-button"
}).css({
}).text(RED._("env-var.revert")).appendTo(buttons);
var items = saveEnvList(list);
revertButton.on("click", function (ev) {
list.editableList("empty");
list.editableList("addItems", items);
});
return pane;
}
function init(done) {
if (!RED.user.hasPermission("settings.write")) {
RED.notify(RED._("user.errors.settings"),"error");
return;
}
RED.userSettings.add({
id:'envvar',
title: RED._("env-var.environment"),
get: getSettingsPane,
focus: function() {
var height = $("#red-ui-settings-tab-envvar").parent().height();
$("#node-input-env-container").editableList("height", (height -100));
},
close: function() {
var list = $("#node-input-env-container");
try {
applyChanges(list);
}
catch (e) {
console.log(e);
console.log(e.stack);
}
}
});
}
return {
init: init,
};
})();

View File

@ -101,6 +101,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({ RED.editor.colorPicker.create({
id:"node-input-style-stroke", id:"node-input-style-stroke",
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4", value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
defaultValue: "#a4a4a4",
palette: colorPalette, palette: colorPalette,
cellPerRow: colorCount, cellPerRow: colorCount,
cellWidth: 16, cellWidth: 16,
@ -112,6 +113,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({ RED.editor.colorPicker.create({
id:"node-input-style-fill", id:"node-input-style-fill",
value: style.fill || defaultGroupStyle.fill ||"none", value: style.fill || defaultGroupStyle.fill ||"none",
defaultValue: "none",
palette: colorPalette, palette: colorPalette,
cellPerRow: colorCount, cellPerRow: colorCount,
cellWidth: 16, cellWidth: 16,
@ -129,6 +131,7 @@ RED.group = (function() {
RED.editor.colorPicker.create({ RED.editor.colorPicker.create({
id:"node-input-style-color", id:"node-input-style-color",
value: style.color || defaultGroupStyle.color ||"#a4a4a4", value: style.color || defaultGroupStyle.color ||"#a4a4a4",
defaultValue: "#a4a4a4",
palette: colorPalette, palette: colorPalette,
cellPerRow: colorCount, cellPerRow: colorCount,
cellWidth: 16, cellWidth: 16,

0
packages/node_modules/@node-red/editor-client/src/js/ui/library.js vendored Executable file → Normal file
View File

View File

@ -0,0 +1,46 @@
// Mermaid diagram stub library for on-demand dynamic loading
// Will be overwritten after script loading by $.getScript
var mermaid = (function () {
var enabled /* = undefined */;
var initializing = false;
var initCalled = false;
function initialize(opt) {
if (enabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
enabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
enabled = true;
}
}
if (enabled) {
initializing = true;
$.getScript("vendor/mermaid/mermaid.min.js",
function (data, stat, jqxhr) {
$(".mermaid").show();
// invoke loaded mermaid API
initializing = false;
mermaid.initialize(opt);
if (initCalled) {
mermaid.init();
initCalled = false;
}
});
}
}
function init() {
if (initializing) {
$(".mermaid").hide();
initCalled = true;
}
}
return {
initialize: initialize,
init: init,
};
})();

16
packages/node_modules/@node-red/editor-client/src/js/ui/palette.js vendored Executable file → Normal file
View File

@ -175,9 +175,19 @@ RED.palette = (function() {
$('<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) $('<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,"\\'"); const safeType = type.replace(/'/g,"\\'");
const wrapStr = function (str) {
if(str.indexOf(' ') >= 0) {
return '"' + str + '"'
}
return str
}
$('<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"; 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)
.on('click', function() {
RED.search.show('type:' + wrapStr(safeType))
})
$('<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) $('<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);
@ -428,6 +438,7 @@ RED.palette = (function() {
categoryNode.find(".red-ui-palette-content").slideToggle(); categoryNode.find(".red-ui-palette-content").slideToggle();
categoryNode.find("i").toggleClass("expanded"); categoryNode.find("i").toggleClass("expanded");
} }
categoryNode.hide();
} }
} }
@ -506,6 +517,7 @@ RED.palette = (function() {
currentCategoryNode.find(".red-ui-palette-content").slideToggle(); currentCategoryNode.find(".red-ui-palette-content").slideToggle();
currentCategoryNode.find("i").toggleClass("expanded"); currentCategoryNode.find("i").toggleClass("expanded");
} }
currentCategoryNode.hide();
} }
} }

View File

@ -545,7 +545,7 @@ RED.projects = (function() {
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow); var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow); $('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow); subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) { $('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault(); e.preventDefault();
dialog.dialog( "close" ); dialog.dialog( "close" );
RED.userSettings.show('gitconfig'); RED.userSettings.show('gitconfig');
@ -1171,11 +1171,11 @@ RED.projects = (function() {
row = $('<div class="form-row button-group"></div>').appendTo(container); row = $('<div class="form-row button-group"></div>').appendTo(container);
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row); var openProject = $('<button type="button" data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row); var createAsEmpty = $('<button type="button" data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row); // var createAsCopy = $('<button type="button" data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row); var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row); // var createAsClone = $('<button type="button" data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) { row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected'); container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
@ -1271,7 +1271,7 @@ RED.projects = (function() {
var credentialsLeftBox = $('<div class="red-ui-projects-dialog-credentials-box-left">').appendTo(credentialsBox); var credentialsLeftBox = $('<div class="red-ui-projects-dialog-credentials-box-left">').appendTo(credentialsBox);
var credentialsEnabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-enabled"></div>').appendTo(credentialsLeftBox); var credentialsEnabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-enabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled"> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox); $('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="enabled" checked> <i class="fa fa-lock"></i> <span>'+RED._("projects.encryption-config.enable")+'</span></label>').appendTo(credentialsEnabledBox);
var credentialsDisabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-disabled disabled"></div>').appendTo(credentialsLeftBox); var credentialsDisabledBox = $('<div class="form-row red-ui-projects-dialog-credentials-box-disabled disabled"></div>').appendTo(credentialsLeftBox);
$('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="disabled"> <i class="fa fa-unlock"></i> <span>'+RED._("projects.encryption-config.disable")+'</span></label>').appendTo(credentialsDisabledBox); $('<label class="red-ui-projects-edit-form-inline-label"><input type="radio" name="projects-encryption-type" value="disabled"> <i class="fa fa-unlock"></i> <span>'+RED._("projects.encryption-config.disable")+'</span></label>').appendTo(credentialsDisabledBox);
@ -1397,7 +1397,7 @@ RED.projects = (function() {
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow); var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow); $('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow); subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) { $('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
e.preventDefault(); e.preventDefault();
$('#red-ui-projects-dialog-cancel').trigger("click"); $('#red-ui-projects-dialog-cancel').trigger("click");
RED.userSettings.show('gitconfig'); RED.userSettings.show('gitconfig');
@ -1631,14 +1631,14 @@ RED.projects = (function() {
function deleteProject(row,name,done) { function deleteProject(row,name,done) {
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row); var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover); $('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>') $('<button type="button" class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
.appendTo(cover) .appendTo(cover)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
cover.remove(); cover.remove();
done(true); done(true);
}); });
$('<button class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>') $('<button type="button" class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
.appendTo(cover) .appendTo(cover)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();
@ -1822,7 +1822,7 @@ RED.projects = (function() {
header.addClass("selectable"); header.addClass("selectable");
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header); var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
$('<button class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>') $('<button type="button" class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
.appendTo(tools) .appendTo(tools)
.on("click", function(e) { .on("click", function(e) {
e.stopPropagation(); e.stopPropagation();

View File

@ -106,38 +106,51 @@ RED.search = (function() {
return val; return val;
} }
function search(val) { function extractType(val, flags) {
var results = []; // extracts: type:XYZ & type:"X Y Z"
var typeFilter; const regEx = /(?:type):\s*(?:"([^"]+)"|([^" ]+))/;
var m = /(?:^| )type:([^ ]+)/.exec(val); let m
if (m) { while ((m = regEx.exec(val)) !== null) {
val = val.replace(/(?:^| )type:[^ ]+/,""); // avoid infinite loops with zero-width matches
typeFilter = m[1]; if (m.index === regEx.lastIndex) {
regEx.lastIndex++;
}
val = val.replace(m[0]," ").trim()
const flag = m[2] || m[1] // quoted entries in capture group 1, unquoted in capture group 2
flags.type = flags.type || [];
flags.type.push(flag);
} }
var flags = {}; return val;
}
function search(val) {
const results = [];
const flags = {};
val = extractFlag(val,"invalid",flags); val = extractFlag(val,"invalid",flags);
val = extractFlag(val,"unused",flags); val = extractFlag(val,"unused",flags);
val = extractFlag(val,"config",flags); val = extractFlag(val,"config",flags);
val = extractFlag(val,"subflow",flags); val = extractFlag(val,"subflow",flags);
val = extractFlag(val,"hidden",flags); val = extractFlag(val,"hidden",flags);
val = extractFlag(val,"modified",flags); val = extractFlag(val,"modified",flags);
val = extractValue(val,"flow",flags);// flow:active or flow:<flow-id> val = extractValue(val,"flow",flags);// flow:current or flow:<flow-id>
val = extractValue(val,"uses",flags);// uses:<node-id> val = extractValue(val,"uses",flags);// uses:<node-id>
val = extractType(val,flags);// type:<node-type>
val = val.trim(); val = val.trim();
var hasFlags = Object.keys(flags).length > 0; const hasFlags = Object.keys(flags).length > 0;
const hasTypeFilter = flags.type && flags.type.length > 0
if (flags.flow && flags.flow.indexOf("current") >= 0) { if (flags.flow && flags.flow.indexOf("current") >= 0) {
let idx = flags.flow.indexOf("current"); let idx = flags.flow.indexOf("current");
flags.flow[idx] = RED.workspaces.active();//convert active to flow ID flags.flow[idx] = RED.workspaces.active();//convert 'current' to active flow ID
} }
if (flags.flow && flags.flow.length) { if (flags.flow && flags.flow.length) {
flags.flow = [ ...new Set(flags.flow) ]; //deduplicate flags.flow = [ ...new Set(flags.flow) ]; //deduplicate
} }
if (val.length > 0 || typeFilter || hasFlags) { if (val.length > 0 || hasFlags) {
val = val.toLowerCase(); val = val.toLowerCase();
var i; let i;
var j; let j;
var list = []; let list = [];
var nodes = {}; const nodes = {};
let keys = []; let keys = [];
if (flags.uses) { if (flags.uses) {
keys = flags.uses; keys = flags.uses;
@ -145,10 +158,10 @@ RED.search = (function() {
keys = Object.keys(index); keys = Object.keys(index);
} }
for (i=0;i<keys.length;i++) { for (i=0;i<keys.length;i++) {
var key = keys[i]; const key = keys[i];
var kpos = keys[i].indexOf(val); const kpos = val ? keys[i].indexOf(val) : -1;
if (kpos > -1) { if (kpos > -1 || (val === "" && hasFlags)) {
var ids = Object.keys(index[key]||{}); const ids = Object.keys(index[key]||{});
for (j=0;j<ids.length;j++) { for (j=0;j<ids.length;j++) {
var node = index[key][ids[j]]; var node = index[key][ids[j]];
var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group'; var isConfigNode = node.node._def.category === "config" && node.node.type !== 'group';
@ -156,7 +169,7 @@ RED.search = (function() {
continue; continue;
} }
if (flags.hasOwnProperty("invalid")) { if (flags.hasOwnProperty("invalid")) {
var nodeIsValid = !node.node.hasOwnProperty("valid") || node.node.valid; const nodeIsValid = !node.node.hasOwnProperty("valid") || node.node.valid;
if (flags.invalid === nodeIsValid) { if (flags.invalid === nodeIsValid) {
continue; continue;
} }
@ -186,8 +199,8 @@ RED.search = (function() {
} }
} }
if (flags.hasOwnProperty("unused")) { if (flags.hasOwnProperty("unused")) {
var isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) || const isUnused = (node.node.type === 'subflow' && node.node.instances.length === 0) ||
(isConfigNode && node.node.users.length === 0) (isConfigNode && node.node.users.length === 0 && node.node._def.hasUsers !== false)
if (flags.unused !== isUnused) { if (flags.unused !== isUnused) {
continue; continue;
} }
@ -197,12 +210,16 @@ RED.search = (function() {
continue; continue;
} }
} }
if (!typeFilter || node.node.type === typeFilter) { let typeIndex = -1
nodes[node.node.id] = nodes[node.node.id] = { if(hasTypeFilter) {
typeIndex = flags.type.indexOf(node.node.type)
}
if (!hasTypeFilter || typeIndex > -1) {
nodes[node.node.id] = nodes[node.node.id] || {
node: node.node, node: node.node,
label: node.label label: node.label
}; };
nodes[node.node.id].index = Math.min(nodes[node.node.id].index||Infinity,kpos); nodes[node.node.id].index = Math.min(nodes[node.node.id].index || Infinity, typeIndex > -1 ? typeIndex : kpos);
} }
} }
} }
@ -538,7 +555,7 @@ RED.search = (function() {
$(previousActiveElement).trigger("focus"); $(previousActiveElement).trigger("focus");
} }
previousActiveElement = null; previousActiveElement = null;
} }
if(!keepSearchToolbar) { if(!keepSearchToolbar) {
clearActiveSearch(); clearActiveSearch();
} }
@ -630,7 +647,7 @@ RED.search = (function() {
$("#red-ui-sidebar-shade").on('mousedown',hide); $("#red-ui-sidebar-shade").on('mousedown',hide);
$("#red-ui-view-searchtools-close").on("click", function close() { $("#red-ui-view-searchtools-close").on("click", function close() {
clearActiveSearch(); clearActiveSearch();
updateSearchToolbar(); updateSearchToolbar();
}); });
$("#red-ui-view-searchtools-close").trigger("click"); $("#red-ui-view-searchtools-close").trigger("click");

View File

@ -521,6 +521,13 @@ RED.subflow = (function() {
RED.nodes.groups(id).forEach(function(n) { RED.nodes.groups(id).forEach(function(n) {
removedGroups.push(n); removedGroups.push(n);
}) })
var removedJunctions = RED.nodes.junctions(id)
for (var i=0;i<removedJunctions.length;i++) {
var removedEntities = RED.nodes.removeJunction(removedJunctions[i])
removedLinks = removedLinks.concat(removedEntities.links)
}
var removedConfigNodes = []; var removedConfigNodes = [];
for (var i=0;i<removedNodes.length;i++) { for (var i=0;i<removedNodes.length;i++) {
var removedEntities = RED.nodes.remove(removedNodes[i].id); var removedEntities = RED.nodes.remove(removedNodes[i].id);
@ -551,6 +558,7 @@ RED.subflow = (function() {
nodes:removedNodes, nodes:removedNodes,
links:removedLinks, links:removedLinks,
groups: removedGroups, groups: removedGroups,
junctions: removedJunctions,
subflows: [activeSubflow] subflows: [activeSubflow]
} }
} }
@ -950,7 +958,6 @@ RED.subflow = (function() {
function buildEnvUIRow(row, tenv, ui, node) { function buildEnvUIRow(row, tenv, ui, node) {
console.log(tenv, ui)
ui.label = ui.label||{}; ui.label = ui.label||{};
if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) { if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
ui.type = "cred"; ui.type = "cred";

View File

@ -50,7 +50,7 @@ RED.sidebar.help = (function() {
tocPanel = $("<div>", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer); tocPanel = $("<div>", {class: "red-ui-sidebar-help-toc"}).appendTo(stackContainer);
var helpPanel = $("<div>").css({ var helpPanel = $("<div>").css({
"overflow-y": "scroll" "overflow-y": "auto"
}).appendTo(stackContainer); }).appendTo(stackContainer);
panels = RED.panels.create({ panels = RED.panels.create({

View File

@ -108,7 +108,7 @@ RED.sidebar.info = (function() {
propertiesPanelContent = $("<div>").css({ propertiesPanelContent = $("<div>").css({
"flex":"1 1 auto", "flex":"1 1 auto",
"overflow-y":"scroll", "overflow-y":"auto",
}).appendTo(propertiesPanel); }).appendTo(propertiesPanel);
@ -463,7 +463,8 @@ RED.sidebar.info = (function() {
el = el.next(); el = el.next();
} }
$(this).toggleClass('expanded',!isExpanded); $(this).toggleClass('expanded',!isExpanded);
}) });
mermaid.init();
} }
var tips = (function() { var tips = (function() {

View File

@ -269,8 +269,8 @@ RED.typeSearch = (function() {
moveCallback = opts.move; moveCallback = opts.move;
RED.events.emit("type-search:open"); RED.events.emit("type-search:open");
//shade.show(); //shade.show();
if ($("#red-ui-main-container").height() - opts.y - 150 < 0) { if ($("#red-ui-main-container").height() - opts.y - 195 < 0) {
opts.y = opts.y - 235; opts.y = opts.y - 275;
} }
dialog.css({left:opts.x+"px",top:opts.y+"px"}).show(); dialog.css({left:opts.x+"px",top:opts.y+"px"}).show();
searchResultsDiv.slideDown(300); searchResultsDiv.slideDown(300);

View File

@ -96,6 +96,37 @@ RED.utils = (function() {
} }
} }
var mermaidIsInitialized = false;
var mermaidIsEnabled /* = undefined */;
renderer.code = function (code, lang) {
if(lang === "mermaid") {
// mermaid diagram rendering
if (mermaidIsEnabled === undefined) {
if (RED.settings.markdownEditor &&
RED.settings.markdownEditor.mermaid) {
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
}
else {
mermaidIsEnabled = true;
}
}
if (mermaidIsEnabled) {
if (!mermaidIsInitialized) {
mermaidIsInitialized = true;
mermaid.initialize({startOnLoad:false});
}
return `<pre class='mermaid'>${code}</pre>`;
}
else {
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
}
}
else {
return "<pre><code>" +code +"</code></pre>";
}
};
window._marked.setOptions({ window._marked.setOptions({
renderer: renderer, renderer: renderer,
gfm: true, gfm: true,

View File

@ -1043,7 +1043,7 @@ RED.view.tools = (function() {
const nodeDef = n._def || RED.nodes.getType(n.type) const nodeDef = n._def || RED.nodes.getType(n.type)
if (nodeDef && nodeDef.defaults && nodeDef.defaults.name) { if (nodeDef && nodeDef.defaults && nodeDef.defaults.name) {
const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef) const paletteLabel = RED.utils.getPaletteLabel(n.type, nodeDef)
const defaultNodeNameRE = new RegExp('^'+paletteLabel+' (\\d+)$') const defaultNodeNameRE = new RegExp('^'+paletteLabel.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')+' (\\d+)$')
if (!typeIndex.hasOwnProperty(n.type)) { if (!typeIndex.hasOwnProperty(n.type)) {
const existingNodes = RED.nodes.filterNodes({type: n.type}) const existingNodes = RED.nodes.filterNodes({type: n.type})
let maxNameNumber = 0; let maxNameNumber = 0;
@ -1242,7 +1242,7 @@ RED.view.tools = (function() {
url += '/edit' url += '/edit'
} }
if (RED.clipboard.copyText(url)) { if (RED.clipboard.copyText(url)) {
RED.notify('Copied url to clipboard', { timeout: 2000 }) RED.notify(RED._("sidebar.info.copyURL2Clipboard"), { timeout: 2000 })
} }
} }
} }

75
packages/node_modules/@node-red/editor-client/src/js/ui/view.js vendored Executable file → Normal file
View File

@ -1105,12 +1105,15 @@ RED.view = (function() {
RED.view.redraw(); RED.view.redraw();
} }
// `point` is the place in the workspace the mouse has clicked.
// This takes into account scrolling and scaling of the workspace.
var ox = point[0]; var ox = point[0];
var oy = point[1]; var oy = point[1];
// Need to map that to browser location to position the pop-up
const offset = $("#red-ui-workspace-chart").offset() const offset = $("#red-ui-workspace-chart").offset()
var clientX = ox + offset.left - $("#red-ui-workspace-chart").scrollLeft() var clientX = (ox * scaleFactor) + offset.left - $("#red-ui-workspace-chart").scrollLeft()
var clientY = oy + offset.top - $("#red-ui-workspace-chart").scrollTop() var clientY = (oy * scaleFactor) + offset.top - $("#red-ui-workspace-chart").scrollTop()
if (RED.settings.get("editor").view['view-snap-grid']) { if (RED.settings.get("editor").view['view-snap-grid']) {
// eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red')
@ -3413,6 +3416,9 @@ RED.view = (function() {
d3.event.stopPropagation(); d3.event.stopPropagation();
return return
} }
// Avoid dbl click causing text selection.
d3.event.preventDefault()
document.getSelection().removeAllRanges()
if (d.type != "subflow") { if (d.type != "subflow") {
if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) { if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) {
RED.workspaces.show(d.type.substring(8)); RED.workspaces.show(d.type.substring(8));
@ -4954,7 +4960,7 @@ RED.view = (function() {
if (d._def.button) { if (d._def.button) {
var buttonEnabled = isButtonEnabled(d); var buttonEnabled = isButtonEnabled(d);
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled); this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled);
if (RED.runtime && Object.hasOwn(RED.runtime,'started')) { if (RED.runtime && RED.runtime.started !== undefined) {
this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started); this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started);
} }
@ -5651,7 +5657,24 @@ RED.view = (function() {
if (activeSubflow) { if (activeSubflow) {
activeSubflowChanged = activeSubflow.changed; activeSubflowChanged = activeSubflow.changed;
} }
var result = RED.nodes.import(nodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap}); var filteredNodesToImport = nodesToImport;
var globalConfig = null;
var gconf = null;
RED.nodes.eachConfig(function (conf) {
if (conf.type === "global-config") {
gconf = conf;
}
});
if (gconf) {
filteredNodesToImport = nodesToImport.filter(function (n) {
return (n.type !== "global-config");
});
globalConfig = nodesToImport.find(function (n) {
return (n.type === "global-config");
});
}
var result = RED.nodes.import(filteredNodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap});
if (result) { if (result) {
var new_nodes = result.nodes; var new_nodes = result.nodes;
var new_links = result.links; var new_links = result.links;
@ -5783,6 +5806,50 @@ RED.view = (function() {
} }
} }
if (globalConfig) {
// merge global env to existing global-config
var env0 = gconf.env;
var env1 = globalConfig.env;
var newEnv = Array.from(env0);
var changed = false;
env1.forEach(function (item1) {
var index = newEnv.findIndex(function (item0) {
return (item0.name === item1.name);
});
if (index >= 0) {
var item0 = newEnv[index];
if ((item0.type !== item1.type) ||
(item0.value !== item1.value)) {
newEnv[index] = item1;
changed = true;
}
}
else {
newEnv.push(item1);
changed = true;
}
});
if(changed) {
gconf.env = newEnv;
var replaceEvent = {
t: "edit",
node: gconf,
changed: true,
changes: {
env: env0
}
};
historyEvent = {
t:"multi",
events: [
replaceEvent,
historyEvent,
]
};
}
}
RED.history.push(historyEvent); RED.history.push(historyEvent);
updateActiveNodes(); updateActiveNodes();

View File

@ -152,6 +152,13 @@ RED.workspaces = (function() {
const hiddenflowCount = hiddenFlows.size; const hiddenflowCount = hiddenFlows.size;
let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active()) let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active())
let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false
const currentTabs = workspace_tabs.listTabs();
let flowCount = 0;
currentTabs.forEach(tab => {
if (RED.nodes.workspace(tab)) {
flowCount++;
}
});
let isCurrentLocked = RED.workspaces.isActiveLocked() let isCurrentLocked = RED.workspaces.isActiveLocked()
if (tab) { if (tab) {
@ -224,8 +231,7 @@ RED.workspaces = (function() {
null null
) )
} }
const currentTabs = workspace_tabs.listTabs() const activeIndex = currentTabs.findIndex(id => (activeWorkspace && (id === activeWorkspace.id)));
const activeIndex = currentTabs.findIndex(id => id === activeWorkspace.id)
menuItems.push( menuItems.push(
{ {
label: RED._("workspace.moveToStart"), label: RED._("workspace.moveToStart"),
@ -267,11 +273,13 @@ RED.workspaces = (function() {
) )
} }
menuItems.push( menuItems.push(
{ {
id:"red-ui-tabs-menu-option-add-hide-all-flows", id:"red-ui-tabs-menu-option-add-hide-all-flows",
label: RED._("workspace.hideAllFlows"), label: RED._("workspace.hideAllFlows"),
onselect: "core:hide-all-flows" onselect: "core:hide-all-flows",
disabled: (hiddenflowCount === flowCount)
}, },
{ {
id:"red-ui-tabs-menu-option-add-show-all-flows", id:"red-ui-tabs-menu-option-add-show-all-flows",
@ -298,7 +306,8 @@ RED.workspaces = (function() {
} else if (tab.type === 'subflow') { } else if (tab.type === 'subflow') {
RED.subflow.delete(tab.id) RED.subflow.delete(tab.id)
} }
} },
disabled: (workspaceTabCount === 1)
}, },
{ {
label: RED._("menu.label.export"), label: RED._("menu.label.export"),

View File

@ -30,7 +30,7 @@
bottom: 0px; bottom: 0px;
left:0px; left:0px;
right: 0px; right: 0px;
overflow-y: scroll; overflow-y: auto;
} }
.red-ui-debug-filter-box { .red-ui-debug-filter-box {
position:absolute; position:absolute;

View File

@ -37,3 +37,27 @@
} }
} }
} }
#red-ui-image-drop-target {
position: absolute;
top: 0; bottom: 0;
left: 0; right: 0;
background: var(--red-ui-dnd-background);
display:table;
width: 100%;
height: 100%;
display: none;
z-index:100;
div {
pointer-events: none;
display: table-cell;
vertical-align: middle;
text-align: center;
font-size: 40px;
color: var(--red-ui-dnd-color);
i {
pointer-events: none;
font-size: 80px;
}
}
}

View File

@ -368,7 +368,7 @@ button.red-ui-button-small
border:1px solid var(--red-ui-secondary-border-color); border:1px solid var(--red-ui-secondary-border-color);
border-radius:5px; border-radius:5px;
height: calc(100% - 21px); height: calc(100% - 21px);
overflow-y: scroll; overflow-y: auto;
background: var(--red-ui-secondary-background); background: var(--red-ui-secondary-background);
} }
@ -562,7 +562,7 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle {
.red-ui-icon-list { .red-ui-icon-list {
width: 308px; width: 308px;
height: 200px; height: 200px;
overflow-y: scroll; overflow-y: auto;
line-height: 0px; line-height: 0px;
position: relative; position: relative;
&.red-ui-icon-list-dark { &.red-ui-icon-list-dark {

View File

@ -32,7 +32,8 @@
color: var(--red-ui-primary-text-color); color: var(--red-ui-primary-text-color);
border: 1px solid var(--red-ui-notification-border-default); border: 1px solid var(--red-ui-notification-border-default);
border-left-width: 16px; border-left-width: 16px;
overflow: hidden; overflow: scroll;
max-height: 80vh;
.ui-dialog-buttonset { .ui-dialog-buttonset {
margin-top: 20px; margin-top: 20px;
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -26,7 +26,7 @@
} }
} }
#red-ui-project-settings-tab-settings { #red-ui-project-settings-tab-settings {
overflow-y: scroll; overflow-y: auto;
} }
.red-ui-sidebar-vc-shade { .red-ui-sidebar-vc-shade {
background: var(--red-ui-primary-background); background: var(--red-ui-primary-background);
@ -183,7 +183,7 @@
} }
.red-ui-projects-dialog-project-list-inner-container { .red-ui-projects-dialog-project-list-inner-container {
flex-grow: 1 ; flex-grow: 1 ;
overflow-y: scroll; overflow-y: auto;
position:relative; position:relative;
.red-ui-editableList-border { .red-ui-editableList-border {
border: none; border: none;

View File

@ -41,6 +41,7 @@
height: 50px; height: 50px;
background: var(--red-ui-secondary-background); background: var(--red-ui-secondary-background);
border: 2px solid var(--red-ui-primary-border-color); border: 2px solid var(--red-ui-primary-border-color);
color: var(--red-ui-primary-text-color);
text-align: center; text-align: center;
line-height:50px; line-height:50px;
@ -51,7 +52,7 @@
.red-ui-editor-radial-menu-opt-disabled { .red-ui-editor-radial-menu-opt-disabled {
border-color: var(--red-ui-tertiary-border-color); border-color: var(--red-ui-tertiary-border-color);
color: var(--red-ui-tertiary-border-color); color: var(--red-ui-secondary-text-color-disabled);
} }
.red-ui-editor-radial-menu-opt-active { .red-ui-editor-radial-menu-opt-active {
background: var(--red-ui-secondary-background-hover); background: var(--red-ui-secondary-background-hover);

View File

@ -20,7 +20,7 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
overflow-y: scroll; overflow-y: auto;
.red-ui-palette-category { .red-ui-palette-category {
&:not(.expanded) button { &:not(.expanded) button {

View File

@ -67,7 +67,7 @@
left: 0; left: 0;
bottom: 0; bottom: 0;
padding: 8px 20px 20px; padding: 8px 20px 20px;
overflow-y: scroll; overflow-y: auto;
} }
.red-ui-settings-row { .red-ui-settings-row {
padding: 5px 10px 2px; padding: 5px 10px 2px;

View File

@ -29,7 +29,7 @@
#red-ui-workspace-chart { #red-ui-workspace-chart {
overflow: auto; overflow: auto;
position: absolute; position: absolute;
bottom:25px; bottom:26px;
top: 35px; top: 35px;
left:0px; left:0px;
right:0px; right:0px;

View File

@ -14,6 +14,9 @@ declare var msg: NodeMessage;
/** @type {string} the id of the incoming `msg` (alias of msg._msgid) */ /** @type {string} the id of the incoming `msg` (alias of msg._msgid) */
declare const __msgid__:string; declare const __msgid__:string;
declare const util:typeof import('util')
declare const promisify:typeof import('util').promisify
/** /**
* @typedef NodeStatus * @typedef NodeStatus
* @type {object} * @type {object}

View File

@ -160,6 +160,7 @@
'$base64encode':{ args:[ ]}, '$base64encode':{ args:[ ]},
'$boolean':{ args:[ 'arg' ]}, '$boolean':{ args:[ 'arg' ]},
'$ceil':{ args:[ 'number' ]}, '$ceil':{ args:[ 'number' ]},
'$clone': { args:[ 'arg' ]},
'$contains':{ args:[ 'str', 'pattern' ]}, '$contains':{ args:[ 'str', 'pattern' ]},
'$count':{ args:[ 'array' ]}, '$count':{ args:[ 'array' ]},
'$decodeUrl':{ args:[ 'str' ]}, '$decodeUrl':{ args:[ 'str' ]},

View File

@ -95,45 +95,64 @@ module.exports = function(RED) {
} }
this.on("input", function(msg, send, done) { this.on("input", function(msg, send, done) {
var errors = []; const errors = [];
var props = this.props; let props = this.props;
if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) { if (msg.__user_inject_props__ && Array.isArray(msg.__user_inject_props__)) {
props = msg.__user_inject_props__; props = msg.__user_inject_props__;
} }
delete msg.__user_inject_props__; delete msg.__user_inject_props__;
props.forEach(p => { props = [...props]
var property = p.p; function evaluateProperty(doneEvaluating) {
var value = p.v ? p.v : ''; if (props.length === 0) {
var valueType = p.vt ? p.vt : 'str'; doneEvaluating()
return
}
const p = props.shift()
const property = p.p;
const value = p.v ? p.v : '';
const valueType = p.vt ? p.vt : 'str';
if (!property) return; if (property) {
if (valueType === "jsonata") {
if (valueType === "jsonata") { if (p.v) {
if (p.v) { try {
try { var exp = RED.util.prepareJSONataExpression(p.v, node);
var exp = RED.util.prepareJSONataExpression(p.v, node); var val = RED.util.evaluateJSONataExpression(exp, msg);
var val = RED.util.evaluateJSONataExpression(exp, msg); RED.util.setMessageProperty(msg, property, val, true);
RED.util.setMessageProperty(msg, property, val, true); }
catch (err) {
errors.push(err.message);
}
} }
catch (err) { evaluateProperty(doneEvaluating)
errors.push(err.message); } else {
try {
RED.util.evaluateNodeProperty(value, valueType, node, msg, (err, newValue) => {
if (err) {
errors.push(err.toString())
} else {
RED.util.setMessageProperty(msg,property,newValue,true);
}
evaluateProperty(doneEvaluating)
})
} catch (err) {
errors.push(err.toString());
evaluateProperty(doneEvaluating)
} }
} }
return; } else {
evaluateProperty(doneEvaluating)
} }
try {
RED.util.setMessageProperty(msg,property,RED.util.evaluateNodeProperty(value, valueType, this, msg),true);
} catch (err) {
errors.push(err.toString());
}
});
if (errors.length) {
done(errors.join('; '));
} else {
send(msg);
done();
} }
evaluateProperty(() => {
if (errors.length) {
done(errors.join('; '));
} else {
send(msg);
done();
}
})
}); });
} }

View File

@ -1,6 +1,6 @@
<script type="text/html" data-template-name="complete"> <script type="text/html" data-template-name="complete">
<div class="form-row node-input-target-row"> <div class="form-row node-input-target-row">
<button id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button> <button type="button" id="node-input-complete-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div> </div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px"> <div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-complete-target-filter"></div> <div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-complete-target-filter"></div>

View File

@ -4,7 +4,7 @@
<label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label> <label style="width: auto" for="node-input-scope" data-i18n="catch.label.source"></label>
<select id="node-input-scope-select"> <select id="node-input-scope-select">
<option value="all" data-i18n="catch.scope.all"></option> <option value="all" data-i18n="catch.scope.all"></option>
<option value="target" data-i18n="catch.scope.selected"></options> <option value="target" data-i18n="catch.scope.selected"></option>
</select> </select>
</div> </div>
<div class="form-row node-input-uncaught-row"> <div class="form-row node-input-uncaught-row">
@ -12,7 +12,7 @@
<label for="node-input-uncaught" style="width: auto" data-i18n="catch.label.uncaught"></label> <label for="node-input-uncaught" style="width: auto" data-i18n="catch.label.uncaught"></label>
</div> </div>
<div class="form-row node-input-target-row"> <div class="form-row node-input-target-row">
<button id="node-input-catch-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button> <button type="button" id="node-input-catch-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div> </div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px"> <div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-catch-target-filter"></div> <div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-catch-target-filter"></div>

View File

@ -4,11 +4,11 @@
<label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label> <label style="width: auto" for="node-input-scope" data-i18n="status.label.source"></label>
<select id="node-input-scope-select"> <select id="node-input-scope-select">
<option value="all" data-i18n="status.scope.all"></option> <option value="all" data-i18n="status.scope.all"></option>
<option value="target" data-i18n="status.scope.selected"></options> <option value="target" data-i18n="status.scope.selected"></option>
</select> </select>
</div> </div>
<div class="form-row node-input-target-row"> <div class="form-row node-input-target-row">
<button id="node-input-status-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button> <button type="button" id="node-input-status-target-select" class="red-ui-button" data-i18n="common.label.selectNodes"></button>
</div> </div>
<div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px"> <div class="form-row node-input-target-row node-input-target-list-row" style="position: relative; min-height: 100px">
<div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-status-target-filter"></div> <div style="position: absolute; top: -30px; right: 0;"><input type="text" id="node-input-status-target-filter"></div>

View File

@ -0,0 +1,27 @@
<script type="text/html" data-template-name="global-config">
  <div class="form-row">
<label style="width: 100%"><span data-i18n="global-config.label.open-conf"></span>:</label>
</div>
<div class="form-row">
<button class="red-ui-button" type="button" id="node-input-edit-env-var" data-i18n="editor:env-var.header" style="margin-left: 20px"></button>
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType('global-config',{
category: 'config',
defaults: {
name: { value: "" },
env: { value: [] },
},
credentials: {
map: { type: "map" }
},
oneditprepare: function() {
$('#node-input-edit-env-var').on('click', function(evt) {
RED.actions.invoke('core:show-user-settings', 'envvar')
});
},
hasUsers: false
});
</script>

View File

@ -0,0 +1,7 @@
module.exports = function(RED) {
"use strict";
function GlobalConfigNode(n) {
RED.nodes.createNode(this,n);
}
RED.nodes.registerType("global-config", GlobalConfigNode);
}

View File

@ -17,6 +17,8 @@
display: flex; display: flex;
background: var(--red-ui-tertiary-background); background: var(--red-ui-tertiary-background);
padding-right: 75px; padding-right: 75px;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
} }
#node-input-libs-container-row .red-ui-editableList-header > div { #node-input-libs-container-row .red-ui-editableList-header > div {
flex-grow: 1; flex-grow: 1;
@ -91,21 +93,21 @@
<div id="func-tab-init" style="display:none"> <div id="func-tab-init" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative"> <div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div> <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-init-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-init-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div> </div>
</div> </div>
<div id="func-tab-body" style="display:none"> <div id="func-tab-body" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative"> <div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div> <div style="height: 220px; min-height:150px;" class="node-text-editor" id="node-input-func-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-function-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div> </div>
</div> </div>
<div id="func-tab-finalize" style="display:none"> <div id="func-tab-finalize" style="display:none">
<div class="form-row node-text-editor-row" style="position:relative"> <div class="form-row node-text-editor-row" style="position:relative">
<div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div> <div style="height: 250px; min-height:150px;" class="node-text-editor" id="node-input-finalize-editor" ></div>
<div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div> <div style="position: absolute; right:0; bottom: calc(100% - 20px); z-Index: 10;"><button type="button" id="node-finalize-expand-js" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button></div>
</div> </div>
</div> </div>
@ -294,7 +296,7 @@
if (val === "_custom_") { if (val === "_custom_") {
val = $(this).val(); val = $(this).val();
} }
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/].?/g, function(v) { return v[1]?v[1].toUpperCase():"" }); var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/\.].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
fvar.val(varName); fvar.val(varName);
fvar.trigger("change"); fvar.trigger("change");
@ -451,11 +453,13 @@
tabs.activateTab("func-tab-body"); tabs.activateTab("func-tab-body");
$( "#node-input-outputs" ).spinner({ $( "#node-input-outputs" ).spinner({
min:0, min: 0,
max: 500,
change: function(event, ui) { change: function(event, ui) {
var value = this.value; var value = parseInt(this.value);
if (!value.match(/^\d+$/)) { value = 1; } value = isNaN(value) ? 1 : value;
else if (value < this.min) { value = this.min; } value = Math.max(value, parseInt($(this).attr("aria-valuemin")));
value = Math.min(value, parseInt($(this).attr("aria-valuemax")));
if (value !== this.value) { $(this).spinner("value", value); } if (value !== this.value) { $(this).spinner("value", value); }
} }
}); });

View File

@ -318,7 +318,7 @@ module.exports = function(RED) {
} }
var r = node.rules[currentRule]; var r = node.rules[currentRule];
if (r.t === "move") { if (r.t === "move") {
if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1)) { if ((r.tot !== r.pt) || (r.p.indexOf(r.to) !== -1) && (r.p !== r.to)) {
applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => { applyRule(msg,{t:"set", p:r.to, pt:r.tot, to:r.p, tot:r.pt},(err,msg) => {
applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => { applyRule(msg,{t:"delete", p:r.p, pt:r.pt}, (err,msg) => {
completeApplyingRules(msg,currentRule,done); completeApplyingRules(msg,currentRule,done);

View File

@ -10,6 +10,7 @@
<option value="scale" data-i18n="range.scale.payload"></option> <option value="scale" data-i18n="range.scale.payload"></option>
<option value="clamp" data-i18n="range.scale.limit"></option> <option value="clamp" data-i18n="range.scale.limit"></option>
<option value="roll" data-i18n="range.scale.wrap"></option> <option value="roll" data-i18n="range.scale.wrap"></option>
<option value="drop" data-i18n="range.scale.drop"></option>
</select> </select>
</div> </div>
<br/> <br/>

View File

@ -32,11 +32,15 @@ module.exports = function(RED) {
if (value !== undefined) { if (value !== undefined) {
var n = Number(value); var n = Number(value);
if (!isNaN(n)) { if (!isNaN(n)) {
if (node.action == "clamp") { if (node.action === "drop") {
if (n < node.minin) { done(); return; }
if (n > node.maxin) { done(); return; }
}
if (node.action === "clamp") {
if (n < node.minin) { n = node.minin; } if (n < node.minin) { n = node.minin; }
if (n > node.maxin) { n = node.maxin; } if (n > node.maxin) { n = node.maxin; }
} }
if (node.action == "roll") { if (node.action === "roll") {
var divisor = node.maxin - node.minin; var divisor = node.maxin - node.minin;
n = ((n - node.minin) % divisor + divisor) % divisor + node.minin; n = ((n - node.minin) % divisor + divisor) % divisor + node.minin;
} }

View File

@ -21,12 +21,13 @@
<option value="javascript">JavaScript</option> <option value="javascript">JavaScript</option>
<option value="css">CSS</option> <option value="css">CSS</option>
<option value="markdown">Markdown</option> <option value="markdown">Markdown</option>
<option value="php">PHP</option>
<option value="python">Python</option> <option value="python">Python</option>
<option value="sql">SQL</option> <option value="sql">SQL</option>
<option value="yaml">YAML</option> <option value="yaml">YAML</option>
<option value="text" data-i18n="template.label.none"></option> <option value="text" data-i18n="template.label.none"></option>
</select> </select>
<button id="node-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button> <button type="button" id="node-template-expand-editor" class="red-ui-button red-ui-button-small"><i class="fa fa-expand"></i></button>
</div> </div>
</div> </div>
<div class="form-row node-text-editor-row"> <div class="form-row node-text-editor-row">

View File

@ -25,7 +25,7 @@
<select id="node-then-type" style="width:70%;"> <select id="node-then-type" style="width:70%;">
<option value="block" data-i18n="trigger.wait-reset"></option> <option value="block" data-i18n="trigger.wait-reset"></option>
<option value="wait" data-i18n="trigger.wait-for"></option> <option value="wait" data-i18n="trigger.wait-for"></option>
<option value="loop" data-i18n="trigger.wait-loop"></option> <option id="node-trigger-wait-loop" value="loop" data-i18n="trigger.wait-loop"></option>
</select> </select>
</div> </div>
<div class="form-row node-type-duration"> <div class="form-row node-type-duration">
@ -181,6 +181,13 @@
$("#node-input-op2type").val('str'); $("#node-input-op2type").val('str');
} }
$("#node-input-op1").on("change", function() {
if ($("#node-input-op1type").val() === "nul") {
$("#node-trigger-wait-loop").hide();
}
else { $("#node-trigger-wait-loop").show(); }
});
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false}; var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false}; var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false};
var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false}; var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false};

View File

@ -25,7 +25,7 @@
<label class="red-ui-button" for="node-config-input-certfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label> <label class="red-ui-button" for="node-config-input-certfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" id="node-config-input-certfile"> <input class="hide" type="file" id="node-config-input-certfile">
<span id="tls-config-certname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span> <span id="tls-config-certname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-cert-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button> <button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-cert-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span> </span>
<input type="hidden" id="node-config-input-certname"> <input type="hidden" id="node-config-input-certname">
<input type="hidden" id="node-config-input-certdata"> <input type="hidden" id="node-config-input-certdata">
@ -37,7 +37,7 @@
<label class="red-ui-button" for="node-config-input-keyfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label> <label class="red-ui-button" for="node-config-input-keyfile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" id="node-config-input-keyfile"> <input class="hide" type="file" id="node-config-input-keyfile">
<span id="tls-config-keyname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span> <span id="tls-config-keyname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-key-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button> <button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-key-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span> </span>
<input type="hidden" id="node-config-input-keyname"> <input type="hidden" id="node-config-input-keyname">
<input type="hidden" id="node-config-input-keydata"> <input type="hidden" id="node-config-input-keydata">
@ -53,7 +53,7 @@
<label class="red-ui-button" for="node-config-input-cafile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label> <label class="red-ui-button" for="node-config-input-cafile"><i class="fa fa-upload"></i> <span data-i18n="tls.label.upload"></span></label>
<input class="hide" type="file" title=" " id="node-config-input-cafile"> <input class="hide" type="file" title=" " id="node-config-input-cafile">
<span id="tls-config-caname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span> <span id="tls-config-caname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
<button class="red-ui-button red-ui-button-small" id="tls-config-button-ca-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button> <button type="button" class="red-ui-button red-ui-button-small" id="tls-config-button-ca-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
</span> </span>
<input type="hidden" id="node-config-input-caname"> <input type="hidden" id="node-config-input-caname">
<input type="hidden" id="node-config-input-cadata"> <input type="hidden" id="node-config-input-cadata">

View File

@ -101,6 +101,7 @@
hostField.val(data.host); hostField.val(data.host);
} }
}, },
sortable: true,
removable: true removable: true
}); });
if (this.noproxy) { if (this.noproxy) {

View File

@ -421,7 +421,11 @@
<script type="text/javascript"> <script type="text/javascript">
(function() { (function() {
var typedInputNoneOpt = { value: 'none', label: '', hasValue: false }; var typedInputNoneOpt = {
value: 'none',
label: RED._("node-red:mqtt.label.none"),
hasValue: false
};
var makeTypedInputOpt = function(value){ var makeTypedInputOpt = function(value){
return { return {
value: value, value: value,
@ -436,7 +440,11 @@
makeTypedInputOpt("text/csv"), makeTypedInputOpt("text/csv"),
makeTypedInputOpt("text/html"), makeTypedInputOpt("text/html"),
makeTypedInputOpt("text/plain"), makeTypedInputOpt("text/plain"),
{value:"other", label:""} {
value: "other",
label: RED._("node-red:mqtt.label.other"),
icon: "red/images/typedInput/az.svg"
}
]; ];
function getDefaultContentType(value) { function getDefaultContentType(value) {
@ -499,17 +507,17 @@
cleansession: {value: true}, cleansession: {value: true},
birthTopic: {value:"", validate:validateMQTTPublishTopic}, birthTopic: {value:"", validate:validateMQTTPublishTopic},
birthQos: {value:"0"}, birthQos: {value:"0"},
birthRetain: {value:false}, birthRetain: {value:"false"},
birthPayload: {value:""}, birthPayload: {value:""},
birthMsg: { value: {}}, birthMsg: { value: {}},
closeTopic: {value:"", validate:validateMQTTPublishTopic}, closeTopic: {value:"", validate:validateMQTTPublishTopic},
closeQos: {value:"0"}, closeQos: {value:"0"},
closeRetain: {value:false}, closeRetain: {value:"false"},
closePayload: {value:""}, closePayload: {value:""},
closeMsg: { value: {}}, closeMsg: { value: {}},
willTopic: {value:"", validate:validateMQTTPublishTopic}, willTopic: {value:"", validate:validateMQTTPublishTopic},
willQos: {value:"0"}, willQos: {value:"0"},
willRetain: {value:false}, willRetain: {value:"false"},
willPayload: {value:""}, willPayload: {value:""},
willMsg: { value: {}}, willMsg: { value: {}},
userProps: { value: ""}, userProps: { value: ""},

View File

@ -295,7 +295,7 @@ module.exports = function(RED) {
/* mute error - it simply isnt JSON, just leave payload as a string */ /* mute error - it simply isnt JSON, just leave payload as a string */
} }
} }
} //else { } //else {
//leave as buffer //leave as buffer
//} //}
} }
@ -357,7 +357,7 @@ module.exports = function(RED) {
return; return;
} }
done(err); done(err);
}); });
} else { } else {
done(); done();
} }
@ -366,6 +366,16 @@ module.exports = function(RED) {
} }
} }
function updateStatus(node, allNodes) {
let setStatus = setStatusDisconnected
if(node.connecting) {
setStatus = setStatusConnecting
} else if(node.connected) {
setStatus = setStatusConnected
}
setStatus(node, allNodes)
}
function setStatusDisconnected(node, allNodes) { function setStatusDisconnected(node, allNodes) {
if(allNodes) { if(allNodes) {
for (var id in node.users) { for (var id in node.users) {
@ -459,7 +469,6 @@ module.exports = function(RED) {
if(!opts || typeof opts !== "object") { if(!opts || typeof opts !== "object") {
return; //nothing to change, simply return return; //nothing to change, simply return
} }
const originalBrokerURL = node.brokerurl;
//apply property changes (only if the property exists in the opts object) //apply property changes (only if the property exists in the opts object)
setIfHasProperty(opts, node, "url", init); setIfHasProperty(opts, node, "url", init);
@ -468,13 +477,11 @@ module.exports = function(RED) {
setIfHasProperty(opts, node, "clientid", init); setIfHasProperty(opts, node, "clientid", init);
setIfHasProperty(opts, node, "autoConnect", init); setIfHasProperty(opts, node, "autoConnect", init);
setIfHasProperty(opts, node, "usetls", init); setIfHasProperty(opts, node, "usetls", init);
setIfHasProperty(opts, node, "usews", init);
setIfHasProperty(opts, node, "verifyservercert", init); setIfHasProperty(opts, node, "verifyservercert", init);
setIfHasProperty(opts, node, "compatmode", init); setIfHasProperty(opts, node, "compatmode", init);
setIfHasProperty(opts, node, "protocolVersion", init); setIfHasProperty(opts, node, "protocolVersion", init);
setIfHasProperty(opts, node, "keepalive", init); setIfHasProperty(opts, node, "keepalive", init);
setIfHasProperty(opts, node, "cleansession", init); setIfHasProperty(opts, node, "cleansession", init);
setIfHasProperty(opts, node, "sessionExpiry", init);
setIfHasProperty(opts, node, "topicAliasMaximum", init); setIfHasProperty(opts, node, "topicAliasMaximum", init);
setIfHasProperty(opts, node, "maximumPacketSize", init); setIfHasProperty(opts, node, "maximumPacketSize", init);
setIfHasProperty(opts, node, "receiveMaximum", init); setIfHasProperty(opts, node, "receiveMaximum", init);
@ -484,6 +491,11 @@ module.exports = function(RED) {
} else if (hasProperty(opts, "userProps")) { } else if (hasProperty(opts, "userProps")) {
node.userProperties = opts.userProps; node.userProperties = opts.userProps;
} }
if (hasProperty(opts, "sessionExpiry")) {
node.sessionExpiryInterval = opts.sessionExpiry;
} else if (hasProperty(opts, "sessionExpiryInterval")) {
node.sessionExpiryInterval = opts.sessionExpiryInterval
}
function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) { function createLWT(topic, payload, qos, retain, v5opts, v5SubPropName) {
let message = undefined; let message = undefined;
@ -567,9 +579,6 @@ module.exports = function(RED) {
if (typeof node.usetls === 'undefined') { if (typeof node.usetls === 'undefined') {
node.usetls = false; node.usetls = false;
} }
if (typeof node.usews === 'undefined') {
node.usews = false;
}
if (typeof node.verifyservercert === 'undefined') { if (typeof node.verifyservercert === 'undefined') {
node.verifyservercert = false; node.verifyservercert = false;
} }
@ -698,16 +707,21 @@ module.exports = function(RED) {
if (Object.keys(node.users).length === 1) { if (Object.keys(node.users).length === 1) {
if(node.autoConnect) { if(node.autoConnect) {
node.connect(); node.connect();
//update nodes status
setTimeout(function() {
updateStatus(node, true)
}, 1)
} }
} }
}; };
node.deregister = function(mqttNode,done) { node.deregister = function(mqttNode, done, autoDisconnect) {
delete node.users[mqttNode.id]; delete node.users[mqttNode.id];
if (!node.closing && node.connected && Object.keys(node.users).length === 0) { if (autoDisconnect && !node.closing && node.connected && Object.keys(node.users).length === 0) {
node.disconnect(); node.disconnect(done);
} else {
done();
} }
done();
}; };
node.canConnect = function() { node.canConnect = function() {
return !node.connected && !node.connecting; return !node.connected && !node.connecting;
@ -782,7 +796,9 @@ module.exports = function(RED) {
// Send any birth message // Send any birth message
if (node.birthMessage) { if (node.birthMessage) {
node.publish(node.birthMessage); setTimeout(() => {
node.publish(node.birthMessage);
}, 1);
} }
}); });
node._clientOn("reconnect", function() { node._clientOn("reconnect", function() {
@ -839,7 +855,7 @@ module.exports = function(RED) {
let waitEnd = (client, ms) => { let waitEnd = (client, ms) => {
return new Promise( (resolve, reject) => { return new Promise( (resolve, reject) => {
node.closing = true; node.closing = true;
if(!client) { if(!client) {
resolve(); resolve();
} else { } else {
const t = setTimeout(() => { const t = setTimeout(() => {
@ -991,14 +1007,21 @@ module.exports = function(RED) {
} }
if (topicOK) { if (topicOK) {
node.client.publish(msg.topic, msg.payload, options, function(err) { node.client.publish(msg.topic, msg.payload, options, function (err) {
done && done(err); if (done) {
return done(err)
}); } else if(err) {
node.error(err, msg)
}
})
} else { } else {
const error = new Error(RED._("mqtt.errors.invalid-topic")); const error = new Error(RED._("mqtt.errors.invalid-topic"))
error.warn = true; error.warn = true
done(error); if (done) {
done(error)
} else {
node.warn(error, msg)
}
} }
} }
}; };
@ -1011,7 +1034,7 @@ module.exports = function(RED) {
/** /**
* Add event handlers to the MQTT.js client and track them so that * Add event handlers to the MQTT.js client and track them so that
* we do not remove any handlers that the MQTT client uses internally. * we do not remove any handlers that the MQTT client uses internally.
* Use {@link node._clientRemoveListeners `node._clientRemoveListeners`} to remove handlers * Use {@link node._clientRemoveListeners `node._clientRemoveListeners`} to remove handlers
* @param {string} event The name of the event * @param {string} event The name of the event
* @param {function} handler The handler for this event * @param {function} handler The handler for this event
@ -1019,11 +1042,11 @@ module.exports = function(RED) {
node._clientOn = function(event, handler) { node._clientOn = function(event, handler) {
node.clientListeners.push({event, handler}) node.clientListeners.push({event, handler})
node.client.on(event, handler) node.client.on(event, handler)
} }
/** /**
* Remove event handlers from the MQTT.js client & only the events * Remove event handlers from the MQTT.js client & only the events
* that we attached in {@link node._clientOn `node._clientOn`}. * that we attached in {@link node._clientOn `node._clientOn`}.
* * If `event` is omitted, then all events matching `handler` are removed * * If `event` is omitted, then all events matching `handler` are removed
* * If `handler` is omitted, then all events named `event` are removed * * If `handler` is omitted, then all events named `event` are removed
* * If both parameters are omitted, then all events are removed * * If both parameters are omitted, then all events are removed
@ -1212,7 +1235,7 @@ module.exports = function(RED) {
} else { } else {
node.brokerConn.unsubscribe(node.topic,node.id, removed); node.brokerConn.unsubscribe(node.topic,node.id, removed);
} }
node.brokerConn.deregister(node, done); node.brokerConn.deregister(node, done, removed);
node.brokerConn = null; node.brokerConn = null;
} else { } else {
done(); done();
@ -1275,9 +1298,9 @@ module.exports = function(RED) {
node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"}); node.status({fill:"green",shape:"dot",text:"node-red:common.status.connected"});
} }
node.brokerConn.register(node); node.brokerConn.register(node);
node.on('close', function(done) { node.on('close', function(removed, done) {
if (node.brokerConn) { if (node.brokerConn) {
node.brokerConn.deregister(node,done); node.brokerConn.deregister(node, done, removed)
node.brokerConn = null; node.brokerConn = null;
} else { } else {
done(); done();

View File

@ -227,6 +227,7 @@
} }
}); });
}, },
sortable: true,
removable: true removable: true
}); });

View File

@ -46,7 +46,7 @@ module.exports = function(RED) {
isText = true; isText = true;
} else if (parsedType.type !== "application") { } else if (parsedType.type !== "application") {
isText = false; isText = false;
} else if ((parsedType.subtype !== "octet-stream") } else if ((parsedType.subtype !== "octet-stream")
&& (parsedType.subtype !== "cbor") && (parsedType.subtype !== "cbor")
&& (parsedType.subtype !== "x-protobuf")) { && (parsedType.subtype !== "x-protobuf")) {
checkUTF = true; checkUTF = true;
@ -200,6 +200,15 @@ module.exports = function(RED) {
this.callback = function(req,res) { this.callback = function(req,res) {
var msgid = RED.util.generateId(); var msgid = RED.util.generateId();
res._msgid = msgid; res._msgid = msgid;
// Since Node 15, req.headers are lazily computed and the property
// marked as non-enumerable.
// That means it doesn't show up in the Debug sidebar.
// This redefines the property causing it to be evaluated *and*
// marked as enumerable again.
Object.defineProperty(req, 'headers', {
value: req.headers,
enumerable: true
})
if (node.method.match(/^(post|delete|put|options|patch)$/)) { if (node.method.match(/^(post|delete|put|options|patch)$/)) {
node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body}); node.send({_msgid:msgid,req:req,res:createResponseWrapper(node,res),payload:req.body});
} else if (node.method == "get") { } else if (node.method == "get") {
@ -282,7 +291,7 @@ module.exports = function(RED) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
var node = this; var node = this;
this.headers = n.headers||{}; this.headers = n.headers||{};
this.statusCode = n.statusCode; this.statusCode = parseInt(n.statusCode);
this.on("input",function(msg,_send,done) { this.on("input",function(msg,_send,done) {
if (msg.res) { if (msg.res) {
var headers = RED.util.cloneMessage(node.headers); var headers = RED.util.cloneMessage(node.headers);
@ -323,7 +332,7 @@ module.exports = function(RED) {
} }
} }
} }
var statusCode = node.statusCode || msg.statusCode || 200; var statusCode = node.statusCode || parseInt(msg.statusCode) || 200;
if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) { if (typeof msg.payload == "object" && !Buffer.isBuffer(msg.payload)) {
msg.res._res.status(statusCode).jsonp(msg.payload); msg.res._res.status(statusCode).jsonp(msg.payload);
} else { } else {

View File

@ -91,6 +91,11 @@
<label for="node-input-senderr" style="width: auto" data-i18n="httpin.senderr"></label> <label for="node-input-senderr" style="width: auto" data-i18n="httpin.senderr"></label>
</div> </div>
<div class="form-row">
<input type="checkbox" id="node-input-insecureHTTPParser" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-insecureHTTPParser", style="width: auto;" data-i18n="httpin.insecureHTTPParser"></label>
</div>
<div class="form-row"> <div class="form-row">
<label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label> <label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label>
@ -227,6 +232,7 @@
persist: {value:false}, persist: {value:false},
proxy: {type:"http proxy",required: false, proxy: {type:"http proxy",required: false,
label:RED._("node-red:httpin.proxy-config") }, label:RED._("node-red:httpin.proxy-config") },
insecureHTTPParser: {value: false},
authType: {value: ""}, authType: {value: ""},
senderr: {value: false}, senderr: {value: false},
headers: { value: [] } headers: { value: [] }
@ -338,6 +344,12 @@
} else { } else {
$("#node-input-useProxy").prop("checked", false); $("#node-input-useProxy").prop("checked", false);
} }
if (node.insecureHTTPParser) {
$("node-intput-insecureHTTPParser").prop("checked", true)
} else {
$("node-intput-insecureHTTPParser").prop("checked", false)
}
updateProxyOptions(); updateProxyOptions();
$("#node-input-useProxy").on("click", function() { $("#node-input-useProxy").on("click", function() {
updateProxyOptions(); updateProxyOptions();
@ -405,6 +417,7 @@
}); });
}, },
sortable: true,
removable: true removable: true
}); });
if (node.headers) { if (node.headers) {

View File

@ -86,6 +86,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; } if (n.paytoqs === true || n.paytoqs === "query") { paytoqs = true; }
else if (n.paytoqs === "body") { paytobody = true; } else if (n.paytoqs === "body") { paytobody = true; }
node.insecureHTTPParser = n.insecureHTTPParser
var prox, noprox; var prox, noprox;
if (process.env.http_proxy) { prox = process.env.http_proxy; } if (process.env.http_proxy) { prox = process.env.http_proxy; }
@ -244,6 +245,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
delete options.headers[h]; delete options.headers[h];
} }
}) })
if (node.insecureHTTPParser) {
options.insecureHTTPParser = true
}
} }
], ],
beforeRedirect: [ beforeRedirect: [
@ -430,6 +435,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
formData.append(opt, val); formData.append(opt, val);
} else if (typeof val === 'object' && val.hasOwnProperty('value')) { } else if (typeof val === 'object' && val.hasOwnProperty('value')) {
formData.append(opt,val.value,val.options || {}); formData.append(opt,val.value,val.options || {});
} else if (Array.isArray(val)) {
for (var i=0; i<val.length; i++) {
formData.append(opt, val[i])
}
} else { } else {
formData.append(opt,JSON.stringify(val)); formData.append(opt,JSON.stringify(val));
} }

View File

@ -86,7 +86,7 @@ module.exports = function(RED) {
this.topic = n.topic; this.topic = n.topic;
this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/ this.stream = (!n.datamode||n.datamode=='stream'); /* stream,single*/
this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */ this.datatype = n.datatype||'buffer'; /* buffer,utf8,base64 */
this.newline = (n.newline||"").replace("\\n","\n").replace("\\r","\r").replace("\\t","\t"); this.newline = (n.newline||"").replace(/\\n/g,"\n").replace(/\\r/g,"\r").replace(/\\t/g,"\t");
this.base64 = n.base64; this.base64 = n.base64;
this.trim = n.trim || false; this.trim = n.trim || false;
this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server"); this.server = (typeof n.server == 'boolean')?n.server:(n.server == "server");

View File

@ -19,9 +19,9 @@ module.exports = function(RED) {
function CSVNode(n) { function CSVNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.template = (n.temp || ""); this.template = (n.temp || "");
this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r"); this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.quo = '"'; this.quo = '"';
this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r"); this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
this.winflag = (this.ret === "\r\n"); this.winflag = (this.ret === "\r\n");
this.lineend = "\n"; this.lineend = "\n";
this.multi = n.multi || "one"; this.multi = n.multi || "one";
@ -110,7 +110,12 @@ module.exports = function(RED) {
if (msg.payload[s].hasOwnProperty(p)) { if (msg.payload[s].hasOwnProperty(p)) {
/* istanbul ignore else */ /* istanbul ignore else */
if (typeof msg.payload[s][p] !== "object") { if (typeof msg.payload[s][p] !== "object") {
var q = "" + msg.payload[s][p]; // Fix to honour include null values flag
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
var q = "";
if (msg.payload[s][p] !== undefined) {
q += msg.payload[s][p];
}
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
q = q.replace(/"/g, '""'); q = q.replace(/"/g, '""');
ou += node.quo + q + node.quo + node.sep; ou += node.quo + q + node.quo + node.sep;
@ -130,9 +135,15 @@ module.exports = function(RED) {
ou += node.sep; ou += node.sep;
} }
else { else {
var p = RED.util.ensureString(RED.util.getMessageProperty(msg,"payload["+s+"]['"+template[t]+"']")); var tt = template[t];
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
else { tt = '"'+tt+'"'; }
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
/* istanbul ignore else */ /* istanbul ignore else */
if (p === "undefined") { p = ""; } if (p === undefined) { p = ""; }
// fix to honour include null values flag
//if (p === null && node.include_null_values !== true) { p = "";}
p = RED.util.ensureString(p);
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
p = p.replace(/"/g, '""'); p = p.replace(/"/g, '""');
ou += node.quo + p + node.quo + node.sep; ou += node.quo + p + node.quo + node.sep;

View File

@ -224,7 +224,12 @@
outputs:1, outputs:1,
icon: "join.svg", icon: "join.svg",
label: function() { label: function() {
return this.name||this._("join.join"); var nam = this.name||this._("join.join");
if (this.mode === "custom" && !isNaN(Number(this.count))) {
nam += " "+this.count;
if (this.accumulate === true) { nam+= "+"; }
}
return nam;
}, },
labelStyle: function() { labelStyle: function() {
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";

View File

@ -251,7 +251,9 @@ module.exports = function(RED) {
} }
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) {
node.pendingDones.push(done);
}
} }
if (node.buffer.length == 0) { if (node.buffer.length == 0) {
done(); done();

View File

@ -107,7 +107,14 @@
outputs:1, outputs:1,
icon: "batch.svg", icon: "batch.svg",
label: function() { label: function() {
return this.name||this._("batch.batch");; var nam = this.name||this._("batch.batch");
if (this.mode === "count" && !isNaN(Number(this.count))) {
nam += " "+this.count;
}
if (this.mode === "interval" && !isNaN(Number(this.interval))) {
nam += " "+this.interval+"s";
}
return nam;
}, },
labelStyle: function() { labelStyle: function() {
return this.name ? "node_label_italic" : ""; return this.name ? "node_label_italic" : "";

0
packages/node_modules/@node-red/nodes/core/storage/10-file.html vendored Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

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