diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5d4d87bb7..07efaf18e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -28,7 +28,7 @@ To help us understand the issue, please fill-in as much of the following informa ### Please tell us about your environment: - [ ] Node-RED version: -- [ ] node.js version: +- [ ] Node.js version: - [ ] npm version: - [ ] Platform/OS: - [ ] Browser: diff --git a/.github/ISSUE_TEMPLATE/--bug_report.md b/.github/ISSUE_TEMPLATE/--bug_report.md index ff13e2ace..63923455e 100644 --- a/.github/ISSUE_TEMPLATE/--bug_report.md +++ b/.github/ISSUE_TEMPLATE/--bug_report.md @@ -33,7 +33,7 @@ To help us understand the issue, please fill-in as much of the following informa ### Please tell us about your environment: - [ ] Node-RED version: -- [ ] node.js version: +- [ ] Node.js version: - [ ] npm version: - [ ] Platform/OS: - [ ] Browser: diff --git a/.travis.yml b/.travis.yml index 14236730a..a101eecb0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,3 @@ matrix: before_script: - npm install -g istanbul coveralls - node_js: "8" - allow_failures: - - node_js: "12" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bb84978a..b7b75d30f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,223 @@ +#### 1.0.3: Maintenance Release + +Runtime + - Increase timeouts in Subflow tests to minimise false positives + - Update grunt-sass and add node-sass for node12 support + - Fix timings of Delay node tests + - #2340 Update JSONata to 1.7.0 + - Bump https-proxy-agent version + - #2332 Fix error handling of nodes with multiple input handlers + - Add script to generate npm publish script + - #2371 Ensure folder is present before write (e.g. flows file not in user folder) + - #2371 Handle windows UNC '\\' paths + - #2366 Handle logging of non-JSON encodable objects + +Editor + - #2328 Fix language handling in subflow node + - Use default language if lng param not set in i18n req + - #2326 Fix palette editor search visualization + - #2375 Subflow status not showing i18n version of contained core nodes status + - Fix inverse of 'replace' editor event + - #2376 Fallback to base language files if present + - #2373 Support UI testing on the latest Google Chrome + - #2364 Add tooltip to expand button in markdown editor + - #2363 Support ctrl key to select tabs for Windows + - #2356 Make JSONata help initially shown in expression editor + - #2355 Prohibit line break in type menu of typedInput + +Nodes + - Delay: Fix delay to not pass through .reset and .flush props consistently + - #2352 File: Using the ‘a msg per line’ the last line does not get msg.topic passed + - #2339 HTTP Request: Check auth type on opening + - HTTP Request: add units info + - #2372 MQTT/WS: Improved proxy support for MQTT and WebSocket nodes + - #2370 MQTT: Add clarification that MQTT Out requires payload to send msg + + +#### 1.0.2: Maintenance Release + +Runtime + - Allow node.status() to be passed number/bool types + - Allow node emitted events to have multiple arguments + - #2323 Fixed docstrings to have them match the function signature (name of parameters). + - #2318 NLS: Unify translations of "boolean" + +Editor + - Ensure node status is refreshed whenever node is edited + - #2315 #2316 Ensure z property included in full message debug payload + - #2321 Fixed editor.json (JA nls) + - #2313 Fix element to collapse items in visual JSON editor + - #2314 Insert divider in menu by calling RED.menu.addItem('id', null); + +Nodes + - Change: Fixup use of node.done + - #2322 Template: Fix invalid JSON data in template node docs + - #2320 File: Fixed a typo in 10-file.html (JA nls) + - #2312 Template: Remove unnecessary comma in help text + - #2319 Inject: Interval of inject node should be 596 hours or less. + +#### 1.0.1: Maintenance Release + +Runtime + - #2301 Add env vars to enable safe mode and projects + - `NODE_RED_ENABLE_SAFE_MODE` + - `NODE_RED_ENABLE_PROJECTS` + +Editor + - #2308 Fix grid setting + - #2306 i18n support in tooltips + - Fix error when setting typedInput to boolean true/false + - #2299 Fix SVG icons in IE11 + - #2303 Fix issue where subflow color did not update when not on a flow + +Nodes + - #2297 TLS: Allow TLS config node to provide just CA cert + - #2307 Inject: Fix width on inject node property + - #2305 Switch: Let switch node between rule work both ways round + - Range: Add example to range node info and make use of target consistent + - Join: node must clone group message before sending + + +#### 1.0.0: Milestone Release + +Editor + - Add click-on-tooltip to close + - Fix node draggable handling + - Ensure complete node scope property is remapped on import + - Update i18n for project feature + - Fix menu hiding function for flow editor + - Normalise default subflow color references + - Hide header text of very small screens to deploy is visible + - Fix tab access on touch screens + - Update radialMenu to use standard theme colours + - Fix undefined reference loading on mobile + - Allow word breaking of node name with long word + - Enable wrap mode in Markdown editor + - Maximize the size of markdown editor + +Nodes + - remove legacy error option from file in mode + - Change MQTT node default 3.1 compatibility mode to false + - Show clear debug shortcut in tooltip + - Fix file-in port labels for all 4 options + - Add extra comment re Mustache escapes to Template info + - Fix typo in complete node + - Allow Function node output input to go to 0 + +#### 1.0.0-beta.4: Beta Release + +Runtime + - Clone the first message passed to node.send in Function node + +Editor + - Move flow-status button to footer for consistency + - Fix node hover effect to prevent jumping position + - Filter quick-add properly when splicing a wire + - Mark workspace dirty when deleting link node link Fixes #2274 + - Add red-ui-button class to strategy login button + - Fix padding of subflow locale select Closes #2276 + - Update info text of complete node & add JP text + - Add class red-ui-button to cancel button + - Add css class to login submit button (#2275) + - Realign subflow output port labels + - Move context sidebar auto-refresh option to individual sections + - Update Japanese message catalogue + - Fix subflow UI for select + - remove padding before label text for SUBFLOW UI row + - Allow SUBFLOW UI label row without variable name + +Nodes + - Remove old rc option from exec node for 1.0 + - Add python and SQL to template language options + - Fix Switch node display of jsonata_exp type + - Remove sentiment from core nodes + +#### 1.0.0-beta.3: Beta Release + +Runtime + - [FEATURE] Add Node Done API - make message passing async + - Ensure the subflow stop promise is waiting for before restarting + - Limit the regex for the /nodes/ api end points + - Add error event handler to ssh-keygen child_process Fixes #2255 + - Fix default value handling on context array access Fixes #2252 + - Remove all ui test dependencies from package.json + - Add req back to audit log events and extend to Projects api + - Ensure 2nd arg to node.error is an object Fixes #2228 + - Use a more atomic process for writing context files Fixes #2271 + + +Editor + - [FEATURE] Change core node categories + - [FEATURE] Subflow Instance property UI (#2236) + - [FEATURE] Add visual JSON editor + - [FEATURE] Add Action List dialog + - [FEATURE] Add new shortcut to clear debug message list - ctrl-alt-l + - [FEATURE] Add show-library dialog actions + - [FEATURE] Add shift-cursor handling for moving quick-add dialog + - [FEATURE] Add enable/disable-flow actions + - [FEATURE] Add actions to change deploy type + - [FEATURE] Allow config nodes to be disabled, tidy css and add actions + - [FEATURE] Add default shortcut (ctrl-d) for deploy + - [FEATURE] Initial implementation of redo (un-undo) - ctrl-y + - [FEATURE] add support for specifying subflow template color + - [FEATURE] Use ctrl-click on wire to splice node in place + - [FEATURE] Allow search results to show more than 25 results + - [FEATURE] Allow a node to change if it has an input port Closes #2268 + - Revealing node position needs to account for zoom level Fixes #2172 + - Fix typedInput option selection Fixes #2174 + - Fix palette node id handling so search works Fixes #2173 + - Add popover tooltips to debug sidebar,function and template + - Add popovers to context sidebar mini buttons + - Ensure node status icon is shown when value set + - Revert treeList children function signature change + - Restore tray component css for compatibility. Mark as deprecated + - fix function name & string compare function + - Handle empty list of example flows Fixes #2171 + - Ensure library list has an item selected when opened + - Ensure tooltip popover doesn't replace normal popover + - Fix clipboard export download button + - Ensure input box has focus on repeated quick add + - Fix width calculation of typedInput + - Remove some hardcoded css colors + - Fix display of node help when clicking in palette Fixes #2194 + - Ensure node help is loaded in the right language Fixes #2195 + - Do not allow tab focus on clipboard hidden element + - Fix undefined error on typedInput due to valueLabel used before being added + - Fix undo of flow disable state change + - Fix select-all action in main view + - Fix delete-all action on config node sidebar + - Update UI tests for new editor css + - Add insertItemAt doc to editableList + - Ensure focus returns to the right element after dialogs shown + - Set autocomplete to disabled in form input elements + - Update all node icons to SVG + - Handle png/svg fallback for def.icon values. Remove old pngs + - Ignore empty examples directories (don't add to import menu) + - better handle example file at any depth - #2222 + - Properly escape node types in palette + - Ensure session expiry timeout doesn't exceed limit + - Use node/tab map to make filterNodes more efficient + - Rearrange contents of subflow template settings tab + - Handle undefined node.\_def in edit stack title. + - fix converting selection to subflow + - Fix inserting new subflow node to existing wire between nodes + - Support displaying falsey node status values Fixes #2246 + - Remove tab menu from node property UI for subflow and config nodes + - Mark workspace dirty when shift-click-drag detaches wires Fixes #2260 + - Fix subflow category change on palette + + +Nodes + - Remove pi gpi, twitter, email and feedparser nodes from core + - Fix error handling in Websocket broadcast function Fixes #2182 + - Handle websocket item being parseable but not an object better + - stop join tripping up if last message of buffer is blank. + - Add support for env var propety in switch node + - Improve handling of file upload in request node + - Add "has key" rule to switch node + tests + - Optimise generation of switch node edit dialog + - Add keep-alive option to HTTP Request - #2261 + #### 1.0.0-beta.2: Beta Release Runtime @@ -46,6 +266,11 @@ Nodes - Add expand editor button to Template node - Update catch/status nodes to use selectNodes api and treeList +#### 0.20.8: Maintenance Release + + - Sanitize tab name in edit dialog + - Pass httpServer to runtime even when httpAdmin disabled Fixes #2272 + #### 0.20.7: Maintenance Release - Update jsonata to 1.6.5 which should fix #2183 @@ -725,7 +950,7 @@ Nodes - Initial support of sequence rules for SWITCH node (#1545) - initial support of SORT node (#1500) - Inject node - let once delay be editable (#1541) - - Introduce `nodeMaxMessageBufferLength` setting for msg sequence nodes + - Introduce `nodeMessageBufferMaxLength` setting for msg sequence nodes - Let CSV correct parts if we remove header row. - let default apply if msg.delay not set in override mode. (#1397) - let trigger node be reset by boolean message (#1554) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33a2f582f..f0f4096c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog. At a minimum, please include: - Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly. - - Version of node.js - what does `node -v` say? + - Version of Node.js - what does `node -v` say? ## Feature requests diff --git a/Gruntfile.js b/Gruntfile.js index 8b7b85a25..cbf70f4dc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,6 +16,7 @@ var path = require("path"); var fs = require("fs-extra"); +var sass = require("node-sass"); module.exports = function(grunt) { @@ -25,9 +26,13 @@ module.exports = function(grunt) { nodemonArgs.push(flowFile); } + var browserstack = grunt.option('browserstack'); + if (browserstack) { + process.env.BROWSERSTACK = true; + } var nonHeadless = grunt.option('non-headless'); if (nonHeadless) { - process.env.NODE_RED_NON_HEADLESS = 'true'; + process.env.NODE_RED_NON_HEADLESS = true; } grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -79,20 +84,20 @@ module.exports = function(grunt) { //"loopfunc": true, // allow functions to be defined in loops //"sub": true // don't warn that foo['bar'] should be written as foo.bar }, - all: [ - 'Gruntfile.js', - 'red.js', - 'packages/**/*.js' - ], - core: { - files: { - src: [ - 'Gruntfile.js', - 'red.js', - 'packages/**/*.js', - ] - } - }, + // all: [ + // 'Gruntfile.js', + // 'red.js', + // 'packages/**/*.js' + // ], + // core: { + // files: { + // src: [ + // 'Gruntfile.js', + // 'red.js', + // 'packages/**/*.js', + // ] + // } + // }, nodes: { files: { src: [ 'nodes/core/*/*.js' ] @@ -100,7 +105,7 @@ module.exports = function(grunt) { }, editor: { files: { - src: [ 'editor/js/**/*.js' ] + src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ] } }, tests: { @@ -220,6 +225,7 @@ module.exports = function(grunt) { sass: { build: { options: { + implementation: sass, outputStyle: 'compressed' }, files: [{ @@ -276,7 +282,7 @@ module.exports = function(grunt) { files: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ], - tasks: ['copy:build','concat','uglify','attachCopyright:js'] + tasks: ['copy:build','concat',/*'uglify',*/ 'attachCopyright:js'] }, sass: { files: [ @@ -496,7 +502,9 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-chmod'); grunt.loadNpmTasks('grunt-jsonlint'); grunt.loadNpmTasks('grunt-mocha-istanbul'); - grunt.loadNpmTasks('grunt-webdriver'); + if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { + grunt.loadNpmTasks('grunt-webdriver'); + } grunt.loadNpmTasks('grunt-jsdoc'); grunt.loadNpmTasks('grunt-jsdoc-to-markdown'); grunt.loadNpmTasks('grunt-npm-command'); @@ -555,12 +563,25 @@ module.exports = function(grunt) { }); grunt.registerTask('verifyUiTestDependencies', function() { - if (!fs.existsSync(path.join("node_modules", "chromedriver"))) { - grunt.fail.fatal('You need to run "npm install chromedriver@2" before running UI test.'); + if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { + grunt.fail.fatal('You need to install the UI test dependencies first.\nUse the script in "scripts/install-ui-test-dependencies.sh"'); return false; } }); + grunt.registerTask('generatePublishScript', + 'Generates a script to publish build output to npm', + function () { + const done = this.async(); + const generatePublishScript = require("./scripts/generate-publish-script.js"); + generatePublishScript().then(function(output) { + grunt.log.writeln(output); + const filePath = path.join(grunt.config.get('paths.dist'),"modules","publish.sh"); + grunt.file.write(filePath,output); + + done(); + }); + }); grunt.registerTask('setDevEnv', 'Sets NODE_ENV=development so non-minified assets are used', function () { @@ -579,9 +600,15 @@ module.exports = function(grunt) { 'Runs code style check on editor code', ['jshint:editor']); - grunt.registerTask('test-ui', - 'Builds editor content then runs unit tests on editor ui', - ['verifyUiTestDependencies','build','jshint:editor','webdriver:all']); + if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { + grunt.registerTask('test-ui', + 'Builds editor content then runs unit tests on editor ui', + ['verifyUiTestDependencies']); + } else { + grunt.registerTask('test-ui', + 'Builds editor content then runs unit tests on editor ui', + ['verifyUiTestDependencies','build','jshint:editor','webdriver:all']); + } grunt.registerTask('test-nodes', 'Runs unit tests on core nodes', @@ -597,7 +624,7 @@ module.exports = function(grunt) { grunt.registerTask('release', 'Create distribution zip file', - ['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules']); + ['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules','generatePublishScript']); grunt.registerTask('pack-modules', 'Create module pack files for release', diff --git a/README.md b/README.md index 3d0acb0e3..3baa69377 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ http://nodered.org [![Build Status](https://travis-ci.org/node-red/node-red.svg?branch=master)](https://travis-ci.org/node-red/node-red) [![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master) -A visual tool for wiring the Internet of Things. +Low-code programming for event-driven applications. -![Node-RED: A visual tool for wiring the Internet of Things](http://nodered.org/images/node-red-screenshot.png) +![Node-RED: Low-code programming for event-driven applications](http://nodered.org/images/node-red-screenshot.png) ## Quick Start @@ -56,7 +56,7 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena ## Authors -Node-RED is a project of the [JS Foundation](http://js.foundation). +Node-RED is a project of the [OpenJS Foundation](https://openjsf.org). It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/). @@ -67,4 +67,4 @@ It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-t ## Copyright and license -Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE). +Copyright JS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..a52064d59 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.0.0 | :white_check_mark: | +| 0.20.x | :white_check_mark: | + + +## Reporting a Vulnerability + +Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly. diff --git a/package.json b/package.json index 92bc8020d..a6c4c5091 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "node-red", - "version": "1.0.0-beta.2", - "description": "A visual tool for wiring the Internet of Things", + "version": "1.0.3", + "description": "Low-code programming for event-driven applications", "homepage": "http://nodered.org", "license": "Apache-2.0", "repository": { @@ -24,7 +24,7 @@ } ], "dependencies": { - "ajv": "6.10.0", + "ajv": "6.10.2", "basic-auth": "2.0.1", "bcryptjs": "2.4.3", "body-parser": "1.19.0", @@ -34,29 +34,29 @@ "cookie": "0.4.0", "cookie-parser": "1.4.4", "cors": "2.8.5", - "cron": "1.7.1", + "cron": "1.7.2", "denque": "1.4.1", "express": "4.17.1", - "express-session": "1.16.2", + "express-session": "1.17.0", "fs-extra": "8.1.0", "fs.notify": "0.0.4", "hash-sum": "2.0.0", - "https-proxy-agent": "2.2.1", + "https-proxy-agent": "2.2.4", "i18next": "15.1.2", "iconv-lite": "0.5.0", "is-utf8": "0.2.1", "js-yaml": "3.13.1", "json-stringify-safe": "5.0.1", - "jsonata": "1.6.5", + "jsonata": "1.8.0", "media-typer": "1.1.0", "memorystore": "1.6.1", "mime": "2.4.4", "mqtt": "2.18.8", - "multer": "1.4.1", - "mustache": "3.0.1", - "node-red-node-rbe": "^0.2.4", - "node-red-node-sentiment": "^0.1.3", - "node-red-node-tail": "^0.0.2", + "multer": "1.4.2", + "mustache": "3.0.2", + "node-red-node-rbe": "^0.2.6", + "node-red-node-sentiment": "^0.1.6", + "node-red-node-tail": "^0.1.0", "nopt": "4.0.1", "oauth2orize": "1.11.0", "on-headers": "1.0.2", @@ -65,11 +65,11 @@ "passport-oauth2-client-password": "0.1.2", "raw-body": "2.4.1", "request": "2.88.0", - "semver": "6.2.0", - "uglify-js": "3.6.0", + "semver": "6.3.0", + "uglify-js": "3.6.9", "when": "3.7.8", "ws": "6.2.1", - "xml2js": "0.4.19" + "xml2js": "0.4.22" }, "optionalDependencies": { "bcrypt": "3.0.6" @@ -79,38 +79,34 @@ "grunt-chmod": "~1.1.1", "grunt-cli": "~1.3.2", "grunt-concurrent": "~2.3.1", - "grunt-contrib-clean": "~1.1.0", - "grunt-contrib-compress": "~1.4.0", + "grunt-contrib-clean": "~2.0.0", + "grunt-contrib-compress": "~1.5.0", "grunt-contrib-concat": "~1.0.1", "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-jshint": "~1.1.0", - "grunt-contrib-uglify": "~3.4.0", + "grunt-contrib-jshint": "~2.1.0", + "grunt-contrib-uglify": "~4.0.1", "grunt-contrib-watch": "~1.1.0", "grunt-jsdoc": "^2.2.1", "grunt-jsdoc-to-markdown": "^4.0.0", - "grunt-jsonlint": "~1.1.0", + "grunt-jsonlint": "~2.0.0", "grunt-mkdir": "~1.0.0", "grunt-mocha-istanbul": "5.0.2", "grunt-nodemon": "~0.4.2", "grunt-npm-command": "~0.1.2", - "grunt-sass": "~2.0.0", + "grunt-sass": "~3.1.0", "grunt-simple-mocha": "~0.4.1", - "grunt-webdriver": "^2.0.3", - "http-proxy": "^1.16.2", + "http-proxy": "1.18.0", "istanbul": "0.4.5", + "jsdoc-nr-template": "github:node-red/jsdoc-nr-template", "minami": "1.2.3", "mocha": "^5.2.0", "mosca": "^2.8.3", + "node-red-node-test-helper": "^0.2.3", + "node-sass": "^4.13.0", "should": "^8.4.0", "sinon": "1.17.7", "stoppable": "^1.1.0", - "supertest": "3.4.2", - "wdio-chromedriver-service": "^0.1.5", - "wdio-mocha-framework": "^0.6.4", - "wdio-spec-reporter": "^0.1.5", - "webdriverio": "^4.14.1", - "node-red-node-test-helper": "^0.2.3", - "jsdoc-nr-template": "node-red/jsdoc-nr-template" + "supertest": "3.4.2" }, "engines": { "node": ">=8" diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/context.js b/packages/node_modules/@node-red/editor-api/lib/admin/context.js index 6a2efd82d..54bfd9f85 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/context.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/context.js @@ -30,7 +30,8 @@ module.exports = { scope: req.params.scope, id: req.params.id, key: req.params[0], - store: req.query['store'] + store: req.query['store'], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.context.getValue(opts).then(function(result) { res.json(result); @@ -45,7 +46,8 @@ module.exports = { scope: req.params.scope, id: req.params.id, key: req.params[0], - store: req.query['store'] + store: req.query['store'], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.context.delete(opts).then(function(result) { res.status(204).end(); diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/flow.js b/packages/node_modules/@node-red/editor-api/lib/admin/flow.js index 5ba5d7a04..98ae997ff 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/flow.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/flow.js @@ -24,7 +24,8 @@ module.exports = { get: function(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.flows.getFlow(opts).then(function(result) { return res.json(result); @@ -35,7 +36,8 @@ module.exports = { post: function(req,res) { var opts = { user: req.user, - flow: req.body + flow: req.body, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.flows.addFlow(opts).then(function(id) { return res.json({id:id}); @@ -47,7 +49,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - flow: req.body + flow: req.body, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.flows.updateFlow(opts).then(function(id) { return res.json({id:id}); @@ -58,7 +61,8 @@ module.exports = { delete: function(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.flows.deleteFlow(opts).then(function() { res.status(204).end(); diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/flows.js b/packages/node_modules/@node-red/editor-api/lib/admin/flows.js index ccad8718f..11b30e446 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/flows.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/flows.js @@ -27,7 +27,8 @@ module.exports = { return res.status(400).json({code:"invalid_api_version", message:"Invalid API Version requested"}); } var opts = { - user: req.user + user: req.user, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.flows.getFlows(opts).then(function(result) { if (version === "v1") { @@ -46,7 +47,8 @@ module.exports = { } var opts = { user: req.user, - deploymentType: req.get("Node-RED-Deployment-Type")||"full" + deploymentType: req.get("Node-RED-Deployment-Type")||"full", + req: apiUtils.getRequestLogObject(req) } if (opts.deploymentType !== 'reload') { diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/index.js b/packages/node_modules/@node-red/editor-api/lib/admin/index.js index 32bf010c5..50d7b168f 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/index.js @@ -48,13 +48,13 @@ module.exports = { // Nodes adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler); adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler); - adminApp.get(/\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler); - adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler); - adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler); - adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.putModule,apiUtil.errorHandler); - adminApp.delete(/\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.delete,apiUtil.errorHandler); - adminApp.get(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.read"),nodes.getSet,apiUtil.errorHandler); - adminApp.put(/\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.write"),nodes.putSet,apiUtil.errorHandler); + adminApp.get(/^\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler); + adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler); + adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler); + adminApp.put(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.putModule,apiUtil.errorHandler); + adminApp.delete(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.write"),nodes.delete,apiUtil.errorHandler); + adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.read"),nodes.getSet,apiUtil.errorHandler); + adminApp.put(/^\/nodes\/((@[^\/]+\/)?[^\/]+)\/([^\/]+)$/,needsPermission("nodes.write"),nodes.putSet,apiUtil.errorHandler); // Context adminApp.get("/context/:scope(global)",needsPermission("context.read"),context.get,apiUtil.errorHandler); diff --git a/packages/node_modules/@node-red/editor-api/lib/admin/nodes.js b/packages/node_modules/@node-red/editor-api/lib/admin/nodes.js index 59a137587..b78de9d75 100644 --- a/packages/node_modules/@node-red/editor-api/lib/admin/nodes.js +++ b/packages/node_modules/@node-red/editor-api/lib/admin/nodes.js @@ -24,7 +24,8 @@ module.exports = { }, getAll: function(req,res) { var opts = { - user: req.user + user: req.user, + req: apiUtils.getRequestLogObject(req) } if (req.get("accept") == "application/json") { runtimeAPI.nodes.getNodeList(opts).then(function(list) { @@ -42,7 +43,9 @@ module.exports = { var opts = { user: req.user, module: req.body.module, - version: req.body.version + version: req.body.version, + url: req.body.url, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.addModule(opts).then(function(info) { res.json(info); @@ -54,7 +57,8 @@ module.exports = { delete: function(req,res) { var opts = { user: req.user, - module: req.params[0] + module: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.removeModule(opts).then(function() { res.status(204).end(); @@ -66,7 +70,8 @@ module.exports = { getSet: function(req,res) { var opts = { user: req.user, - id: req.params[0] + "/" + req.params[2] + id: req.params[0] + "/" + req.params[2], + req: apiUtils.getRequestLogObject(req) } if (req.get("accept") === "application/json") { runtimeAPI.nodes.getNodeInfo(opts).then(function(result) { @@ -87,7 +92,8 @@ module.exports = { getModule: function(req,res) { var opts = { user: req.user, - module: req.params[0] + module: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.getModuleInfo(opts).then(function(result) { res.send(result); @@ -106,7 +112,8 @@ module.exports = { var opts = { user: req.user, id: req.params[0] + "/" + req.params[2], - enabled: body.enabled + enabled: body.enabled, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.setNodeSetState(opts).then(function(result) { res.send(result); @@ -125,7 +132,8 @@ module.exports = { var opts = { user: req.user, module: req.params[0], - enabled: body.enabled + enabled: body.enabled, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.setModuleState(opts).then(function(result) { res.send(result); @@ -139,7 +147,8 @@ module.exports = { var opts = { user: req.user, module: req.params[0], - lang: req.query.lng + lang: req.query.lng, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.getModuleCatalog(opts).then(function(result) { res.json(result); @@ -152,7 +161,8 @@ module.exports = { getModuleCatalogs: function(req,res) { var opts = { user: req.user, - lang: req.query.lng + lang: req.query.lng, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.getModuleCatalogs(opts).then(function(result) { res.json(result); @@ -164,7 +174,8 @@ module.exports = { getIcons: function(req,res) { var opts = { - user: req.user + user: req.user, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.nodes.getIconList(opts).then(function(list) { res.json(list); diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/index.js b/packages/node_modules/@node-red/editor-api/lib/editor/index.js index 3628fa702..a9cdf0ffc 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/index.js @@ -88,13 +88,13 @@ module.exports = { // Locales var locales = require("./locales"); locales.init(runtimeAPI); - editorApp.get(/locales\/(.+)\/?$/,locales.get,apiUtil.errorHandler); + editorApp.get(/^\/locales\/(.+)\/?$/,locales.get,apiUtil.errorHandler); // Library var library = require("./library"); library.init(runtimeAPI); - editorApp.get(/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry); - editorApp.post(/library\/([^\/]+)\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry); + editorApp.get(/^\/library\/([^\/]+)\/([^\/]+)(?:$|\/(.*))/,needsPermission("library.read"),library.getEntry); + editorApp.post(/^\/library\/([^\/]+)\/([^\/]+)\/(.*)/,needsPermission("library.write"),library.saveEntry); // Credentials diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/locales.js b/packages/node_modules/@node-red/editor-api/lib/editor/locales.js index 5e10647e7..a7f300cd3 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/locales.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/locales.js @@ -15,7 +15,7 @@ **/ var fs = require('fs'); var path = require('path'); -//var apiUtil = require('../util'); +// var apiUtil = require('../util'); var i18n = require("@node-red/util").i18n; // TODO: separate module @@ -41,7 +41,7 @@ module.exports = { var namespace = req.params[0]; var lngs = req.query.lng; namespace = namespace.replace(/\.json$/,""); - var lang = req.query.lng; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []); + var lang = req.query.lng || i18n.defaultLang; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []); var prevLang = i18n.i.language; // Trigger a load from disk of the language if it is not the default i18n.i.changeLanguage(lang, function(){ diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/projects.js b/packages/node_modules/@node-red/editor-api/lib/editor/projects.js index ff7fc5e85..0849c8ff8 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/projects.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/projects.js @@ -22,7 +22,8 @@ var needsPermission = require("../auth").needsPermission; function listProjects(req,res) { var opts = { - user: req.user + user: req.user, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.listProjects(opts).then(function(result) { res.json(result); @@ -33,7 +34,8 @@ function listProjects(req,res) { function getProject(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getProject(opts).then(function(data) { if (data) { @@ -49,7 +51,8 @@ function getProjectStatus(req,res) { var opts = { user: req.user, id: req.params.id, - remote: req.query.remote + remote: req.query.remote, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getStatus(opts).then(function(data){ if (data) { @@ -64,7 +67,8 @@ function getProjectStatus(req,res) { function getProjectRemotes(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getRemotes(opts).then(function(data) { res.json(data); @@ -98,7 +102,8 @@ module.exports = { app.post("/", needsPermission("projects.write"), function(req,res) { var opts = { user: req.user, - project: req.body + project: req.body, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.createProject(opts).then(function(result) { res.json(result); @@ -112,7 +117,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - project: req.body + project: req.body, + req: apiUtils.getRequestLogObject(req) } if (req.body.active) { @@ -150,7 +156,8 @@ module.exports = { app.delete("/:id", needsPermission("projects.write"), function(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.deleteProject(opts).then(function() { res.status(204).end(); @@ -168,7 +175,8 @@ module.exports = { app.get("/:id/files", needsPermission("projects.read"), function(req,res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getFiles(opts).then(function(data) { res.json(data); @@ -185,7 +193,8 @@ module.exports = { user: req.user, id: req.params.id, path: req.params[0], - tree: req.params.treeish + tree: req.params.treeish, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getFile(opts).then(function(data) { res.json({content:data}); @@ -199,7 +208,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - path: req.params[0] + path: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.revertFile(opts).then(function() { @@ -214,7 +224,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - path: req.params[0] + path: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.stageFile(opts).then(function() { getProjectStatus(req,res); @@ -228,7 +239,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - path: req.body.files + path: req.body.files, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.stageFile(opts).then(function() { getProjectStatus(req,res); @@ -242,7 +254,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - message: req.body.message + message: req.body.message, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.commit(opts).then(function() { getProjectStatus(req,res); @@ -256,7 +269,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - path: req.params[0] + path: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.unstageFile(opts).then(function() { getProjectStatus(req,res); @@ -269,7 +283,8 @@ module.exports = { app.delete("/:id/stage", needsPermission("projects.write"), function(req, res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.unstageFile(opts).then(function() { getProjectStatus(req,res); @@ -284,7 +299,8 @@ module.exports = { user: req.user, id: req.params.id, path: req.params[0], - type: req.params.type + type: req.params.type, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getFileDiff(opts).then(function(data) { res.json({ @@ -301,7 +317,8 @@ module.exports = { user: req.user, id: req.params.id, limit: req.query.limit || 20, - before: req.query.before + before: req.query.before, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getCommits(opts).then(function(data) { res.json(data); @@ -315,7 +332,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - sha: req.params.sha + sha: req.params.sha, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getCommit(opts).then(function(data) { res.json({commit:data}); @@ -330,7 +348,8 @@ module.exports = { user: req.user, id: req.params.id, remote: req.params[0], - track: req.query.u + track: req.query.u, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.push(opts).then(function(data) { res.status(204).end(); @@ -346,7 +365,8 @@ module.exports = { id: req.params.id, remote: req.params[0], track: req.query.setUpstream, - allowUnrelatedHistories: req.query.allowUnrelatedHistories + allowUnrelatedHistories: req.query.allowUnrelatedHistories, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.pull(opts).then(function(data) { res.status(204).end(); @@ -359,7 +379,8 @@ module.exports = { app.delete("/:id/merge", needsPermission("projects.write"), function(req, res) { var opts = { user: req.user, - id: req.params.id + id: req.params.id, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.abortMerge(opts).then(function() { res.status(204).end(); @@ -374,7 +395,8 @@ module.exports = { user: req.user, id: req.params.id, path: req.params[0], - resolution: req.body.resolutions + resolution: req.body.resolutions, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.resolveMerge(opts).then(function() { res.status(204).end(); @@ -388,7 +410,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - remote: false + remote: false, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getBranches(opts).then(function(data) { res.json(data); @@ -403,7 +426,8 @@ module.exports = { user: req.user, id: req.params.id, branch: req.params.branchName, - force: !!req.query.force + force: !!req.query.force, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.deleteBranch(opts).then(function(data) { res.status(204).end(); @@ -417,7 +441,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - remote: true + remote: true, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getBranches(opts).then(function(data) { res.json(data); @@ -431,7 +456,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - branch: req.params[0] + branch: req.params[0], + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.getBranchStatus(opts).then(function(data) { res.json(data); @@ -446,7 +472,8 @@ module.exports = { user: req.user, id: req.params.id, branch: req.body.name, - create: req.body.create + create: req.body.create, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.setBranch(opts).then(function(data) { res.json(data); @@ -463,7 +490,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - remote: req.body + remote: req.body, + req: apiUtils.getRequestLogObject(req) } if (/^https?:\/\/[^/]+@/i.test(req.body.url)) { res.status(400).json({error:"unexpected_error", message:"Git http url must not include username/password"}); @@ -481,7 +509,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - remote: req.params.remoteName + remote: req.params.remoteName, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.removeRemote(opts).then(function(data) { getProjectRemotes(req,res); @@ -497,7 +526,8 @@ module.exports = { var opts = { user: req.user, id: req.params.id, - remote: remote + remote: remote, + req: apiUtils.getRequestLogObject(req) } runtimeAPI.projects.updateRemote(opts).then(function() { res.status(204).end(); diff --git a/packages/node_modules/@node-red/editor-api/lib/index.js b/packages/node_modules/@node-red/editor-api/lib/index.js index 10c9912ed..0dc06ab71 100644 --- a/packages/node_modules/@node-red/editor-api/lib/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/index.js @@ -42,7 +42,7 @@ var editor; /** * Initialise the module. * @param {Object} settings The runtime settings - * @param {HTTPServer} server An instance of HTTP Server + * @param {HTTPServer} _server An instance of HTTP Server * @param {Storage} storage An instance of Node-RED Storage * @param {Runtime} runtimeAPI An instance of Node-RED Runtime * @memberof @node-red/editor-api diff --git a/packages/node_modules/@node-red/editor-api/lib/util.js b/packages/node_modules/@node-red/editor-api/lib/util.js index 1984bd5f1..0cef96bbb 100644 --- a/packages/node_modules/@node-red/editor-api/lib/util.js +++ b/packages/node_modules/@node-red/editor-api/lib/util.js @@ -47,5 +47,12 @@ module.exports = { code: err.code||"unexpected_error", message: err.message||err.toString() }); + }, + getRequestLogObject: function(req) { + return { + user: req.user, + path: req.path, + ip: (req.headers && req.headers['x-forwarded-for']) || (req.connection && req.connection.remoteAddress) || undefined + } } } diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index e4b3b1f69..824f4df5b 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "1.0.0-beta.2", + "version": "1.0.3", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,17 +16,17 @@ } ], "dependencies": { - "@node-red/util": "1.0.0-beta.2", - "@node-red/editor-client": "1.0.0-beta.2", + "@node-red/util": "1.0.3", + "@node-red/editor-client": "1.0.3", "bcryptjs": "2.4.3", "body-parser": "1.19.0", "clone": "2.1.2", "cors": "2.8.5", - "express-session": "1.16.2", + "express-session": "1.17.0", "express": "4.17.1", "memorystore": "1.6.1", "mime": "2.4.4", - "mustache": "3.0.1", + "mustache": "3.0.2", "oauth2orize": "1.11.0", "passport-http-bearer": "1.0.1", "passport-oauth2-client-password": "0.1.2", diff --git a/packages/node_modules/@node-red/editor-client/locales/de/editor.json b/packages/node_modules/@node-red/editor-client/locales/de/editor.json index 05ccfbed2..c7394c347 100755 --- a/packages/node_modules/@node-red/editor-client/locales/de/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/de/editor.json @@ -26,8 +26,7 @@ "status" : "Status", "enabled" : "Aktiviert", "disabled" : "Inaktiviert", - "info" : "Beschreibung", - "tip" : "Beschreibung akzeptiert Markdown und wird auf der Registerkarte Info angezeigt." + "info" : "Beschreibung" }, "menu" : { "label" : { @@ -237,7 +236,6 @@ "deleteSubflow" : "Subflow löschen", "info" : "Beschreibung", "category" : "Kategorie", - "format" : "Markdown-Format", "errors" : { "noNodesSelected" : " Subflow kann nicht erstellt werden : Es wurden keine Nodes ausgewählt.", "multipleInputsToSelection" : " Subflow kann nicht erstellt werden : Mehrere Eingaben zur Auswahl" @@ -446,8 +444,8 @@ "none" : "keine", "subflows" : "Subflows", "flows" : "Flows", - "filterUnused" : "Nicht verwendet", "filterAll" : "alle", + "filterUnused" : "Nicht verwendet", "filtered" : "__count__ verdeckt" }, "context" : { diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 9a53a870c..7977a5752 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -15,6 +15,17 @@ "next": "Next", "clone": "Clone project", "cont": "Continue" + }, + "type": { + "string": "string", + "number": "number", + "boolean": "boolean", + "array": "array", + "buffer": "buffer", + "object": "object", + "jsonString": "JSON string", + "undefined": "undefined", + "null": "null" } }, "workspace": { @@ -310,10 +321,12 @@ "addNewType": "Add new __type__...", "nodeProperties": "node properties", "label": "Label", + "color": "Color", "portLabels": "Port labels", "labelInputs": "Inputs", "labelOutputs": "Outputs", "settingIcon": "Icon", + "default": "default", "noDefaultLabel": "none", "defaultLabel": "use default label", "searchIcons": "Search icons", @@ -321,6 +334,40 @@ "description": "Description", "show": "Show", "hide": "Hide", + "locale": "Select UI Language", + "icon": "Icon", + "inputType": "Input type", + "inputs" : { + "input": "input", + "select": "select", + "checkbox": "checkbox", + "spinner": "spinner", + "none": "none", + "hidden": "hide property" + }, + "types": { + "str": "string", + "num": "number", + "bool": "bool", + "json": "JSON", + "bin": "buffer", + "env": "env variable" + }, + "menu": { + "input": "input", + "select": "select", + "checkbox": "checkbox", + "spinner": "spinner", + "hidden": "label only" + }, + "select": { + "label": "Label", + "value": "Value" + }, + "spinner": { + "min": "Minimum", + "max": "Maximum" + }, "errors": { "scopeChange": "Changing the scope will make it unavailable to nodes in other flows that use it", "invalidProperties": "Invalid properties:" @@ -379,9 +426,13 @@ "addCategory": "Add new...", "label": { "subflows": "subflows", + "network": "network", + "common": "common", "input": "input", "output": "output", "function": "function", + "sequence": "sequence", + "parser": "parser", "social": "social", "storage": "storage", "analysis": "analysis", @@ -518,8 +569,10 @@ "none": "none", "subflows": "subflows", "flows": "flows", - "filterUnused":"unused", - "filterAll":"all", + "filterAll": "all", + "showAllConfigNodes": "Show all config nodes", + "filterUnused": "unused", + "showAllUnusedConfigNodes": "Show all unused config nodes", "filtered": "__count__ hidden" }, "context": { @@ -532,7 +585,7 @@ "flow": "Flow", "global": "Global", "deleteConfirm": "Are you sure you want to delete this item?", - "autoRefresh": "Auto-refresh", + "autoRefresh": "Refresh on selection change", "refrsh": "Refresh", "delete": "Delete" }, @@ -549,6 +602,7 @@ "noSummaryAvailable": "No summary available", "editDescription": "Edit project description", "editDependencies": "Edit project dependencies", + "noDescriptionAvailable": "No description available", "editReadme": "Edit README.md", "showProjectSettings": "Show project settings", "projectSettings": { @@ -703,7 +757,8 @@ "bin": "buffer", "date": "timestamp", "jsonata": "expression", - "env": "env variable" + "env": "env variable", + "cred": "credential" } }, "editableList": { @@ -749,10 +804,14 @@ "copyPath": "Copy path to item", "expandItems": "Expand items", "collapseItems": "Collapse items", - "duplicate": "Duplicate" + "duplicate": "Duplicate", + "error": { + "invalidJSON": "Invalid JSON: " + } }, "markdownEditor": { "title": "Markdown editor", + "expand": "Expand", "format": "Formatted with markdown", "heading1": "Heading 1", "heading2": "Heading 2", @@ -939,15 +998,18 @@ }, "editor-tab": { "properties": "Properties", + "envProperties": "Environment Variables", "description": "Description", "appearance": "Appearance", - "env": "Environment Variables" + "preview": "UI Preview", + "defaultValue": "Default value" }, "languages" : { "de": "German", "en-US": "English", "ja": "Japanese", "ko": "Korean", - "zh-CN": "Chinese(Simplified)" + "zh-CN": "Chinese(Simplified)", + "zh-TW": "Chinese(Traditional)" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json index 9c33e8d6d..d777d1919 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/jsonata.json @@ -1,7 +1,7 @@ { "$string": { - "args": "arg", - "desc": "Casts the *arg* parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function" + "args": "arg[, prettify]", + "desc": "Casts the `arg` parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function. If `prettify` is true, then \"prettified\" JSON is produced. i.e One line per field and lines will be indented based on the field depth." }, "$length": { "args": "str", @@ -185,7 +185,7 @@ }, "$reduce": { "args":"array, function [, init]", - "desc":"Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`.\n\nThe optional `init` parameter is used as the initial value in the aggregation." + "desc":"Returns an aggregated value derived from applying the `function` parameter successively to each value in `array` in combination with the result of the previous application of the function.\n\nThe function must accept two arguments, and behaves like an infix operator between each value within the `array`. The signature of `function` must be of the form: `myfunc($accumulator, $value[, $index[, $array]])`\n\nThe optional `init` parameter is used as the initial value in the aggregation." }, "$flowContext": { "args": "string[, string]", @@ -230,6 +230,41 @@ "$parseInteger": { "args": "string, picture", "desc": "Parses the contents of the `string` parameter to an integer (as a JSON number) using the format specified by the `picture` string. The `picture` string parameter has the same format as `$formatInteger`." - + }, + "$error": { + "args": "[str]", + "desc": "Throws an error with a message. The optional `str` will replace the default message of `$error() function evaluated`" + }, + "$assert": { + "args": "arg, str", + "desc": "If `arg` is true the function returns undefined. If `arg` is false an exception is thrown with `str` as the message of the exception." + }, + "$single": { + "args": "array, function", + "desc": "Returns the one and only value in the `array` parameter that satisfies the `function` predicate (i.e. the `function` returns Boolean `true` when passed the value). Throws an exception if the number of matching values is not exactly one.\n\nThe function should be supplied in the following signature: `function(value [, index [, array]])` where value is each input of the array, index is the position of that value and the whole array is passed as the third argument" + }, + "$encodeUrl": { + "args": "str", + "desc": "Encodes a Uniform Resource Locator (URL) component by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.\n\nExample: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" + }, + "$encodeUrlComponent": { + "args": "str", + "desc": "Encodes a Uniform Resource Locator (URL) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character. \n\nExample: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" + }, + "$decodeUrl": { + "args": "str", + "desc": "Decodes a Uniform Resource Locator (URL) component previously created by encodeUrlComponent. \n\nExample: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" + }, + "$decodeUrlComponent": { + "args": "str", + "desc": "Decodes a Uniform Resource Locator (URL) previously created by encodeUrl. \n\nExample: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" + }, + "$distinct": { + "args": "array", + "desc": "Returns an array with duplicate values removed from `array`" + }, + "$type": { + "args": "value", + "desc": "Returns the type of `value` as a string. If `value` is undefined, this will return `undefined`" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 770c75589..cf04ced1d 100755 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -15,6 +15,17 @@ "next": "進む", "clone": "プロジェクトをクローン", "cont": "続ける" + }, + "type": { + "string": "文字列", + "number": "数値", + "boolean": "真偽値", + "array": "配列", + "buffer": "バッファ", + "object": "オブジェクト", + "jsonString": "JSON文字列", + "undefined": "undefined", + "null": "null" } }, "workspace": { @@ -80,7 +91,7 @@ "projects-new": "新規", "projects-open": "開く", "projects-settings": "設定", - "showNodeLabelDefault": "追加したノードのラベルを表示する" + "showNodeLabelDefault": "追加したノードのラベルを表示" } }, "actions": { @@ -310,10 +321,12 @@ "addNewType": "新規に __type__ を追加...", "nodeProperties": "プロパティ", "label": "ラベル", + "color": "色", "portLabels": "ポートラベル", "labelInputs": "入力", "labelOutputs": "出力", "settingIcon": "アイコン", + "default": "デフォルト", "noDefaultLabel": "なし", "defaultLabel": "既定のラベルを使用", "searchIcons": "アイコンを検索", @@ -321,6 +334,40 @@ "description": "詳細", "show": "表示", "hide": "非表示", + "locale": "UI言語の選択", + "icon": "記号", + "inputType": "入力形式", + "inputs": { + "input": "入力", + "select": "メニュー", + "checkbox": "チェックボックス", + "spinner": "スピナー", + "none": "無し", + "hidden": "非表示" + }, + "types": { + "str": "文字列", + "num": "数値", + "bool": "真偽", + "json": "JSON", + "bin": "バッファ", + "env": "環境変数" + }, + "menu": { + "input": "入力", + "select": "選択", + "checkbox": "チェックボックス", + "spinner": "数値", + "hidden": "ラベルのみ" + }, + "select": { + "label": "ラベル", + "value": "値" + }, + "spinner": { + "min": "最小値", + "max": "最大値" + }, "errors": { "scopeChange": "スコープの変更は、他のフローで使われているノードを無効にします", "invalidProperties": "プロパティが不正です:" @@ -351,7 +398,8 @@ "pasteNode": "ノードを貼り付け", "undoChange": "変更操作を戻す", "searchBox": "ノードを検索", - "managePalette": "パレットの管理" + "managePalette": "パレットの管理", + "actionList": "動作一覧" }, "library": { "library": "ライブラリ", @@ -378,9 +426,13 @@ "addCategory": "新規追加...", "label": { "subflows": "サブフロー", + "network": "ネットワーク", + "common": "共通", "input": "入力", "output": "出力", "function": "機能", + "sequence": "シーケンス", + "parser": "パーサ", "social": "ソーシャル", "storage": "ストレージ", "analysis": "分析", @@ -517,8 +569,10 @@ "none": "なし", "subflows": "サブフロー", "flows": "フロー", - "filterUnused": "未使用", "filterAll": "全て", + "showAllConfigNodes": "全設定ノードを表示", + "filterUnused": "未使用", + "showAllUnusedConfigNodes": "未使用の全設定ノードを表示", "filtered": "__count__ 個が無効" }, "context": { @@ -527,11 +581,13 @@ "none": "選択されていません", "refresh": "読み込みのため更新してください", "empty": "データが存在しません", - "node": "Node", - "flow": "Flow", - "global": "Global", + "node": "ノード", + "flow": "フロー", + "global": "グローバル", "deleteConfirm": "データを削除しても良いですか?", - "autoRefresh": "自動更新" + "autoRefresh": "選択対象が変化した場合更新", + "refrsh": "更新", + "delete": "削除" }, "palette": { "name": "パレットの管理", @@ -543,9 +599,10 @@ "description": "詳細", "dependencies": "依存関係", "settings": "設定", - "noSummaryAvailable": "サマリが存在しません", + "noSummaryAvailable": "要約が存在しません", "editDescription": "プロジェクトの詳細を編集", "editDependencies": "プロジェクトの依存関係を編集", + "noDescriptionAvailable": "詳細が存在しません", "editReadme": "README.mdを編集", "showProjectSettings": "プロジェクト設定を表示", "projectSettings": { @@ -736,10 +793,23 @@ }, "jsonEditor": { "title": "JSONエディタ", - "format": "JSONフォーマット" + "format": "JSONフォーマット", + "rawMode": "JSONを編集", + "uiMode": "ビジュアルエディタ", + "insertAbove": "上に挿入", + "insertBelow": "下に挿入", + "addItem": "要素を追加", + "copyPath": "要素のパスをコピー", + "expandItems": "要素を展開", + "collapseItems": "要素を折り畳む", + "duplicate": "複製", + "error": { + "invalidJSON": "不正なJSON: " + } }, "markdownEditor": { "title": "マークダウンエディタ", + "expand": "拡大", "format": "マークダウン形式で記述", "heading1": "見出しレベル1", "heading2": "見出しレベル2", @@ -900,7 +970,7 @@ "confirm": "

デプロイされていない変更は失われます。

続けますか?

" }, "send-req": { - "auth-req": "リポジトリ対する認証が必要です", + "auth-req": "リポジトリに対する認証が必要です", "username": "ユーザ名", "password": "パスワード", "passphrase": "パスフレーズ", @@ -926,15 +996,18 @@ }, "editor-tab": { "properties": "プロパティ", + "envProperties": "環境変数", "description": "説明", "appearance": "外観", - "env": "環境変数" + "preview": "UIプレビュー", + "defaultValue": "デフォルト値" }, - "languages" : { + "languages": { "de": "ドイツ語", "en-US": "英語", "ja": "日本語", "ko": "韓国語", - "zh-CN": "中国語(簡体)" + "zh-CN": "中国語(簡体)", + "zh-TW": "中国語(繁体)" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/ja/jsonata.json index 14cadfecf..659cf66df 100755 --- a/packages/node_modules/@node-red/editor-client/locales/ja/jsonata.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/jsonata.json @@ -1,7 +1,7 @@ { "$string": { - "args": "arg", - "desc": "以下の型変換ルールを用いて、引数 *arg* を文字列へ型変換します。:\n\n - 文字列は変換しません。\n - 関数は空の文字列に変換します。\n - JSONの数値として表現できないため、無限大やNaNはエラーになります。\n - 他の値は `JSON.stringify` 関数を用いて、JSONの文字列へ変換します。" + "args": "arg[, prettify]", + "desc": "以下の型変換ルールを用いて、引数 *arg* を文字列へ型変換します。:\n\n - 文字列は変換しません。\n - 関数は空の文字列に変換します。\n - JSONの数値として表現できないため、無限大やNaNはエラーになります。\n - 他の値は `JSON.stringify` 関数を用いて、JSONの文字列へ変換します。`prettify`が真の場合、JSONを整形出力します。フィールドを1行毎に出力。フィールドのネスト深さによってインデントを行います。" }, "$length": { "args": "str", @@ -185,7 +185,7 @@ }, "$reduce": { "args": "array, function [, init]", - "desc": "配列の各要素値に関数 `function` を連続的に適用して得られる集約値を返します。 `function` の適用の際には、直前の `function` の適用結果と要素値が引数として与えられます。\n\n関数 `function` は引数を2つ取り、配列の各要素の間に配置する中置演算子のように作用しなくてはなりません。\n\n任意の引数 `init` には、集約時の初期値を設定します。" + "desc": "配列の各要素値に関数 `function` を連続的に適用して得られる集約値を返します。 `function` の適用の際には、直前の `function` の適用結果と要素値が引数として与えられます。\n\n関数 `function` は引数を2つ取り、配列の各要素の間に配置する中置演算子のように作用しなくてはなりません。関数`function`のシグネチャは`myfunc($accumulator, $value[, $index[, $array]])`という形式でなければなりません。\n\n任意の引数 `init` には、集約時の初期値を設定します。" }, "$flowContext": { "args": "string", @@ -230,5 +230,41 @@ "$parseInteger": { "args": "string, picture", "desc": "`picture`文字列の指定に従って、`string`パラメータを整数(JSON数値)に変換します。`picture`文字列は`$formatInteger`と同じ形式です。" + }, + "$error": { + "args": "[str]", + "desc": "メッセージを指定して例外を送出します。メッセージ`str`を省略した場合は`$error() function evaluated`をメッセージとします。" + }, + "$assert": { + "args": "arg, str", + "desc": "`arg`が真の場合、undefinedを返します。偽の場合、`str`をメッセージとする例外を送出します。" + }, + "$single": { + "args": "array, function", + "desc": "`array`の要素のうち、条件判定関数`function`を満たす(`function`に与えた場合に真偽値`true`を返す)要素が1つのみである場合、それを返します。マッチする要素が1つのみでない場合、例外を送出します。\n\n指定する関数は`function(value [, index [, array]])`というシグネチャでなければなりません。ここで、`value`は`array`の要素値、`index`は要素の添字、第三引数には配列全体を渡します。" + }, + "$encodeUrl": { + "args": "str", + "desc": "Uniform Resource Locator (URL)を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" + }, + "$encodeUrlComponent": { + "args": "str", + "desc": "Uniform Resource Locator (URL)要素を構成する文字を1、2、3、もしくは、4文字エスケープシーケンスのUTF-8文字エンコーディングで置換します。\n\n例: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" + }, + "$decodeUrl": { + "args": "str", + "desc": "encodeUrlComponentで置換したUniform Resource Locator (URL)をデコードします。\n\n例: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" + }, + "$decodeUrlComponent": { + "args": "str", + "desc": "encodeUrlで置換したUniform Resource Locator (URL)要素をデコードします。 \n\n例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" + }, + "$distinct": { + "args": "array", + "desc": "配列`array`から重複要素を削除した配列を返します。" + }, + "$type": { + "args": "value", + "desc": "`value` の型を文字列として返します。もし `value` が未定義の場合、 `undefined` が返されます。" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json index 3e7811f53..3c12e116a 100755 --- a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json @@ -273,7 +273,6 @@ "deleteSubflow": "서브 플로우 삭제", "info": "상세내역", "category": "카테고리", - "format": "Markdown 형식", "errors": { "noNodesSelected": "서브 플로우를 생성할 수 없습니다 : 노드가 선택되지 않았습니다", "multipleInputsToSelection": "서브 플로우를 생성할 수 없습니다 : 복수의 입력이 선택되었습니다" @@ -495,8 +494,8 @@ "none": "없음", "subflows": "보조 플로우", "flows": "플로우", - "filterUnused": "미사용", "filterAll": "전체", + "filterUnused": "미사용", "filtered": "__count__ 개 숨김" }, "context": { diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json index f65f428f6..3695eb263 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json @@ -10,7 +10,22 @@ "load": "读取", "save": "保存", "import": "导入", - "export": "导出" + "export": "导出", + "back": "后退", + "next": "下一个", + "clone": "克隆项目", + "cont": "继续" + }, + "type": { + "string": "字符串", + "number": "数字", + "boolean": "布尔值", + "array": "数组", + "buffer": "buffer", + "object": "对象", + "jsonString": "JSON字符串", + "undefined": "为定义", + "null": "空" } }, "workspace": { @@ -19,10 +34,13 @@ "confirmDelete": "确认删除", "delete": "你确定想删除 '__label__'?", "dropFlowHere": "把流程放到这里", + "addFlow": "添加流程", + "listFlows": "流程一览", "status": "状态", "enabled": "有效", "disabled": "无效", - "info": "详细描述" + "info": "详细描述", + "selectNodes": "点击节点来选择" }, "menu": { "label": { @@ -36,11 +54,16 @@ "defaultDir": "默认方向", "ltr": "从左到右", "rtl": "从右到左", - "auto": "上下文" + "auto": "上下文", + "language": "语言", + "browserDefault": "浏览器默认" }, "sidebar": { "show": "显示侧边栏" }, + "palette": { + "show": "显示控制板" + }, "settings": "设置", "userSettings": "用户设置", "nodes": "节点", @@ -60,11 +83,23 @@ "keyboardShortcuts": "键盘快捷方式", "login": "登陆", "logout": "退出", - "editPalette":"节点管理", + "editPalette": "节点管理", "other": "其他", - "showTips": "显示小提示" + "showTips": "显示小提示", + "help": "Node-RED网页", + "projects": "项目", + "projects-new": "新建", + "projects-open": "打开", + "projects-settings": "项目设定", + "showNodeLabelDefault": "显示新添加的节点的标签" } }, + "actions": { + "toggle-navigator": "切换导航器", + "zoom-out": "缩小", + "zoom-reset": "重设缩放", + "zoom-in": "放大" + }, "user": { "loggedInAs": "作为__name__登陆", "username": "账号", @@ -82,29 +117,73 @@ "warning": "警告: __message__", "warnings": { "undeployedChanges": "节点中存在未部署的更改", + "nodeActionDisabled": "节点操作已禁用", "nodeActionDisabledSubflow": "节点动作在子流程中被禁用", "missing-types": "流程由于缺少节点类型而停止。请检查日志的详细信息", - "restartRequired": "Node-RED必须重新启动,以启用升级的模块" + "safe-mode": "

流程以安全模式停止。

您可以修改流程并部署更改以重新启动。

", + "restartRequired": "Node-RED必须重新启动,以启用升级的模块", + "credentials_load_failed": "

由于无法解密凭据,因此流程停止。

流程凭据文件已加密,但是项目的加密密钥丢失或无效。

", + "credentials_load_failed_reset": "

凭据无法解密

流凭据文件已加密,但是项目的加密密钥丢失或无效。

流凭据文件将在下一次部署时重置。任何现有的流凭证将被清除。

", + "missing_flow_file": "

找不到项目流程文件。

该项目未配置流程文件。

", + "missing_package_file": "

找不到项目包文件。

项目缺少package.json文件。

", + "project_empty": "

该项目为空。

是否要创建一组默认的项目文件?
否则,您将必须在编辑器外部手动将文件添加到项目中。

", + "project_not_found": "

未找到项目'__project__'。

", + "git_merge_conflict": "

自动合并更改失败。

修复未合并的冲突,然后提交结果。

" }, - "error": "Error: __message__", + "error": "错误: __message__", "errors": { "lostConnection": "丢失与服务器的连接,重新连接...", "lostConnectionReconnect": "丢失与服务器的连接,__time__秒后重新连接", "lostConnectionTry": "现在尝试", "cannotAddSubflowToItself": "无法向其自身添加子流程", "cannotAddCircularReference": "无法添加子流程 - 循环引用", - "unsupportedVersion": "您正在使用不受支持的Node.js版本
请升级到最新版本的Node.js LTS" + "unsupportedVersion": "您正在使用不受支持的Node.js版本
请升级到最新版本的Node.js LTS", + "failedToAppendNode": "

'__module__'加载失败

__error__

" + }, + "project": { + "change-branch": "转到本地分支'__project__'", + "merge-abort": "Git合并中止", + "loaded": "项目'__project__'已加载", + "updated": "项目'__project__'已更新", + "pull": "项目'__project__'已重新加载", + "revert": "项目 '__project__'已还原", + "merge-complete": "Git合并完成", + "setupCredentials": "设定证书", + "setupProjectFiles": "设置项目文件", + "no": "不了,谢谢", + "createDefault": "创建默认项目文件", + "mergeConflict": "显示合并冲突" + }, + "label": { + "manage-project-dep": "管理项目依赖性", + "setup-cred": "设定证书", + "setup-project": "设置项目文件", + "create-default-package": "创建默认的包文件", + "no-thanks": "不了,谢谢", + "create-default-project": "创建默认项目文件", + "show-merge-conflicts": "显示合并冲突" } }, "clipboard": { "clipboard": "剪贴板", "nodes": "节点", + "node": "__count__节点", + "node_plural": "__count__节点", + "configNode": "__count__配置节点", + "configNode_plural": "__count__配置节点", + "flow": "__count__流程", + "flow_plural": "__count__流程", + "subflow": "__count__子流程", + "subflow_plural": "__count__子流程", "pasteNodes": "在这里粘贴节点", + "selectFile": "选择要导入的文件", "importNodes": "导入节点", "exportNodes": "导出节点至剪贴板", + "download": "下载", "importUnrecognised": "导入了无法识别的类型:", "importUnrecognised_plural": "导入了无法识别的类型:", "nodesExported": "节点导出到了剪贴板", + "nodesImported": "导入:", "nodeCopied": "已复制__count__个节点", "nodeCopied_plural": "已复制__count__个节点", "invalidFlow": "无效的流程: __message__", @@ -114,11 +193,21 @@ "all": "所有流程", "compact": "紧凑", "formatted": "已格式化", - "copy": "导出到剪贴板" + "copy": "导出到剪贴板", + "export": "到处到库", + "exportAs": "导出为", + "overwrite": "替换", + "exists": "

\"__file__\"已存在

是否要替换它?

" }, "import": { "import": "导入到", - "newFlow": "新流程" + "newFlow": "新流程", + "errors": { + "notArray": "输入的不是JSON数组", + "itemNotObject": "输入的流无效 - 项目__index__不是节点对象", + "missingId": "输入的流无效-项 __index__ 缺少'id'属性", + "missingType": "输入的流程无效-项__index__缺少'类型'属性" + } }, "copyMessagePath": "已复制路径", "copyMessageValue": "已复制数值", @@ -132,7 +221,10 @@ "modifiedFlowsDesc": "只部署包含已更改节点的流", "modifiedNodes": "已更改的节点", "modifiedNodesDesc": "只部署已经更改的节点", + "restartFlows": "重启流程", + "restartFlowsDesc": "重新启动当前部署的流程", "successfulDeploy": "部署成功", + "successfulRestart": "成功重启流程", "deployFailed": "部署失败: __message__", "unusedConfigNodes": "您有一些未使用的配置节点", "unusedConfigNodesLink": "点击此处查看它们", @@ -152,16 +244,24 @@ "improperlyConfigured": "工作区包含一些未正确配置的节点:", "unknown": "工作区包含一些未知的节点类型:", "confirm": "你确定要部署吗?", + "doNotWarn": "不要再对此发出警告", "conflict": "服务器正在运行较新的一组流程。", "backgroundUpdate": "服务器上的流程已更新。", "conflictChecking": "检查是否可以自动合并更改", "conflictAutoMerge": "此更改不包括冲突,可以自动合并", - "conflictManualMerge": "这些更改包括了在部署之前必须解决的冲突。" + "conflictManualMerge": "这些更改包括了在部署之前必须解决的冲突。", + "plusNMore": "+ __count__更多" } }, + "eventLog": { + "title": "事件记录日志", + "view": "查看日志" + }, "diff": { "unresolvedCount": "__count__个未解决的冲突", "unresolvedCount_plural": "__count__个未解决的冲突", + "globalNodes": "全局节点", + "flowProperties": "流程属性", "type": { "added": "已添加", "changed": "已更改", @@ -175,9 +275,19 @@ "nodeCount": "__count__个节点", "nodeCount_plural": "__count__个节点", "local": "本地", - "remote": "远程" + "remote": "远程", + "reviewChanges": "查看变更", + "noBinaryFileShowed": "无法显示二进制文件内容", + "viewCommitDiff": "查看提交更改", + "compareChanges": "比较变更", + "saveConflict": "保存冲突解决", + "conflictHeader": "已解决__unresolved__中的__resolved__个冲突", + "commonVersionError": "通用版本不包含有效的JSON:", + "oldVersionError": "旧版本不包含有效的JSON:", + "newVersionError": "新版本不包含有效的JSON:" }, "subflow": { + "editSubflowInstance": "编辑子流实例:__name__", "editSubflow": "编辑流程模板: __name__", "edit": "编辑流程模板", "subflowInstances": "这个子流程模板有__count__个实例", @@ -185,8 +295,14 @@ "editSubflowProperties": "编辑属性", "input": "输入:", "output": "输出:", + "status": "状态节点", "deleteSubflow": "删除子流程", "info": "详细描述", + "category": "类别", + "env": { + "restore": "恢复为默认子流", + "remove": "删除环境变量" + }, "errors": { "noNodesSelected": "无法创建子流程: 未选择节点", "multipleInputsToSelection": "无法创建子流程: 多个输入到了选择" @@ -204,18 +320,68 @@ "editConfig": "编辑__type__配置", "addNewType": "添加新的__type__节点", "nodeProperties": "节点属性", + "label": "标签", + "color": "颜色", "portLabels": "端口标签", "labelInputs": "输入", "labelOutputs": "输出", + "settingIcon": "图标", + "default": "默认", "noDefaultLabel": "无", "defaultLabel": "使用默认标签", + "searchIcons": "搜索图标", + "useDefault": "使用默认", + "description": "描述", + "show": "显示", + "hide": "隐藏", + "locale": "选择界面语言", + "icon": "图标", + "inputType": "输入类型", + "inputs": { + "input": "输入", + "select": "选择", + "checkbox": "复选框", + "spinner": "微调器", + "none": "空", + "hidden": "隐藏属性" + }, + "types": { + "str": "字符串", + "num": "数字", + "bool": "布尔", + "json": "JSON", + "bin": "buffer", + "env": "环境变量" + }, + "menu": { + "input": "输入", + "select": "选择", + "checkbox": "复选框", + "spinner": "微调器", + "hidden": "仅标签" + }, + "select": { + "label": "标签", + "value": "值" + }, + "spinner": { + "min": "最小值", + "max": "最大值" + }, "errors": { - "scopeChange": "更改范围将使其他流中的节点无法使用" + "scopeChange": "更改范围将使其他流中的节点无法使用", + "invalidProperties": "无效的属性:" } }, "keyboard": { "title": "键盘快捷键", + "keyboard": "键盘", + "filterActions": "筛选动作", + "shortcut": "快捷键", + "scope": "范围", "unassigned": "未分配", + "global": "全局", + "workspace": "工作组", "selectAll": "选择所有节点", "selectAllConnected": "选择所有连接的节点", "addRemoveNode": "从选择中添加/删除节点", @@ -226,12 +392,14 @@ "nudgeNode": "移动所选节点(1px)", "moveNode": "移动所选节点(20px)", "toggleSidebar": "切换侧边栏", + "togglePalette": "切换控制板", "copyNode": "复制所选节点", "cutNode": "剪切所选节点", "pasteNode": "粘贴节点", "undoChange": "撤消上次执行的更改", "searchBox": "打开搜索框", - "managePalette": "管理面板" + "managePalette": "管理面板", + "actionList": "动作列表" }, "library": { "library": "库", @@ -239,30 +407,42 @@ "saveToLibrary": "保存到库...", "typeLibrary": "__type__类型库", "unnamedType": "无名__type__", - "exportToLibrary": "将节点导出到库", + "exportedToLibrary": "节点导出到库", "dialogSaveOverwrite": "一个叫做__libraryName__的__libraryType__已经存在,您需要覆盖么?", "invalidFilename": "无效的文件名", "savedNodes": "保存的节点", "savedType": "已保存__type__", "saveFailed": "保存失败: __message__", + "newFolder": "新文件夹", "types": { + "local": "本地的", "examples": "例子" - } + }, + "exportToLibrary": "将节点导出到库" }, "palette": { "noInfo": "无可用信息", "filter": "过滤节点", "search": "搜索模块", + "addCategory": "添加新的...", "label": { "subflows": "子流程", + "network": "网络", + "common": "共通", "input": "输入", "output": "输出", "function": "功能", + "sequence": "序列", + "parser": "解析", "social": "社交", "storage": "存储", "analysis": "分析", "advanced": "高级" }, + "actions": { + "collapse-all": "收起所有类别", + "expand-all": "展开所有类别" + }, "event": { "nodeAdded": "添加到面板中的节点:", "nodeAdded_plural": "添加到面板中的多个节点", @@ -276,6 +456,7 @@ }, "editor": { "title": "面板管理", + "palette": "控制板", "times": { "seconds": "秒前", "minutes": "分前", @@ -309,6 +490,8 @@ "updated": "已更新", "install": "安装", "installed": "已安装", + "conflict": "冲突", + "conflictTip": "

无法安装此模块,因为它包含已安装的
节点类型

__module__冲突

", "loading": "加载目录...", "tab-nodes": "节点", "tab-install": "安装", @@ -356,6 +539,7 @@ "label": "信息", "node": "节点", "type": "类型", + "module": "模组", "id": "ID", "status": "状态", "enabled": "启用", @@ -364,17 +548,18 @@ "instances": "实例", "properties": "属性", "info": "信息", + "desc": "描述", "blank": "空白", "null": "空", "showMore": "展开", "showLess": "收起", "flow": "流程", - "selection":"选择", - "nodes":"__count__ 个节点", + "selection": "选择", + "nodes": "__count__ 个节点", "flowDesc": "流程描述", "subflowDesc": "子流程描述", "nodeHelp": "节点帮助", - "none":"无", + "none": "无", "arrayItems": "__count__个项目", "showTips": "您可以从设置面板启用提示信息" }, @@ -385,10 +570,26 @@ "none": "无", "subflows": "子流程", "flows": "流程", - "filterUnused": "未使用", "filterAll": "所有", + "showAllConfigNodes": "显示所有配置节点", + "filterUnused": "未使用", + "showAllUnusedConfigNodes": "显示所有未使用的配置节点", "filtered": "__count__ 个隐藏" }, + "context": { + "name": "上下文数据", + "label": "上下午", + "none": "未选择", + "refresh": "刷新以加载", + "empty": "空", + "node": "节点", + "flow": "流程", + "global": "全局", + "deleteConfirm": "你确定要删除这个项目吗?", + "autoRefresh": "刷新选择更改", + "refrsh": "刷新", + "delete": "删除" + }, "palette": { "name": "节点管理", "label": "节点" @@ -399,8 +600,151 @@ "description": "描述", "dependencies": "依赖", "settings": "设置", + "noSummaryAvailable": "无可用摘要", "editDescription": "编辑项目描述", - "editDependencies": "编辑项目依赖" + "editDependencies": "编辑项目依赖", + "noDescriptionAvailable": "没有可用的描述", + "editReadme": "编辑README.md", + "showProjectSettings": "显示项目设置", + "projectSettings": { + "title": "项目设置", + "edit": "编辑", + "none": "空", + "install": "安装", + "removeFromProject": "从项目中删除", + "addToProject": "添加到项目", + "files": "文件", + "package": "包", + "flow": "流程", + "credentials": "证书", + "packageCreate": "保存更改后将创建文件", + "fileNotExist": "文件不存在", + "selectFile": "选择文件", + "invalidEncryptionKey": "无效的加密密钥", + "encryptionEnabled": "启用加密", + "encryptionDisabled": "加密已禁用", + "setTheEncryptionKey": "设置加密密钥", + "resetTheEncryptionKey": "重置加密密钥", + "changeTheEncryptionKey": "更改加密密钥", + "currentKey": "当前密钥", + "newKey": "新密钥", + "credentialsAlert": "这将删除所有现有凭证", + "versionControl": "版本控制", + "branches": "分支", + "noBranches": "没有分支", + "deleteConfirm": "您确定要删除本地分支'__name__'吗? 这不能被撤消。", + "unmergedConfirm": "本地分支'__name__'具有未合并的更改,这些更改将丢失。你确定要删除吗?", + "deleteUnmergedBranch": "删除未合并的分支", + "gitRemotes": "Git远程仓库", + "addRemote": "添加远程仓库", + "addRemote2": "添加远程仓库", + "remoteName": "远程仓库名", + "nameRule": "只能包含A-Z 0-9 _ -", + "url": "URL", + "urlRule": "https://, ssh:// or file://", + "urlRule2": "网址中不能包含用户名/密码", + "noRemotes": "没有远程仓库", + "deleteRemoteConfrim": "您确定要删除远程仓库'__name__'吗?", + "deleteRemote": "删除远程仓库" + }, + "userSettings": { + "committerDetail": "提交者详细信息", + "committerTip": "保留空白以使用系统默认值", + "userName": "用户名", + "email": "电子邮件", + "sshKeys": "SSH密钥", + "sshKeysTip": "允许您创建到远程git存储库的安全连接。", + "add": "添加密钥", + "addSshKey": "添加SSH密钥", + "addSshKeyTip": "生成新的公钥/私钥对", + "name": "名字", + "nameRule": "只能包含A-Z 0-9 _ -", + "passphrase": "密码短语", + "passphraseShort": "密码短语过短", + "optional": "可选的", + "cancel": "取消", + "generate": "生成密钥", + "noSshKeys": "没有SSH密钥", + "copyPublicKey": "将公钥复制到剪贴板", + "delete": "删除密钥", + "gitConfig": "Git配置", + "deleteConfirm": "您确定要删除SSH密钥__name__吗?这不能被撤消。" + }, + "versionControl": { + "unstagedChanges": "未暂存的变更", + "stagedChanges": "暂存的变更", + "unstageChange": "取消变更的暂存", + "stageChange": "暂存变更", + "unstageAllChange": "取消所有变更的暂存", + "stageAllChange": "暂存所有变更", + "commitChanges": "提交变更", + "resolveConflicts": "解决冲突", + "head": "HEAD", + "staged": "暂存的", + "unstaged": "未暂存的", + "local": "本地的", + "remote": "远程的", + "revert": "您确定要将更改恢复为'__file__'吗?这不能被撤消。", + "revertChanges": "还原变更", + "localChanges": "本地变更", + "none": "None", + "conflictResolve": "解决所有冲突。提交更改以完成合并。", + "localFiles": "本地文件", + "all": "所有的", + "unmergedChanges": "未合并的更改", + "abortMerge": "中止合并", + "commit": "提交", + "changeToCommit": "提交变更", + "commitPlaceholder": "输入您的提交信息", + "cancelCapital": "取消", + "commitCapital": "提交", + "commitHistory": "提交历史", + "branch": "分支:", + "moreCommits": "更多提交", + "changeLocalBranch": "变更本地分支", + "createBranchPlaceholder": "查找或创建分支", + "upstream": "上游", + "localOverwrite": "切换分支会覆盖您现有的本地更改。您必须先提交或撤消那些更改。", + "manageRemoteBranch": "管理远程分支", + "unableToAccess": "无法访问远程存储库", + "retry": "重试", + "setUpstreamBranch": "设置为上游分支", + "createRemoteBranchPlaceholder": "查找或创建远程分支", + "trackedUpstreamBranch": "创建的分支将被设置为跟踪的上游分支。", + "selectUpstreamBranch": "分支将被创建。 在下面选择以将其设置为被跟踪的上游分支。", + "pushFailed": "推送失败,因为远程具有更多的最新提交。请先拉取并合并,然后再尝试推送。", + "push": "推送", + "pull": "拉取", + "unablePull": "

无法提取远程更改;您未暂存的本地更改将被覆盖。

请先提交更改,然后重试。

", + "showUnstagedChanges": "显示未暂存的更改", + "connectionFailed": "无法连接到远程存储库:", + "pullUnrelatedHistory": "

远程有无关的提交历史

您确定要将这些更改拉入本地仓库吗?

", + "pullChanges": "拉取更改", + "history": "历史", + "projectHistory": "项目历史", + "daysAgo": "__count__天前", + "daysAgo_plural": "__count__天前", + "hoursAgo": "__count__小时前", + "hoursAgo_plural": "__count__小时前", + "minsAgo": "__count__分钟前", + "minsAgo_plural": "__count__分钟前", + "secondsAgo": "秒前", + "notTracking": "您的本地分支当前未跟踪一个远程分支。", + "statusUnmergedChanged": "您的仓库中有未合并的更改。您需要解决冲突并提交结果。", + "repositoryUpToDate": "您的仓库是最新的。", + "commitsAhead": "您的存储库领先远程仓库__count__次提交。您现在可以推送这些提交。", + "commitsAhead_plural": "您的存储库领先远程仓库__count__次提交。您现在可以推送这些提交。", + "commitsBehind": "您的存储库落后远程仓库__count__次提交。您现在可以拉取这些提交。", + "commitsBehind_plural": "您的存储库落后远程仓库__count__次提交。您现在可以拉取这些提交。", + "commitsAheadAndBehind1": "您的存储库落后远程仓库__count__次提交", + "commitsAheadAndBehind1_plural": "您的存储库落后远程仓库__count__次提交", + "commitsAheadAndBehind2": "领先远程仓库__count__次提交。", + "commitsAheadAndBehind2_plural": "领先远程仓库__count__次提交。", + "commitsAheadAndBehind3": "您必须先拉取远程提交,然后才能进行推送。", + "commitsAheadAndBehind3_plural": "您必须先拉取远程提交,然后才能进行推送。", + "refreshCommitHistory": "刷新提交历史", + "refreshChanges": "刷新更改" + } } }, "typedInput": { @@ -408,10 +752,12 @@ "str": "文字列", "num": "数字", "re": "正则表达式", - "bool": "布尔", + "bool": "布尔值", "json": "JSON", "bin": "二进制流", - "date": "时间戳" + "date": "时间戳", + "jsonata": "表达式", + "env": "环境变量" } }, "editableList": { @@ -423,8 +769,10 @@ }, "expressionEditor": { "functions": "功能", + "functionReference": "功能reference", "insert": "插入", "title": "JSONata表达式编辑器", + "test": "测试", "data": "示例消息", "result": "结果", "format": "格式表达方法", @@ -438,14 +786,228 @@ "eval": "评估表达式错误:\n __message__" } }, + "jsEditor": { + "title": "JavaScript编辑器" + }, + "textEditor": { + "title": "文本编辑器" + }, "jsonEditor": { "title": "JSON编辑器", - "format": "格式化JSON" + "format": "格式化JSON", + "rawMode": "编辑 JSON", + "uiMode": "Visual编辑器", + "insertAbove": "在上方插入", + "insertBelow": "在下方插入", + "addItem": "添加项目", + "copyPath": "复制路径到项目", + "expandItems": "展开项目", + "collapseItems": "收合项目", + "duplicate": "重复", + "error": { + "invalidJSON": "无效的JSON: " + } + }, + "markdownEditor": { + "title": "Markdown编辑器", + "expand": "展开", + "format": "格式化为markdown", + "heading1": "标题 1", + "heading2": "标题 2", + "heading3": "标题 3", + "bold": "粗体", + "italic": "斜体", + "code": "代码", + "ordered-list": "排序的列表", + "unordered-list": "非排序的列表", + "quote": "引用", + "link": "链接", + "horizontal-rule": "水平线", + "toggle-preview": "切换预览" }, "bufferEditor": { "title": "缓冲区编辑器", "modeString": "作为UTF-8字符串处理", "modeArray": "作为JSON数组处理", "modeDesc": "

缓冲区编辑器

缓冲区类型被存储为字节值的JSON数组。编辑器将尝试将输入的数值解析为JSON数组。如果它不是有效的JSON,它将被视为UTF-8字符串,并被转换为单个字符代码点的数组。

例如,Hello World的值会被转换为JSON数组:

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" + }, + "projects": { + "config-git": "配置Git客户端", + "welcome": { + "hello": "你好! 我们已经将“项目”引入了Node-RED。", + "desc0": "这是一种用于管理流程文件的新方法,并且包括对流程的版本控制。", + "desc1": "首先,您可以创建您的第一个项目或从git存储库克隆现有项目。", + "desc2": "如果不确定,可以暂时跳过此步骤。您仍然可以随时通过“项目”菜单创建第一个项目。", + "create": "建立专案", + "clone": "克隆仓库", + "openExistingProject": "打开现有项目", + "not-right-now": "不是现在" + }, + "git-config": { + "setup": "设置您的版本控制客户端", + "desc0": "Node-RED使用开源工具Git进行版本控制。它跟踪对项目文件的更改,并允许您将其推送到远程存储库。", + "desc1": "提交一组更改时,Git会使用用户名和电子邮件地址记录谁进行了更改。用户名可以是您想要的任何名称-不必是您的真实姓名。", + "desc2": "您的Git客户端已经配置了以下详细信息。", + "desc3": "您可以稍后在设置对话框的'Git config'标签下更改这些设置。", + "username": "用户名", + "email": "电子邮件" + }, + "project-details": { + "create": "创建你的项目", + "desc0": "项目被维护为Git仓库。与他人一起共享您的流程", + "desc1": "您可以创建多个项目,并通过编辑器在它们之间快速切换。", + "desc2": "首先,您的项目需要一个名称和一个可选的描述。", + "already-exists": "项目已存在", + "must-contain": "只能包含A-Z 0-9 _ -", + "project-name": "项目名", + "desc": "描述", + "opt": "可选的" + }, + "clone-project": { + "clone": "克隆一个项目", + "desc0": "如果您已经有一个包含项目的git仓库,则可以对其进行克隆以开始使用。", + "already-exists": "项目已存在", + "must-contain": "只能包含A-Z 0-9 _ -", + "project-name": "项目名", + "no-info-in-url": "网址中不要包含用户名/密码", + "git-url": "Git仓库的url", + "protocols": "https://, ssh:// or file://", + "auth-failed": "认证失败", + "username": "用户名", + "passwd": "秘密啊", + "ssh-key": "SSH密钥", + "passphrase": "密码短语", + "ssh-key-desc": "在通过ssh克隆仓库之前,必须添加SSH密钥才能访问它。", + "ssh-key-add": "添加一个ssh密钥", + "credential-key": "证书加密密钥", + "cant-get-ssh-key": "错误! 无法获取所选的SSH密钥路径。", + "already-exists2": "已存在", + "git-error": "git错误", + "connection-failed": "连接失败", + "not-git-repo": "不是一个git仓库", + "repo-not-found": "未发现仓库" + }, + "default-files": { + "create": "创建您的项目文件", + "desc0": "一个包含您的流程文件,Readme文件和package.json文件的项目。", + "desc1": "它可以包含您要在Git仓库中维护的任何其他文件。", + "desc2": "您现有的流程和凭证文件将被复制到项目中。", + "flow-file": "流程文件", + "credentials-file": "证书文件" + }, + "encryption-config": { + "setup": "设置证书文件的加密", + "desc0": "您的流程证书文件可以被加密以确保其内容安全。", + "desc1": "如果要将这些证书存储在公共Git存储库中,则必须通过提供密钥短语来对它们进行加密。", + "desc2": "您的流程证书文件当前未加密。", + "desc3": "这意味着任何有权访问该文件的人都可以读取其内容,例如密码和访问令牌。", + "desc4": "如果要将这些证书存储在公共Git仓库中,则必须通过提供密钥短语来对它们进行加密。", + "desc5": "当前,使用设置文件中的credentialSecret属性作为密钥来加密流程证书文件。", + "desc6": "您的流程证书文件当前使用系统生成的密钥加密。您应该为此项目提供一个新的密钥。", + "desc7": "密钥将与项目文件分开存储。您将需要提供在另一个Node-RED实例中使用该项目的密钥。", + "credentials": "证书", + "enable": "启用加密", + "disable": "禁用加密", + "disabled": "禁用的", + "copy": "复制现有密钥", + "use-custom": "使用自定义密钥", + "desc8": "证书文件不会被加密,其内容很容易阅读", + "create-project-files": "创建项目文件", + "create-project": "创建项目", + "already-exists": "已存在", + "git-error": "git错误", + "git-auth-error": "git认证错误" + }, + "create-success": { + "success": "您已经成功创建了第一个项目!", + "desc0": "现在,您可以像往常一样继续使用Node-RED。", + "desc1": "侧栏中的“信息”标签显示了您当前的活动项目。名称旁边的按钮可用于访问项目设置视图。", + "desc2": "侧栏中的“历史记录”标签可用于查看项目中已更改的文件并提交。它向您显示了提交的完整历史记录,并允许您将更改推送到远程存储库。" + }, + "create": { + "projects": "项目", + "already-exists": "项目已存在", + "must-contain": "只能包含A-Z 0-9 _ -", + "no-info-in-url": "网址中不要包含用户名/密码", + "open": "打开项目", + "create": "创建项目", + "clone": "克隆仓库", + "project-name": "项目名", + "desc": "描述", + "opt": "可选的", + "flow-file": "流程文件", + "credentials": "证书", + "enable-encryption": "启用加密", + "disable-encryption": "禁用加密", + "encryption-key": "加密密钥", + "desc0": "用来保护您的凭证的短语", + "desc1": "凭证文件不会被加密,其内容很容易阅读", + "git-url": "Git存储库URL", + "protocols": "https://, ssh:// or file://", + "auth-failed": "验证失败", + "username": "用户名", + "password": "密码", + "ssh-key": "SSH密钥", + "passphrase": "密码短语", + "desc2": "在通过ssh克隆存储库之前,必须添加SSH密钥才能访问它。", + "add-ssh-key": "添加一个ssh密钥", + "credentials-encryption-key": "证书加密密钥", + "already-exists-2": "已存在", + "git-error": "git错误", + "con-failed": "连接失败", + "not-git": "不是git仓库", + "no-resource": "找不到存储库", + "cant-get-ssh-key-path": "错误!无法获取所选的SSH密钥路径。", + "unexpected_error": "意外的错误" + }, + "delete": { + "confirm": "您确定要删除此项目吗?" + }, + "create-project-list": { + "search": "搜索您的项目", + "current": "当前的" + }, + "require-clean": { + "confirm": "

您有未部署的更改,这些更改将丢失。

您要继续吗?

" + }, + "send-req": { + "auth-req": "存储库需要认证", + "username": "用户名", + "password": "秘密", + "passphrase": "密码短语", + "retry": "重试", + "update-failed": "无法更新身份验证", + "unhandled": "未处理的错误响应" + }, + "create-branch-list": { + "invalid": "无效的分支", + "create": "创建分支", + "current": "当前的" + }, + "create-default-file-set": { + "no-active": "没有活动项目就无法创建默认文件集", + "no-empty": "无法在非空项目上创建默认文件集", + "git-error": "git错误" + }, + "errors": { + "no-username-email": "您的Git客户端未配置用户名/电子邮件。", + "unexpected": "发生了一个意料之外的问题", + "code": "代码" + } + }, + "editor-tab": { + "properties": "属性", + "envProperties": "环境变量", + "description": "描述", + "appearance": "外观", + "preview": "UI预览", + "defaultValue": "默认值" + }, + "languages": { + "de": "德语", + "en-US": "英文", + "ja": "日语", + "ko": "韩文", + "zh-CN": "简体中文" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/jsonata.json index 669a7683a..56ede1b4e 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/jsonata.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/jsonata.json @@ -214,5 +214,53 @@ "$toMillis": { "args": "timestamp", "desc": "将ISO 8601格式的字符串`timestamp`转换为从UNIX时间 (1970年1月1日 UTC/GMT的午夜)开始到现在的毫秒数。如果该字符串的格式不正确,则抛出错误。" + }, + "$env": { + "args": "arg", + "desc": "返回环境变量的值。\n\n这是Node-RED定义的函数。" + }, + "$eval": { + "args": "expr [, context]", + "desc": "使用当前上下文来作为评估依据,分析并评估字符串`expr`,其中包含文字JSON或JSONata表达式。" + }, + "$formatInteger": { + "args": "number, picture", + "desc": "将“数字”转换为字符串,并将其格式化为“图片”字符串指定的整数表示形式。图片字符串参数定义了数字的格式,并具有与XPath F&O 3.1 规范中的fn:format-integer相同的语法。" + }, + "$parseInteger": { + "args": "string, picture", + "desc": "使用“图片”字符串指定的格式将“字符串”参数的内容解析为整数(作为JSON数字)。图片字符串参数与$formatInteger格式相同。." + }, + "$error": { + "args": "[str]", + "desc": "引发错误并显示一条消息。 可选的`str`将替代$error()函数评估的默认消息。" + }, + "$assert": { + "args": "arg, str", + "desc": "如果`arg`为真,则该函数返回。 如果arg为假,则抛出带有str的异常作为异常消息。" + }, + "$single": { + "args": "array, function", + "desc": "返回满足参数function谓语的array参数中的唯一值 (比如:传递值时,函数返回布尔值“true”)。如果匹配值的数量不唯一时,则抛出异常。\n\n应在以下签名中提供函数:`function(value [,index [,array []]])`其中value是数组的每个输入,index是该值的位置,整个数组作为第三个参数传递。" + }, + "$encodeUrl": { + "args": "str", + "desc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例,对统一资源定位符(URL)组件进行编码。\n\n示例:`$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`" + }, + "$encodeUrlComponent": { + "args": "str", + "desc": "通过用表示字符的UTF-8编码的一个,两个,三个或四个转义序列替换某些字符的每个实例,对统一资源定位符(URL)进行编码。\n\n示例: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`" + }, + "$decodeUrl": { + "args": "str", + "desc": "解码以前由encodeUrlComponent创建的统一资源定位器(URL)组件。 \n\n示例: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`" + }, + "$decodeUrlComponent": { + "args": "str", + "desc": "解码先前由encodeUrl创建的统一资源定位符(URL)。 \n\n示例: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`" + }, + "$distinct": { + "args": "array", + "desc": "返回一个数组,其中重复的值已从`数组`中删除" } } diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json new file mode 100644 index 000000000..492fce58c --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json @@ -0,0 +1,945 @@ +{ + "common": { + "label": { + "name": "名稱", + "ok": "確認", + "done": "完成", + "cancel": "取消", + "delete": "刪除", + "close": "關閉", + "load": "讀取", + "save": "保存", + "import": "匯入", + "export": "匯出", + "back": "返回", + "next": "下一步", + "clone": "複製專案", + "cont": "Continue" + } + }, + "workspace": { + "defaultName": "流程__number__", + "editFlow": "編輯流程: __name__", + "confirmDelete": "確認刪除", + "delete": "確定想要刪除 '__label__'?", + "dropFlowHere": "把流程放到這裡", + "addFlow": "新增流程", + "listFlows": "流程列表", + "status": "狀態", + "enabled": "有效", + "disabled": "無效", + "info": "詳細描述", + "selectNodes": "點擊節點用於選擇", + "tip": "詳細描述支援Markdown羽量級標記語言,並將出現在資訊標籤中。" + }, + "menu": { + "label": { + "view": { + "view": "顯示", + "grid": "格線", + "showGrid": "顯示格線", + "snapGrid": "對齊格線", + "gridSize": "格線尺寸", + "textDir": "文本方向", + "defaultDir": "默認方向", + "ltr": "從左到右", + "rtl": "從右到左", + "auto": "上下文", + "language": "Language", + "browserDefault": "Browser default" + }, + "sidebar": { + "show": "顯示側邊欄" + }, + "palette": { + "show": "Show palette" + }, + "settings": "設置", + "userSettings": "使用者設置", + "nodes": "節點", + "displayStatus": "顯示節點狀態", + "displayConfig": "修改節點配置", + "import": "匯入", + "export": "匯出", + "search": "搜尋流程", + "searchInput": "搜尋流程", + "subflows": "子流程", + "createSubflow": "新建子流程", + "selectionToSubflow": "將選擇部分更改為子流程", + "flows": "流程", + "add": "增加", + "rename": "重新命名", + "delete": "刪除", + "keyboardShortcuts": "鍵盤快速鍵", + "login": "登入", + "logout": "退出", + "editPalette": "節點管理", + "other": "其他", + "showTips": "顯示小提示", + "help": "Node-RED website", + "projects": "專案", + "projects-new": "新專案", + "projects-open": "開啟專案", + "projects-settings": "專案設定", + "showNodeLabelDefault": "顯示新添加節點的標籤", + "clipboard": "剪貼簿", + "library": "庫", + "examples": "範例" + } + }, + "actions": { + "toggle-navigator": "切換導航器", + "zoom-out": "縮小", + "zoom-reset": "重置縮放", + "zoom-in": "放大" + }, + "user": { + "loggedInAs": "作為__name__登入", + "username": "帳號", + "password": "密碼", + "login": "登入", + "loginFailed": "登入失敗", + "notAuthorized": "未授權", + "errors": { + "settings": "設置資訊需要登入後才能訪問", + "deploy": "改動需要登入後才能部署", + "notAuthorized": "此操作需要登入後才能執行" + } + }, + "notification": { + "warning": "警告: __message__", + "warnings": { + "undeployedChanges": "節點中存在未部署的更改", + "nodeActionDisabled": "節點動作在子流程中被禁用", + "nodeActionDisabledSubflow": "子流程中禁用了節點操作", + "missing-types": "流程由於缺少節點類型而停止。請檢查日誌的詳細資訊", + "safe-mode": "

流程在安全模式下停止。

您可以修改流程並部署更改以重新啟動。

", + "restartRequired": "Node-RED必須重新啟動,以啟用升級的模組", + "credentials_load_failed": "

流程由於無法解密證書而停止。

流程證書文件已加密,但是項目的加密密鑰丟失或無效。

", + "credentials_load_failed_reset": "

證書無法解密

流程證書文件已加密,但是項目的加密密鑰丟失或無效。

流程證書文件將在下一次部署時重置。任何現有的流程證書將被清除。

", + "missing_flow_file": "

找不到項目流程文件。

該項目未配置流程文件。

", + "missing_package_file": "

找不到項目包文件。

項目缺少package.json文件。

", + "project_empty": "

該項目為空。

是否要創建一組默認的項目文件?
否則,您將不得不在編輯器外部手動將文件添加到項目中。

", + "project_not_found": "

找不到項目的'__project__'

", + "git_merge_conflict": "

自動合併更改失敗。

修復未合併的衝突,然後提交結果。

" + }, + "error": "Error: __message__", + "errors": { + "lostConnection": "丟失與伺服器的連接,重新連接...", + "lostConnectionReconnect": "丟失與伺服器的連接,__time__秒後重新連接", + "lostConnectionTry": "現在嘗試", + "cannotAddSubflowToItself": "無法向其自身添加子流程", + "cannotAddCircularReference": "無法添加子流程 - 迴圈引用", + "unsupportedVersion": "您正在使用不受支持的Node.js版本
請升級到最新版本的Node.js LTS", + "failedToAppendNode": "

加載'__module__'失敗

__error__

" + }, + "project": { + "change-branch": "轉到本地分支'__project__'", + "merge-abort": "Git合併中止", + "loaded": "已加載項目'__project__'", + "updated": "已更新項目'__project__'", + "pull": "已重新加載項目'__project__'", + "revert": "項目“__project__”已還原", + "merge-complete": "Git合併完成", + "setupCredentials": "設定證書", + "setupProjectFiles": "設置項目文件", + "no": "不了,謝謝", + "createDefault": "創建默認項目文件", + "mergeConflict": "顯示合併衝突" + }, + "label": { + "manage-project-dep": "管理項目依賴性", + "setup-cred": "設定憑證", + "setup-project": "設置項目文件", + "create-default-package": "創建默認的包文件", + "no-thanks": "不了,謝謝", + "create-default-project": "創建默認項目文件", + "show-merge-conflicts": "顯示合併衝突" + } + }, + "clipboard": { + "clipboard": "剪貼簿", + "nodes": "節點", + "node": "__count__ 節點", + "node_plural": "__count__ 多個節點", + "configNode": "__count__ 節點組態", + "configNode_plural": "__count__ 多節點組態", + "flow": "__count__ 流程", + "flow_plural": "__count__ 多流程", + "subflow": "__count__ 子流程", + "subflow_plural": "__count__ 多子流程", + "pasteNodes": "在這裡粘貼節點", + "selectFile": "匯入所選檔案", + "importNodes": "匯入節點", + "exportNodes": "匯出節點至剪貼簿", + "download": "下載", + "importUnrecognised": "匯入了無法識別的類型:", + "importUnrecognised_plural": "匯入了無法識別的類型:", + "nodesExported": "節點匯出到了剪貼簿", + "nodesImported": "已匯入:", + "nodeCopied": "已複製__count__個節點", + "nodeCopied_plural": "已複製__count__個節點", + "invalidFlow": "無效的流程: __message__", + "export": { + "selected": "已選擇的節點", + "current": "現在的節點", + "all": "所有流程", + "compact": "緊湊", + "formatted": "已格式化", + "copy": "匯出到剪貼簿", + "export": "匯出到庫", + "exportAs": "匯出為", + "overwrite": "取代", + "exists": "

\"__file__\" 已經存在.

是否要取代?

" + }, + "import": { + "import": "匯入到", + "newFlow": "新流程", + "errors": { + "notArray": "輸入的不是JSON數組", + "itemNotObject": "輸入的流程無效-項目__index__不是節點對象", + "missingId": "輸入的流程無效-項__index__缺少“ id”屬性", + "missingType": "輸入的流程無效-項__index__缺少“類型”屬性" + } + }, + "copyMessagePath": "已複製路徑", + "copyMessageValue": "已複製數值", + "copyMessageValue_truncated": "已複製捨棄的數值", + "selectNodes": "選擇上面的文本並複製到剪貼簿" + }, + "deploy": { + "deploy": "部署", + "full": "全面", + "fullDesc": "在工作區中部署所有內容", + "modifiedFlows": "已修改的流程", + "modifiedFlowsDesc": "只部署包含已更改節點的流程", + "modifiedNodes": "已更改的節點", + "modifiedNodesDesc": "只部署已經更改的節點", + "restartFlows": "重新啟動流程", + "restartFlowsDesc": "重新啟動當前部署的流程", + "successfulDeploy": "部署成功", + "successfulRestart": "成功重啟流程", + "deployFailed": "部署失敗: __message__", + "unusedConfigNodes": "您有一些未使用的配置節點", + "unusedConfigNodesLink": "點擊此處查看它們", + "errors": { + "noResponse": "伺服器沒有回應" + }, + "confirm": { + "button": { + "ignore": "忽略", + "confirm": "確認部署", + "review": "查看更改", + "cancel": "取消", + "merge": "合併", + "overwrite": "忽略 & 部署" + }, + "undeployedChanges": "您有未部署的更改。\n\n離開此頁面將丟失這些更改。", + "improperlyConfigured": "工作區包含一些未正確配置的節點:", + "unknown": "工作區包含一些未知的節點類型:", + "confirm": "你確定要部署嗎?", + "doNotWarn": "不要再對此發出警告", + "conflict": "伺服器正在運行較新的一組流程。", + "backgroundUpdate": "伺服器上的流程已更新。", + "conflictChecking": "檢查是否可以自動合併更改", + "conflictAutoMerge": "此更改不包括衝突,可以自動合併", + "conflictManualMerge": "這些更改包括了在部署之前必須解決的衝突。", + "plusNMore": "+更多的__count__" + } + }, + "eventLog": { + "title": "事件日誌", + "view": "查看日誌" + }, + "diff": { + "unresolvedCount": "__count__個未解決的衝突", + "unresolvedCount_plural": "__count__個未解決的衝突", + "globalNodes": "全局節點", + "flowProperties": "流程屬性", + "type": { + "added": "已添加", + "changed": "已更改", + "unchanged": "未更改", + "deleted": "已刪除", + "flowDeleted": "已刪除流程", + "flowAdded": "已添加流程", + "movedTo": "移動至__id__", + "movedFrom": "從__id__移動" + }, + "nodeCount": "__count__個節點", + "nodeCount_plural": "__count__個節點", + "local": "本地", + "remote": "遠端", + "reviewChanges": "查看變更", + "noBinaryFileShowed": "無法顯示二進製文件內容", + "viewCommitDiff": "查看提交更改", + "compareChanges": "比較變更", + "saveConflict": "保存衝突解決", + "conflictHeader": "已解決__unresolved__中的__resolved__個衝突", + "commonVersionError": "通用版本不包含有效的JSON:", + "oldVersionError": "舊版本不包含有效的JSON:", + "newVersionError": "新版本不包含有效的JSON:" + }, + "subflow": { + "editSubflowInstance": "編輯子流程實例:__name__", + "editSubflow": "編輯流程範本: __name__", + "edit": "編輯流程範本", + "subflowInstances": "這個子流程範本有__count__個實例", + "subflowInstances_plural": "這個子流程範本有__count__個實例", + "editSubflowProperties": "編輯屬性", + "input": "輸入:", + "output": "輸出:", + "status": "狀態節點", + "deleteSubflow": "刪除子流程", + "info": "詳細描述", + "category": "類別", + "env": { + "restore": "恢復為默認子流程", + "remove": "類別刪除環境變量" + }, + "errors": { + "noNodesSelected": "無法創建子流程: 未選擇節點", + "multipleInputsToSelection": "無法創建子流程: 多個輸入到了選擇" + }, + "format": "標記格式" + }, + "editor": { + "configEdit": "編輯", + "configAdd": "添加", + "configUpdate": "更新", + "configDelete": "刪除", + "nodesUse": "__count__個節點使用此配置", + "nodesUse_plural": "__count__個節點使用此配置", + "addNewConfig": "添加新的__type__配置", + "editNode": "編輯__type__節點", + "editConfig": "編輯__type__配置", + "addNewType": "添加新的__type__節點", + "nodeProperties": "節點屬性", + "label": "Label", + "portLabels": "埠標籤", + "labelInputs": "輸入", + "labelOutputs": "輸出", + "settingIcon": "Icon", + "noDefaultLabel": "無", + "defaultLabel": "使用默認標籤", + "searchIcons": "搜尋 icons", + "useDefault": "使用默認", + "description": "描述", + "show": "顯示", + "hide": "隱藏", + "errors": { + "scopeChange": "更改範圍將使其他流程中的節點無法使用", + "invalidProperties": "無效的屬性:" + } + }, + "keyboard": { + "title": "鍵盤快速鍵", + "keyboard": "鍵盤", + "filterActions": "篩選動作", + "shortcut": "快捷鍵", + "scope": "範圍", + "unassigned": "未分配", + "global": "全局", + "workspace": "工作區", + "selectAll": "選擇所有節點", + "selectAllConnected": "選擇所有連接的節點", + "addRemoveNode": "從選擇中添加/刪除節點", + "editSelected": "編輯選定節點", + "deleteSelected": "刪除選定節點或連結", + "importNode": "匯入節點", + "exportNode": "匯出節點", + "nudgeNode": "移動所選節點(1px)", + "moveNode": "移動所選節點(20px)", + "toggleSidebar": "切換側邊欄", + "togglePalette": "切換調色板", + "copyNode": "複製所選節點", + "cutNode": "剪切所選節點", + "pasteNode": "粘貼節點", + "undoChange": "撤銷上次執行的更改", + "searchBox": "打開搜索框", + "managePalette": "管理面板" + }, + "library": { + "library": "庫", + "openLibrary": "打開庫...", + "saveToLibrary": "保存到庫...", + "typeLibrary": "__type__型別程式庫", + "unnamedType": "無名__type__", + "exportedToLibrary": "節點導出到庫", + "dialogSaveOverwrite": "一個叫做__libraryName__的__libraryType__已經存在,您需要覆蓋麼?", + "invalidFilename": "無效的檔案名", + "savedNodes": "保存的節點", + "savedType": "已保存__type__", + "saveFailed": "保存失敗: __message__", + "types": { + "local": "本地", + "examples": "例子" + }, + "exportToLibrary": "將節點匯出到庫", + "filename": "檔案名", + "folder": "資料夾", + "filenamePlaceholder": "文件", + "fullFilenamePlaceholder": "a/b/文件", + "folderPlaceholder": "a/b", + "breadcrumb": "庫" + }, + "palette": { + "noInfo": "無可用資訊", + "filter": "過濾節點", + "search": "搜索模組", + "addCategory": "添加新的...", + "label": { + "subflows": "子流程", + "input": "輸入", + "output": "輸出", + "function": "功能", + "social": "社交", + "storage": "存儲", + "analysis": "分析", + "advanced": "高級" + }, + "actions": { + "collapse-all": "收起所有類別", + "expand-all": "展開所有類別" + }, + "event": { + "nodeAdded": "添加到面板中的節點:", + "nodeAdded_plural": "添加到面板中的多個節點", + "nodeRemoved": "從面板中刪除的節點:", + "nodeRemoved_plural": "從面板中刪除的多個節點:", + "nodeEnabled": "啟用節點:", + "nodeEnabled_plural": "啟用多個節點:", + "nodeDisabled": "禁用節點:", + "nodeDisabled_plural": "禁用多個節點:", + "nodeUpgraded": "節點模組__module__升級到__version__版本" + }, + "editor": { + "title": "面板管理", + "palette": "Palette", + "times": { + "seconds": "秒前", + "minutes": "分前", + "minutesV": "__count__分前", + "hoursV": "__count__小時前", + "hoursV_plural": "__count__小時前", + "daysV": "__count__天前", + "daysV_plural": "__count__天前", + "weeksV": "__count__周前", + "weeksV_plural": "__count__周前", + "monthsV": "__count__月前", + "monthsV_plural": "__count__月前", + "yearsV": "__count__年前", + "yearsV_plural": "__count__年前", + "yearMonthsV": "__y__年, __count__月前", + "yearMonthsV_plural": "__y__年, __count__月前", + "yearsMonthsV": "__y__年, __count__月前", + "yearsMonthsV_plural": "__y__年, __count__月前" + }, + "nodeCount": "__label__個節點", + "nodeCount_plural": "__label__個節點", + "moduleCount": "__count__個可用模組", + "moduleCount_plural": "__count__個可用模組", + "inuse": "使用中", + "enableall": "全部啟用", + "disableall": "全部禁用", + "enable": "啟用", + "disable": "禁用", + "remove": "移除", + "update": "更新至__version__版本", + "updated": "已更新", + "install": "安裝", + "installed": "已安裝", + "conflict": "conflict", + "conflictTip": "

無法安裝此模塊,因為它包含已安裝的
節點類型

__module__衝突

", + "loading": "載入目錄...", + "tab-nodes": "節點", + "tab-install": "安裝", + "sort": "排序:", + "sortAZ": "a-z順序", + "sortRecent": "日期順序", + "more": "增加__count__個", + "errors": { + "catalogLoadFailed": "無法載入節點目錄。
查看瀏覽器控制台瞭解更多資訊", + "installFailed": "無法安裝: __module__
__message__
查看日誌瞭解更多資訊", + "removeFailed": "無法刪除: __module__
__message__
查看日誌瞭解更多資訊", + "updateFailed": "無法更新: __module__
__message__
查看日誌瞭解更多資訊", + "enableFailed": "無法啟用: __module__
__message__
查看日誌瞭解更多資訊", + "disableFailed": "無法禁用: __module__
__message__
查看日誌瞭解更多資訊" + }, + "confirm": { + "install": { + "body": "在安裝之前,請閱讀節點的文檔,某些節點的依賴關係不能自動解決,可能需要重新啟動Node-RED。", + "title": "安裝節點" + }, + "remove": { + "body": "刪除節點將從Node-RED卸載它。節點可能會繼續使用資源,直到重新啟動Node-RED。", + "title": "刪除節點" + }, + "update": { + "body": "更新節點將需要重新啟動Node-RED來完成更新,該過程必須由手動完成。", + "title": "更新節點" + }, + "cannotUpdate": { + "body": "此節點的更新可用,但不會安裝在面板管理器可以更新的位置。

請參閱有關如何更新此節點的文檔。" + }, + "button": { + "review": "打開節點資訊", + "install": "安裝", + "remove": "刪除", + "update": "更新" + } + } + } + }, + "sidebar": { + "info": { + "name": "節點信息", + "tabName": "名稱", + "label": "信息", + "node": "節點", + "type": "類型", + "module": "Module", + "id": "ID", + "status": "狀態", + "enabled": "啟用", + "disabled": "禁用", + "subflow": "子流程", + "instances": "實例", + "properties": "屬性", + "info": "信息", + "desc": "描述", + "blank": "空白", + "null": "空", + "showMore": "展開", + "showLess": "收起", + "flow": "流程", + "selection": "選擇", + "nodes": "__count__ 個節點", + "flowDesc": "流程描述", + "subflowDesc": "子流程描述", + "nodeHelp": "節點幫助", + "none": "無", + "arrayItems": "__count__個項目", + "showTips": "您可以從設置面板啟用提示資訊" + }, + "config": { + "name": "配置節點", + "label": "配置", + "global": "所有流程", + "none": "無", + "subflows": "子流程", + "flows": "流程", + "filterUnused": "未使用", + "filterAll": "所有", + "filtered": "__count__ 個隱藏" + }, + "context": { + "name": "上下文數據", + "label": "上下文", + "none": "未選擇", + "refresh": "刷新以加載", + "empty": "空", + "node": "節點", + "flow": "流程", + "global": "全局的", + "deleteConfirm": "你確定要刪除這個項目嗎?", + "autoRefresh": "自動刷新" + }, + "palette": { + "name": "節點管理", + "label": "節點" + }, + "project": { + "label": "項目", + "name": "名稱", + "description": "描述", + "dependencies": "依賴", + "settings": "設置", + "noSummaryAvailable": "無可用摘要", + "editDescription": "編輯專案描述", + "editDependencies": "編輯項目依賴", + "editReadme": "Edit README.md", + "showProjectSettings": "顯示項目設置", + "projectSettings": { + "title": "項目設定", + "edit": "編輯", + "none": "None", + "install": "安裝", + "removeFromProject": "從項目中刪除", + "addToProject": "添加到項目", + "files": "文件", + "package": "包", + "flow": "流程", + "credentials": "證書", + "packageCreate": "保存更改後將創建文件", + "fileNotExist": "文件不存在", + "selectFile": "選擇文件", + "invalidEncryptionKey": "無效的加密密鑰", + "encryptionEnabled": "啟用加密", + "encryptionDisabled": "禁用加密", + "setTheEncryptionKey": "設置加密密鑰", + "resetTheEncryptionKey": "重置加密密鑰", + "changeTheEncryptionKey": "更改加密密鑰", + "currentKey": "當前密鑰", + "newKey": "新密鑰", + "credentialsAlert": "這將刪除所有現有證書", + "versionControl": "版本控制", + "branches": "分支", + "noBranches": "沒有分支", + "deleteConfirm": "您確定要刪除本地分支'__name__'嗎?這不能被撤消。", + "unmergedConfirm": "本地分支'__name__'具有未合併的更改,這些更改將丟失。你確定要刪除嗎?", + "deleteUnmergedBranch": "刪除未合併的分支", + "gitRemotes": "Git遠程倉庫", + "addRemote": "添加遠程倉庫", + "addRemote2": "添加遠程倉庫", + "remoteName": "遠程倉庫名", + "nameRule": "必須僅包含A-Z 0-9 _ -", + "url": "URL", + "urlRule": "https://, ssh:// or file://", + "urlRule2": "網址中不要包含用戶名/密碼", + "noRemotes": "沒有遠程倉庫", + "deleteRemoteConfrim": "確定要刪除遠程倉庫'__name__'嗎?", + "deleteRemote": "刪除遠程倉庫" + }, + "userSettings": { + "committerDetail": "提交者詳細信息", + "committerTip": "保留空白以使用系統默認值", + "userName": "用戶名", + "email": "電子郵件", + "sshKeys": "SSH密鑰", + "sshKeysTip": "允許您創建到遠程git存儲庫的安全連接。", + "add": "添加密鑰", + "addSshKey": "添加SSH密鑰", + "addSshKeyTip": "生成新的公鑰/私鑰對", + "name": "名字", + "nameRule": "必須僅包含A-Z 0-9 _ -", + "passphrase": "密碼短語", + "passphraseShort": "密碼短語太短", + "optional": "可選的", + "cancel": "取消", + "generate": "產生密鑰", + "noSshKeys": "沒有SSH密鑰", + "copyPublicKey": "將公鑰複製到剪貼板", + "delete": "刪除密鑰", + "gitConfig": "Git配置", + "deleteConfirm": "您確定要刪除SSH密鑰__name__嗎? 這不能被撤消。" + }, + "versionControl": { + "unstagedChanges": "未暫存的更改", + "stagedChanges": "已暫存的更改", + "unstageChange": "取消暫存更改", + "stageChange": "暫存更改", + "unstageAllChange": "取消暫存所有更改", + "stageAllChange": "暫存所有更改", + "commitChanges": "提交變更", + "resolveConflicts": "解決衝突", + "head": "HEAD", + "staged": "以暫存", + "unstaged": "未暫存", + "local": "本地的", + "remote": "遠程的", + "revert": "您確定要將更改恢復為'__file__'嗎? 這不能被撤消。", + "revertChanges": "還原變更", + "localChanges": "當地變化", + "none": "None", + "conflictResolve": "解決所有衝突。 提交更改以完成合併。", + "localFiles": "本地文件", + "all": "所有的", + "unmergedChanges": "未合併的更改", + "abortMerge": "合併中止", + "commit": "提交", + "changeToCommit": "提交變更", + "commitPlaceholder": "輸入您的提交信息", + "cancelCapital": "取消", + "commitCapital": "提交", + "commitHistory": "提交歷史", + "branch": "分支:", + "moreCommits": "更多提交", + "changeLocalBranch": "變更當地分支", + "createBranchPlaceholder": "查找或創建分支", + "upstream": "上游的", + "localOverwrite": "您有可通过切换分支覆盖的本地更改。您必须先提交或撤销那些更改。", + "manageRemoteBranch": "管理遠程分支", + "unableToAccess": "無法訪問遠程存儲庫", + "retry": "重試", + "setUpstreamBranch": "設置為上游分支", + "createRemoteBranchPlaceholder": "查找或創建遠程分支", + "trackedUpstreamBranch": "創建的分支將被設置為跟踪的上游分支。", + "selectUpstreamBranch": "分支將被創建。 在下面選擇以將其設置為被跟踪的上游分支。", + "pushFailed": "Push失敗,因為遠程具有更多的最新提交。請先進行pull與merge,然後再嘗試push。", + "push": "push", + "pull": "pull", + "unablePull": "

無法提取遠程更改;您未進行的暫存本地更改將被覆蓋。

提交更改,然後重試。

", + "showUnstagedChanges": "顯示未分階段的更改", + "connectionFailed": "無法連接到遠程存儲庫:", + "pullUnrelatedHistory": "

遠程服務器具有不相關的提交歷史記錄。

您確定要將更改保存到本地存儲庫中嗎?

", + "pullChanges": "Pull變更", + "history": "歷史", + "projectHistory": "項目歷史", + "daysAgo": "__count__天前", + "daysAgo_plural": "__count__天前", + "hoursAgo": "__count__小時前", + "hoursAgo_plural": "__count__小時前", + "minsAgo": "__count__分鐘前", + "minsAgo_plural": "__count__分鐘前", + "secondsAgo": "秒前", + "notTracking": "您的本地分支當前未跟踪遠程分支。", + "statusUnmergedChanged": "您的存儲庫中有未合併的更改。您需要解決衝突並提交結果。", + "repositoryUpToDate": "您的存儲庫是最新的。", + "commitsAhead": "您的倉庫領先遠程倉庫__count__次提交。您現在可以push這些提交。", + "commitsAhead_plural": "您的倉庫領先遠程倉庫__count__次提交。您現在可以push這些提交。", + "commitsBehind": "您的倉庫落後遠程倉庫__count__次提交。您現在可以pull這些提交。", + "commitsBehind_plural": "您的倉庫落後遠程倉庫__count__次提交。您現在可以pull這些提交。", + "commitsAheadAndBehind1": "您的倉庫落後遠程倉庫__count__次提交", + "commitsAheadAndBehind1_plural": "您的倉庫落後遠程倉庫__count__次提交", + "commitsAheadAndBehind2": "領先遠程倉庫__count__次提交。", + "commitsAheadAndBehind2_plural": "領先遠程倉庫__count__次提交。", + "commitsAheadAndBehind3": "您必須先pull遠程提交,然後再進行push。", + "commitsAheadAndBehind3_plural": "您必須先pull遠程提交,然後再進行push。", + "refreshCommitHistory": "刷新提交歷史", + "refreshChanges": "刷新更改" + } + } + }, + "typedInput": { + "type": { + "str": "文字列", + "num": "數字", + "re": "規則運算式", + "bool": "布林", + "json": "JSON", + "bin": "二進位流", + "date": "時間戳記", + "jsonata": "expression", + "env": "env variable" + } + }, + "editableList": { + "add": "添加" + }, + "search": { + "empty": "找不到匹配", + "addNode": "添加一個節點..." + }, + "expressionEditor": { + "functions": "功能", + "functionReference": "Function reference", + "insert": "插入", + "title": "JSONata運算式編輯器", + "test": "Test", + "data": "示例消息", + "result": "結果", + "format": "格式表達方法", + "compatMode": "相容模式啟用", + "compatModeDesc": "

JSONata的相容模式

目前的運算式仍然參考msg,所以將以相容性模式進行評估。請更新運算式,使其不使用msg,因為此模式將在將來刪除。

當JSONata支持首次添加到Node-RED時,它需要運算式引用msg物件。例如msg.payload將用於訪問有效負載。

這樣便不再需要運算式直接針對消息進行評估。要訪問有效負載,運算式應該只是payload.

", + "noMatch": "無匹配結果", + "errors": { + "invalid-expr": "無效的JSONata運算式:\n __message__", + "invalid-msg": "無效的示例JSON消息:\n __message__", + "context-unsupported": "無法測試上下文函數\n $flowContext 或 $globalContext", + "eval": "評估運算式錯誤:\n __message__" + } + }, + "jsEditor": { + "title": "JavaScript 編輯器" + }, + "textEditor": { + "title": "Text 編輯器" + }, + "jsonEditor": { + "title": "JSON編輯器", + "format": "格式化JSON" + }, + "markdownEditor": { + "title": "Markdown 編輯器", + "format": "F使用markdown格式化", + "heading1": "Heading 1", + "heading2": "Heading 2", + "heading3": "Heading 3", + "bold": "粗體", + "italic": "斜體", + "code": "程式碼", + "ordered-list": "編號清單", + "unordered-list": "符號清單", + "quote": "引用", + "link": "連結", + "horizontal-rule": "分隔線", + "toggle-preview": "預覽" + }, + "bufferEditor": { + "title": "緩衝區編輯器", + "modeString": "作為UTF-8字串處理", + "modeArray": "作為JSON陣列處理", + "modeDesc": "

緩衝區編輯器

緩衝區類型被存儲為位元組值的JSON陣列。編輯器將嘗試將輸入的數值解析為JSON陣列。如果它不是有效的JSON,它將被視為UTF-8字串,並被轉換為單個字元代碼點的陣列。

例如,Hello World的值會被轉換為JSON陣列:

[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

" + }, + "projects": { + "config-git": "配置Git客戶端", + "welcome": { + "hello": "你好! 我們已經將“項目”引入了Node-RED。", + "desc0": "這是一種用於管理流程文件的新方法,並且包括對流程的版本控制。", + "desc1": "首先,您可以創建您的第一個項目或從git存儲庫克隆現有項目。", + "desc2": "如果不確定,可以暫時跳過此步驟。 您仍然可以隨時通過“項目”菜單創建第一個項目。", + "create": "創建項目", + "clone": "克隆存儲庫", + "openExistingProject": "打開現有項目", + "not-right-now": "不是現在" + }, + "git-config": { + "setup": "設置您的版本控制客戶端", + "desc0": "Node-RED使用開源工具Git進行版本控制。 它跟踪對項目文件的更改,並允許您將其推送到遠程存儲庫。", + "desc1": "提交一組更改時,Git會使用用戶名和電子郵件地址記錄誰進行了更改。 用戶名可以是您想要的任何名稱-不必是您的真實姓名。", + "desc2": "您的Git客戶端已經配置了以下詳細信息。", + "desc3": "您可以稍後在設置對話框的“ Git config”標籤下更改這些設置。", + "username": "用戶名", + "email": "電子郵件" + }, + "project-details": { + "create": "創建您的項目", + "desc0": "項目被維護為Git存儲庫。 與他人共享您的流程並進行協作更容易。", + "desc1": "您可以創建多個項目,並通過編輯器在它們之間快速切換。", + "desc2": "首先,您的項目需要一個名稱和一個可選的描述。", + "already-exists": "項目已存在", + "must-contain": "必須僅包含A-Z 0-9 _ -", + "project-name": "項目名", + "desc": "描述", + "opt": "可選的" + }, + "clone-project": { + "clone": "克隆項目", + "desc0": "如果您已經有一個包含項目的git存儲庫,則可以對其進行克隆以開始使用。", + "already-exists": "項目已經存在", + "must-contain": "必須僅包含A-Z 0-9 _ -", + "project-name": "項目名", + "no-info-in-url": "網址中不要包含用戶名/密碼", + "git-url": "Git存儲庫URL", + "protocols": "https://, ssh:// or file://", + "auth-failed": "驗證失敗", + "username": "用戶名", + "passwd": "密碼", + "ssh-key": "SSH密鑰", + "passphrase": "密碼短語", + "ssh-key-desc": "在通過ssh克隆存儲庫之前,必須添加SSH密鑰才能訪問它。", + "ssh-key-add": "添加一個ssh密鑰", + "credential-key": "證書加密密鑰", + "cant-get-ssh-key": "錯誤! 無法獲取所選的SSH密鑰路徑。", + "already-exists2": "已存在", + "git-error": "git錯誤", + "connection-failed": "連接失敗", + "not-git-repo": "不是一個git倉庫", + "repo-not-found": "未發現倉庫" + }, + "default-files": { + "create": "創建您的項目文件", + "desc0": "一個項目包含您的流程文件,自述文件和package.json文件。", + "desc1": "它可以包含您要在Git存儲庫中維護的任何其他文件。", + "desc2": "您現有的流程和證書文件將被複製到項目中。", + "flow-file": "流文件", + "credentials-file": "證書文件" + }, + "encryption-config": { + "setup": "設置證書文件的加密", + "desc0": "您的流程證書文件可以被加密以確保其內容安全。", + "desc1": "如果要將這些證書存儲在公共Git存儲庫中,則必須通過提供密鑰短語來對它們進行加密。", + "desc2": "您的流程證書文件當前未加密。", + "desc3": "這意味著任何有權訪問該文件的人都可以讀取其內容,例如密碼和訪問令牌。", + "desc4": "如果要將這些證書存儲在公共Git存儲庫中,則必須通過提供密鑰短語來對它們進行加密。", + "desc5": "當前,使用設置文件中的credentialSecret屬性作為密鑰來加密流憑據文件。", + "desc6": "您的流程證書文件當前使用系統生成的密鑰加密。 您應該為此項目提供一個新的密鑰。", + "desc7": "密鑰將與項目文件分開存儲。 您將需要提供在另一個Node-RED實例中使用該項目的密鑰。", + "credentials": "證書", + "enable": "啟用加密", + "disable": "禁用加密", + "disabled": "禁用的", + "copy": "複製現有密鑰", + "use-custom": "使用自定義密鑰", + "desc8": "憑證文件不會被加密,其內容很容易閱讀", + "create-project-files": "創建項目文件", + "create-project": "創建項目", + "already-exists": "已存在", + "git-error": "git錯誤", + "git-auth-error": "git認證錯誤" + }, + "create-success": { + "success": "您已經成功創建了第一個項目!", + "desc0": "現在,您可以像往常一樣繼續使用Node-RED。", + "desc1": "側欄中的“信息”標籤顯示了您當前的活動項目。名稱旁邊的按鈕可用於訪問項目設置視圖。", + "desc2": "側欄中的“歷史記錄”標籤可用於查看項目中已更改的文件並提交。 它向您顯示了提交的完整歷史記錄,並允許您將更改推送到遠程存儲庫。" + }, + "create": { + "projects": "項目", + "already-exists": "項目已存在", + "must-contain": "必須僅包含A-Z 0-9 _ -", + "no-info-in-url": "網址中不要包含用戶名/密碼", + "open": "打開項目", + "create": "創建項目", + "clone": "克隆倉庫", + "project-name": "項目名", + "desc": "描述", + "opt": "可選的", + "flow-file": "流程文件", + "credentials": "證書", + "enable-encryption": "啟用加密", + "disable-encryption": "禁用加密", + "encryption-key": "加密的密鑰", + "desc0": "用來保護您的憑證的短語", + "desc1": "憑證文件不會被加密,其內容很容易閱讀", + "git-url": "Git倉庫的URL", + "protocols": "https://, ssh:// or file://", + "auth-failed": "驗證失敗", + "username": "用戶名", + "password": "密碼", + "ssh-key": "SSH密鑰", + "passphrase": "密碼短語", + "desc2": "在通過ssh克隆存儲庫之前,必須添加SSH密鑰才能訪問它。", + "add-ssh-key": "添加一個ssh密鑰", + "credentials-encryption-key": "憑證加密密鑰", + "already-exists-2": "已存在", + "git-error": "git錯誤", + "con-failed": "連接失敗", + "not-git": "不是git倉庫", + "no-resource": "找不到存儲庫", + "cant-get-ssh-key-path": "錯誤! 無法獲取所選的SSH密鑰路徑。", + "unexpected_error": "意外的錯誤" + }, + "delete": { + "confirm": "您確定要刪除此項目嗎?" + }, + "create-project-list": { + "search": "搜索您的項目", + "current": "當前的" + }, + "require-clean": { + "confirm": "

您有未部署的更改,這些更改將丟失。

您要繼續嗎?

" + }, + "send-req": { + "auth-req": "存儲庫需要認證", + "username": "用戶名", + "password": "密碼", + "passphrase": "密碼短語", + "retry": "重試", + "update-failed": "無法更新身份驗證", + "unhandled": "未處理的錯誤響應" + }, + "create-branch-list": { + "invalid": "無效的分支", + "create": "創建分支", + "current": "當前的" + }, + "create-default-file-set": { + "no-active": "沒有活動項目就無法創建默認文件集", + "no-empty": "無法在非空項目上創建默認文件集", + "git-error": "git error" + }, + "errors": { + "no-username-email": "您的Git客戶端未配置用戶名/電子郵件。", + "unexpected": "發生了一個意料之外的問題", + "code": "代碼" + } + }, + "editor-tab": { + "properties": "屬性", + "description": "描述", + "appearance": "外觀", + "env": "環境變量" + } +} diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-TW/infotips.json b/packages/node_modules/@node-red/editor-client/locales/zh-TW/infotips.json new file mode 100644 index 000000000..f783f2e8b --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/locales/zh-TW/infotips.json @@ -0,0 +1,23 @@ +{ + "info": { + "tip0" : "您可以用 {{core:delete-selection}} 刪除選擇的節點或連結。", + "tip1" : "{{core:search}} 可以在流程內搜索節點。", + "tip2": "{{core:toggle-sidebar}} 可以顯示或隱藏邊側欄。", + "tip3": "您可以在 {{core:manage-palette}} 中管理節點的控制台。", + "tip4": "側邊欄中會列出流程中所有的配置節點。您可以通過功能表或者 {{core:show-config-tab}} 來訪問這些節點。", + "tip5": "您可以在設定中選擇顯示或隱藏這些提示。", + "tip6": "您可以用[left] [up] [down] [right]鍵來移動被選中的節點。按住[shift]可以更快地移動節點。", + "tip7": "把節點拖到連接上可以向連接中插入節點。", + "tip8": "您可以用 {{core:show-export-dialog}} 來匯出被選中的節點或標籤頁中的流程。", + "tip9": "您可以將流程的json文字檔拖入編輯方塊或 {{core:show-import-dialog}} 來導入流程。", + "tip10": "按住[shift]後按一下並拖動節點可以將該節點的多個連接一併移動到其他節點的埠。", + "tip11": "{{core:show-info-tab}} 可以顯示「資訊」標籤頁。 {{core:show-debug-tab}} 可以顯示「調試」標籤頁。", + "tip12": "按住[ctrl]的同時點擊工作介面可以在節點的對話欄中快速添加節點。", + "tip13": "按住[ctrl]的同時點擊節點的埠或後續節點可以快速連接多個節點。", + "tip14": "按住[shift]的同時點擊節點會選中所有被連接的節點。", + "tip15": "按住[ctrl]的同時點擊節點可以在選中或取消選中節點。", + "tip16": "{{core:show-previous-tab}} 和 {{core:show-next-tab}} 可以切換標籤頁。", + "tip17": "您可以在節點的屬性配置畫面中通過 {{core:confirm-edit-tray}} 來更改設置,或者用 {{core:cancel-edit-tray}} 來取消更改。", + "tip18": "您可以通過點擊 {{core:edit-selected-node}} 來顯示被選中節點的屬性設置畫面。" + } +} diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-TW/jsonata.json b/packages/node_modules/@node-red/editor-client/locales/zh-TW/jsonata.json new file mode 100644 index 000000000..ae62fe068 --- /dev/null +++ b/packages/node_modules/@node-red/editor-client/locales/zh-TW/jsonata.json @@ -0,0 +1,218 @@ +{ + "$string": { + "args": "arg", + "desc": "通過以下的類型轉換規則將參數*arg*轉換成字串:\n\n - 字串不轉換。\n -函數轉換成空的字串。\n - JSON的值無法用數字表示所以用無限大或者NaN(非數)表示。\n - 用’JSON.stringify’函數將其他值轉換成JSON字串。" + }, + "$length": { + "args": "str", + "desc": "輸出字串’str’的字數。如果’str’不是字串,拋出錯誤。" + }, + "$substring": { + "args": "str, start[, length]", + "desc": "輸出`start`位置後的的首次出現的包括`str`的子字串。 如果`length`被指定,那麼的字串中將只包括前`length`個文字。如果`start`是負數則輸出從`str`末尾開始的`length`個文字" + }, + "$substringBefore": { + "args": "str, chars", + "desc": "輸出’str’中首次出現的’chars’之前的子字串,如果’str’中不包括’chars’則輸出’str’。" + }, + "$substringAfter": { + "args": "str, chars", + "desc": "輸出’str’中首次出現的’chars’之後的子字串,如果’str’中不包括’chars’則輸出’str’。" + }, + "$uppercase": { + "args": "str", + "desc": "`將’str’中的所有字母變為大寫後輸出。" + }, + "$lowercase": { + "args": "str", + "desc": "將’str’中的所有字母變為小寫後輸出。" + }, + "$trim": { + "args": "str", + "desc": "將以下步驟應用於`str`來去除所有空白文字並實現標準化。\n\n – 將全部tab定位字元、Enter鍵、換行字元用空白代替。\n- 將連續的空白文字變成一個空白文字。\n- 消除開頭和末尾的空白文字。\n\n如果`str`沒有被指定(即在無輸入參數的情況下調用本函數),將上下文的值作為`str`來使用。 如果`str` 不是字串則拋出錯誤。" + }, + "$contains": { + "args": "str, pattern", + "desc": "字串`str` 和 `pattern`匹配的話輸出`true`,不匹配的情況下輸出 `false`。 不指定`str`的情況下(比如用一個參數調用本函數時)、將上下文的值作為`str`來使用。參數 `pattern`可以為字串或正則表達。" + }, + "$split": { + "args": "str[, separator][, limit]", + "desc": "將參數`str`分解成由子字串組成的陣列。 如果`str`不是字串拋出錯誤。可以省略的參數 `separator`中指定字串`str`的分隔符號。分隔符號可以是文字或規則運算式。在不指定`separator`的情況下、將分隔符號看作空的字串並把`str`拆分成由單個字母組成的陣列。如果`separator`不是字串則拋出錯誤。在可省略的參數`limit`中指定分割後的子字串的最大個數。超出個數的子字串將被捨棄。如果`limit`沒有被指定,`str` 將不考慮子字串的個數而將字串完全分隔。如果`limit`是負數則拋出錯誤。" + }, + "$join": { + "args": "array[, separator]", + "desc": "用可以省略的參數 `separator`來把多個字元串連接。如果`array`不是字串則拋出錯誤。 如果沒有指定`separator`,則用空字串來連接字元(即字串之間沒有`separator`)。 如果`separator`不是字元則拋出錯誤。" + }, + "$match": { + "args": "str, pattern [, limit]", + "desc": "對字串`str`使用規則運算式`pattern`並輸出與`str`相匹配的部分資訊。" + }, + "$replace": { + "args": "str, pattern, replacement [, limit]", + "desc": "在字串`str`中搜索`pattern`並用`replacement`來替換。\n\n可選參數`limit`用來指定替換次數的上限。" + }, + "$now": { + "args": "", + "desc": "生成ISO 8601互換格式的時刻,並作為字串輸出。" + }, + "$base64encode": { + "args": "string", + "desc": "將ASCII格式的字串轉換為Base 64格式。將字串中的文字視作二進位形式的資料處理。包含URI編碼在內的字串文字必須在0x00到0xFF的範圍內,否則不會被支持。" + }, + "$base64decode": { + "args": "string", + "desc": "用UTF-8內碼表將Base 64形式二進位值轉換為字串。" + }, + "$number": { + "args": "arg", + "desc": "用下述的規則將參數 `arg`轉換為數值。:\n\n – 數值不做轉換。\n – 將字串中合法的JSON數値表示轉換成數値。\n – 其他形式的值則拋出錯誤。" + }, + "$abs": { + "args": "number", + "desc": "輸出參數`number`的絕對值。" + }, + "$floor": { + "args": "number", + "desc": "輸出比`number`的值小的最大整數。" + }, + "$ceil": { + "args": "number", + "desc": "輸出比`number`的值大的最小整數。" + }, + "$round": { + "args": "number [, precision]", + "desc": "輸出四捨五入後的參數`number`。可省略的參數 `precision`指定四捨五入後小數點下的位數。" + }, + "$power": { + "args": "base, exponent", + "desc": "輸出底數`base`的`exponent`次冪。" + }, + "$sqrt": { + "args": "number", + "desc": "輸出參數 `number`的平方根。" + }, + "$random": { + "args": "", + "desc": "輸出比0大,比1小的偽亂數。" + }, + "$millis": { + "args": "", + "desc": "返回從UNIX時間 (1970年1月1日 UTC/GMT的午夜)開始到現在的毫秒數。在同一個運算式的測試中所有對`$millis()`的調用將會返回相同的值。" + }, + "$sum": { + "args": "array", + "desc": "輸出陣列`array`的總和。如果`array`不是數值則拋出錯誤。" + }, + "$max": { + "args": "array", + "desc": "輸出陣列`array`的最大值。如果`array`不是數值則拋出錯誤。" + }, + "$min": { + "args": "array", + "desc": "輸出陣列`array`的最小值。如果`array`不是數值則拋出錯誤。。" + }, + "$average": { + "args": "array", + "desc": "輸出陣列`array`的平均數。如果`array`不是數值則拋出錯誤。。" + }, + "$boolean": { + "args": "arg", + "desc": "用下述規則將資料轉換成布林值。:\n\n - 不轉換布林值`Boolean`。\n – 將空的字串`string`轉換為`false`\n – 將不為空的字串`string`轉換為`true`\n – 將為0的數位`number`轉換成`false`\n –將不為0的數位`number`轉換成`true`\n –將`null`轉換成`false`\n –將空的陣列`array`轉換成`false`\n –如果陣列`array`中含有可以轉換成`true`的要素則轉換成`true`\n –如果`array`中沒有可轉換成`true`的要素則轉換成`false`\n – 空的物件`object`轉換成`false`\n – 非空的物件`object`轉換成`true`\n –將函數`function`轉換成`false`" + }, + "$not": { + "args": "arg", + "desc": "輸出做反轉運算後的布林值。首先將`arg`轉換為布林值。" + }, + "$exists": { + "args": "arg", + "desc": "如果算式`arg`的值存在則輸出`true`。如果算式的值不存在(比如指向不存在區域的引用)則輸出`false`。" + }, + "$count": { + "args": "array", + "desc": "輸出陣列中的元素數。" + }, + "$append": { + "args": "array, array", + "desc": "將兩個陣列連接。" + }, + "$sort": { + "args": "array [, function]", + "desc": "輸出排序後的陣列`array`。\n\n如果使用了比較函數`function`,則下述兩個參數需要被指定。\n\n`function(left, right)`\n\n該比較函數是為了比較left和right兩個值而被排序演算法調用的。如果使用者希望left的值被置於right的值之後,那麼該函數必須輸出布林值`true`來表示位置交換。而在不需要位置交換時函數必須輸出`false`。" + }, + "$reverse": { + "args": "array", + "desc": "輸出倒序後的陣列`array`。" + }, + "$shuffle": { + "args": "array", + "desc": "輸出隨機排序後的陣列 `array`。" + }, + "$zip": { + "args": "array, ...", + "desc": "將陣列中的值按索引順序打包後輸出。" + }, + "$keys": { + "args": "object", + "desc": "輸出由物件內的鍵組成的陣列。如果參數是物件的陣列則輸出由所有物件中的鍵去重後組成的佇列。" + }, + "$lookup": { + "args": "object, key", + "desc": "輸出對象中與參數`key`對應的值。如果第一個參數`object`是陣列,那麼陣列中所有的物件都將被搜索並輸出這些物件中與參數`key`對應的值。" + }, + "$spread": { + "args": "object", + "desc": "將物件中的鍵值對分隔成每個要素中只含有一個鍵值對的陣列。如果參數`object`是陣列,那麼返回值的陣列中包含所有物件中的鍵值對。" + }, + "$merge": { + "args": "array<object>", + "desc": "將輸入陣列`objects`中所有的鍵值對合併到一個`object`中並返回。如果輸入陣列的要素中含有重複的鍵,則返回的`object`中將只包含陣列中最後出現要素的值。如果輸入陣列中包括物件以外的元素,則拋出錯誤。" + }, + "$sift": { + "args": "object, function", + "desc": "輸出參數`object`中符合`function`的鍵值對。\n\n`function`必須含有下述參數。\n\n`function(value [, key [, object]])`" + }, + "$each": { + "args": "object, function", + "desc": "將函數`function`應用於`object`中的所有鍵值對並輸出由所有返回值組成的陣列。" + }, + "$map": { + "args": "array, function", + "desc": "將函數`function`應用於陣列`array`中所有的值並輸出由返回值組成的陣列。\n\n`function`中必須含有下述參數。\n\n`function(value [, index [, array]])`" + }, + "$filter": { + "args": "array, function", + "desc": "輸出陣列`array`中符合函數`function`條件的值組成的陣列。\n\n`function`必須包括下述參數。\n\n`function(value [, index [, array]])`" + }, + "$reduce": { + "args": "array, function [, init]", + "desc": "將`function`依次應用於陣列中的各要素值。 其中,前一個要素值的計算結果將參與到下一次的函數運算中。。\n\n函數`function`接受兩個參數並作為中綴標記法中的操作符。\n\n可省略的參數`init`將作為運算的初始值。" + }, + "$flowContext": { + "args": "string", + "desc": "獲取流上下文(流等級的上下文,可以讓所有節點共用)的屬性。" + }, + "$globalContext": { + "args": "string", + "desc": "獲取全域上下文的屬性。" + }, + "$pad": { + "args": "string, width [, char]", + "desc": "根據需要,向字串`string`的副本中填充文字使該字串的字數達到`width`的絕對值並返回填充文字後的字串。\n\n如果`width`的值為正,則向字串`string`的右側填充文字,如果`width`為負,則向字串`string`的左側填充文字。\n\n可選參數`char`用來指定填充的文字。如果未指定該參數,則填充空白文字。" + }, + "$fromMillis": { + "args": "number", + "desc": "將表示從UNIX時間 (1970年1月1日 UTC/GMT的午夜)開始到現在的毫秒數的數值轉換成ISO 8601形式時間戳記的字串。" + }, + "$formatNumber": { + "args": "number, picture [, options]", + "desc": "將`number`轉換成具有`picture`所指定的數值格式的字串。\n\n此函數的功能與XPath F&O 3.1規格中定義的XPath/XQuery函數的fn:format-number功能相一致。參數`picture`用於指定數值的轉換格式,其語法與fn:format-number中的定義一致。\n\n可選的第三參數`options`用來覆蓋預設的局部環境格式,如小數點分隔符號。如果指定該參數,那麼該參數必須是包含name/value對的物件,並且name/value對必須符合XPath F&O 3.1規格中記述的數值格式。" + }, + "$formatBase": { + "args": "number [, radix]", + "desc": "將`number`變換為以參數`radix`的值為基數形式的字串。如果不指定`radix`的值,則默認基數為10。指定的`radix`值必須在2~36之間,否則拋出錯誤。" + }, + "$toMillis": { + "args": "timestamp", + "desc": "將ISO 8601格式的字串`timestamp`轉換為從UNIX時間 (1970年1月1日 UTC/GMT的午夜)開始到現在的毫秒數。如果該字串的格式不正確,則拋出錯誤。" + } +} diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 162b73c75..7f8061c6d 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "1.0.0-beta.2", + "version": "1.0.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/editor-client/src/ace/bin/snippets/nrjavascript.js b/packages/node_modules/@node-red/editor-client/src/ace/bin/snippets/nrjavascript.js index d11f3fc6a..c0f5c2e0d 100644 --- a/packages/node_modules/@node-red/editor-client/src/ace/bin/snippets/nrjavascript.js +++ b/packages/node_modules/@node-red/editor-client/src/ace/bin/snippets/nrjavascript.js @@ -1,4 +1,4 @@ -ace.define("ace/snippets/nrjavascript",[],function(e,t,n){"use strict";t.snippetText=undefined,t.scope="nrjavascript"}); +ace.define("ace/snippets/nrjavascript",[],function(e,t,n){"use strict";t.snippetText='# Prototype\nsnippet proto\n ${1:class_name}.prototype.${2:method_name} = function(${3:first_argument}) {\n ${4:// body...}\n };\n# Function\nsnippet fun\n function ${1?:function_name}(${2:argument}) {\n ${3:// body...}\n }\n# Anonymous Function\nregex /((=)\\s*|(:)\\s*|(\\()|\\b)/f/(\\))?/\nsnippet f\n function${M1?: ${1:functionName}}($2) {\n ${0:$TM_SELECTED_TEXT}\n }${M2?;}${M3?,}${M4?)}\n# Immediate function\ntrigger \\(?f\\(\nendTrigger \\)?\nsnippet f(\n (function(${1}) {\n ${0:${TM_SELECTED_TEXT:/* code */}}\n }(${1}));\n# if\nsnippet if\n if (${1:true}) {\n ${0}\n }\n# if ... else\nsnippet ife\n if (${1:true}) {\n ${2}\n } else {\n ${0}\n }\n# tertiary conditional\nsnippet ter\n ${1:/* condition */} ? ${2:a} : ${3:b}\n# switch\nsnippet switch\n switch (${1:expression}) {\n case \'${3:case}\':\n ${4:// code}\n break;\n ${5}\n default:\n ${2:// code}\n }\n# case\nsnippet case\n case \'${1:case}\':\n ${2:// code}\n break;\n ${3}\n\n# while (...) {...}\nsnippet wh\n while (${1:/* condition */}) {\n ${0:/* code */}\n }\n# try\nsnippet try\n try {\n ${0:/* code */}\n } catch (e) {}\n# do...while\nsnippet do\n do {\n ${2:/* code */}\n } while (${1:/* condition */});\n# Object Method\nsnippet :f\nregex /([,{[])|^\\s*/:f/\n ${1:method_name}: function(${2:attribute}) {\n ${0}\n }${3:,}\n# setTimeout function\nsnippet setTimeout\nregex /\\b/st|timeout|setTimeo?u?t?/\n setTimeout(function() {${3:$TM_SELECTED_TEXT}}, ${1:10});\n# console.log (Firebug)\nsnippet cl\n console.log(${1});\n# return\nsnippet ret\n return ${1:result}\n# for (property in object ) { ... }\nsnippet fori\n for (var ${1:prop} in ${2:Things}) {\n ${0:$2[$1]}\n }\n# hasOwnProperty\nsnippet has\n hasOwnProperty(${1})\n# docstring\nsnippet /**\n /**\n * ${1:description}\n *\n */\nsnippet @par\nregex /^\\s*\\*\\s*/@(para?m?)?/\n @param {${1:type}} ${2:name} ${3:description}\nsnippet @ret\n @return {${1:type}} ${2:description}\n# JSON.parse\nsnippet jsonp\n JSON.parse(${1:jstr});\n# JSON.stringify\nsnippet jsons\n JSON.stringify(${1:object});\n# self-defining function\nsnippet sdf\n var ${1:function_name} = function(${2:argument}) {\n ${3:// initial code ...}\n\n $1 = function($2) {\n ${4:// main code}\n };\n }\n# \nsnippet for-\n for (var ${1:i} = ${2:Things}.length; ${1:i}--; ) {\n ${0:${2:Things}[${1:i}];}\n }\n# for (...) {...}\nsnippet for\n for (var ${1:i} = 0; $1 < ${2:Things}.length; $1++) {\n ${3:$2[$1]}$0\n }\n# for (...) {...} (Improved Native For-Loop)\nsnippet forr\n for (var ${1:i} = ${2:Things}.length - 1; $1 >= 0; $1--) {\n ${3:$2[$1]}$0\n }\n# Node-RED Specific Funcs\nsnippet nodes\n node.send(${1:msg})\nsnippet clone\n RED.util.cloneMessage(${1:msg})\nsnippet nodel\n node.log($1)\nsnippet nodew\n node.warn($1)\nsnippet nodee\n node.error($1)\nsnippet noded\n node.debug($1)\nsnippet done\n node.done($1)\nsnippet flowg\n flow.get($1)\nsnippet flows\n flow.set($1, $2)\nsnippet globalg\n global.get($1)\nsnippet globals\n global.set($1, $2)\n',t.scope="nrjavascript"}); (function() { ace.require(["ace/snippets/nrjavascript"], function(m) { if (typeof module == "object" && typeof exports == "object" && module) { @@ -6,4 +6,3 @@ ace.define("ace/snippets/nrjavascript",[],function(e,t,n){"use strict";t.snippet } }); })(); - \ No newline at end of file diff --git a/packages/node_modules/@node-red/editor-client/src/images/icons/arrow-in.svg b/packages/node_modules/@node-red/editor-client/src/images/icons/arrow-in.svg index a5fcb49af..de75fbe4e 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/icons/arrow-in.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/icons/arrow-in.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/images/subflow_tab.svg b/packages/node_modules/@node-red/editor-client/src/images/subflow_tab.svg index 4acb241e4..0a0f4b45f 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/subflow_tab.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/subflow_tab.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/09.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/09.svg index 29c54b860..8e91ec38e 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/09.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/09.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/az.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/az.svg index e0abbdc4a..90105811f 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/az.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/az.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/bin.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/bin.svg index 0ef91aed8..efc427ac9 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/bin.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/bin.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/bool.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/bool.svg index e2e4297c7..8eee38409 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/bool.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/bool.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/env.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/env.svg index bddd1c082..a24515f30 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/env.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/env.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/expr.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/expr.svg index c6357af50..500ca3926 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/expr.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/expr.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/json.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/json.svg index 713fe7944..978c89149 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/json.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/json.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/re.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/re.svg index c30cbff55..b44c5921c 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/re.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/re.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/images/typedInput/target.svg b/packages/node_modules/@node-red/editor-client/src/images/typedInput/target.svg index b98996701..41ed2e3a6 100644 --- a/packages/node_modules/@node-red/editor-client/src/images/typedInput/target.svg +++ b/packages/node_modules/@node-red/editor-client/src/images/typedInput/target.svg @@ -1 +1 @@ - + diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 930598dd5..51d0e00a0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -14,7 +14,8 @@ * limitations under the License. **/ RED.history = (function() { - var undo_history = []; + var undoHistory = []; + var redoHistory = []; function undoEvent(ev) { var i; @@ -22,52 +23,81 @@ RED.history = (function() { var node; var subflow; var modifiedTabs = {}; + var inverseEv; if (ev) { if (ev.t == 'multi') { + inverseEv = { + t: 'multi', + events: [] + }; len = ev.events.length; for (i=len-1;i>=0;i--) { - undoEvent(ev.events[i]); + var r = undoEvent(ev.events[i]); + inverseEv.events.push(r); } } else if (ev.t == 'replace') { + inverseEv = { + t: 'replace', + config: RED.nodes.createCompleteNodeSet(), + changed: {}, + rev: RED.nodes.version() + }; RED.nodes.clear(); var imported = RED.nodes.import(ev.config); imported[0].forEach(function(n) { if (ev.changed[n.id]) { n.changed = true; + inverseEv.changed[n.id] = true; } }) RED.nodes.version(ev.rev); } else if (ev.t == 'add') { + inverseEv = { + t: "delete", + }; if (ev.nodes) { + inverseEv.nodes = []; for (i=0;i ev.subflow.inputCount) { + inverseEv.subflow.inputs = ev.node.in.slice(ev.subflow.inputCount); ev.node.in.splice(ev.subflow.inputCount); } else if (ev.subflow.inputs.length > 0) { ev.node.in = ev.node.in.concat(ev.subflow.inputs); } } if (ev.subflow.hasOwnProperty('outputCount')) { + inverseEv.subflow.outputCount = ev.node.out.length; if (ev.node.out.length > ev.subflow.outputCount) { + inverseEv.subflow.outputs = ev.node.out.slice(ev.subflow.outputCount); ev.node.out.splice(ev.subflow.outputCount); } else if (ev.subflow.outputs.length > 0) { ev.node.out = ev.node.out.concat(ev.subflow.outputs); } } if (ev.subflow.hasOwnProperty('instances')) { + inverseEv.subflow.instances = []; ev.subflow.instances.forEach(function(n) { + inverseEv.subflow.instances.push(n); var node = RED.nodes.node(n.id); if (node) { node.changed = n.changed; @@ -258,9 +334,11 @@ RED.history = (function() { var outputMap; if (ev.outputMap) { outputMap = {}; + inverseEv.outputMap = {}; for (var port in ev.outputMap) { if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== "-1") { outputMap[ev.outputMap[port]] = port; + inverseEv.outputMap[ev.outputMap[port]] = port; } } } @@ -268,39 +346,107 @@ RED.history = (function() { RED.editor.validateNode(ev.node); } if (ev.links) { + inverseEv.createdLinks = []; for (i=0;i -1) { + return preferredLangs[i] + } + } + return 'end-US' + }, loadNodeCatalog: function(namespace,done) { var languageList = i18n.functions.toLanguages(localStorage.getItem("editor-language")||i18n.detectLanguage()); var toLoad = languageList.length; diff --git a/packages/node_modules/@node-red/editor-client/src/js/keymap.json b/packages/node_modules/@node-red/editor-client/src/js/keymap.json index ad3307ac7..b7d25e160 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/keymap.json +++ b/packages/node_modules/@node-red/editor-client/src/js/keymap.json @@ -30,7 +30,8 @@ "backspace": "core:delete-config-selection", "delete": "core:delete-config-selection", "ctrl-a": "core:select-all-config-nodes", - "ctrl-z": "core:undo" + "ctrl-z": "core:undo", + "ctrl-y": "core:redo" }, "red-ui-workspace": { "backspace": "core:delete-selection", @@ -40,8 +41,17 @@ "ctrl-x": "core:cut-selection-to-internal-clipboard", "ctrl-v": "core:paste-from-internal-clipboard", "ctrl-z": "core:undo", + "ctrl-y": "core:redo", "ctrl-a": "core:select-all-nodes", "shift-?": "core:show-help", + "w": "core:scroll-view-up", + "d": "core:scroll-view-right", + "s": "core:scroll-view-down", + "a": "core:scroll-view-left", + "shift-w": "core:step-view-up", + "shift-d": "core:step-view-right", + "shift-s": "core:step-view-down", + "shift-a": "core:step-view-left", "up": "core:move-selection-up", "right": "core:move-selection-right", "down": "core:move-selection-down", diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 63027aed1..cdf2c8796 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -301,6 +301,17 @@ RED.nodes = (function() { return {links:removedLinks,nodes:removedNodes}; } + function moveNodeToTab(node, z) { + if (nodeTabMap[node.z]) { + delete nodeTabMap[node.z][node.id]; + } + if (!nodeTabMap[z]) { + nodeTabMap[z] = {}; + } + nodeTabMap[z][node.id] = node; + node.z = z; + } + function removeLink(l) { var index = links.indexOf(l); if (index != -1) { @@ -324,7 +335,7 @@ RED.nodes = (function() { } function removeWorkspace(id) { delete workspaces[id]; - delete nodeTabMap[ws.id]; + delete nodeTabMap[id]; workspacesOrder.splice(workspacesOrder.indexOf(id),1); var removedNodes = []; @@ -381,21 +392,25 @@ RED.nodes = (function() { category: sf.category || "subflows", inputs: sf.in.length, outputs: sf.out.length, - color: "#da9", + color: sf.color || "#DDAA99", label: function() { return this.name||RED.nodes.subflow(sf.id).name }, labelStyle: function() { return this.name?"red-ui-flow-node-label-italic":""; }, paletteLabel: function() { return RED.nodes.subflow(sf.id).name }, inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null }, outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null }, + oneditprepare: function() { + RED.subflow.buildEditForm("subflow",this); + RED.subflow.buildPropertiesForm(this); + }, oneditresize: function(size) { - var rows = $("#dialog-form>div:not(.node-input-env-container-row)"); + // var rows = $(".dialog-form>div:not(.node-input-env-container-row)"); var height = size.height; - for (var i=0; idiv.node-input-env-container-row"); - height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); - $("#node-input-env-container").editableList('height',height-80); + // for (var i=0; idiv.node-input-env-container-row"); + // height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom"))); + $("ol.red-ui-editor-subflow-env-list").editableList('height',height); }, set:{ module: "node-red" @@ -494,19 +509,33 @@ RED.nodes = (function() { node[d] = n[d]; } } - if(exportCreds && n.credentials) { + if (exportCreds) { var credentialSet = {}; - node.credentials = {}; - for (var cred in n._def.credentials) { - if (n._def.credentials.hasOwnProperty(cred)) { - if (n._def.credentials[cred].type == 'password') { + if (/^subflow:/.test(node.type) && n.credentials) { + // A subflow instance node can have arbitrary creds + for (var sfCred in n.credentials) { + if (n.credentials.hasOwnProperty(sfCred)) { if (!n.credentials._ || - n.credentials["has_"+cred] != n.credentials._["has_"+cred] || - (n.credentials["has_"+cred] && n.credentials[cred])) { + n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] || + (n.credentials["has_"+sfCred] && n.credentials[sfCred])) { + credentialSet[sfCred] = n.credentials[sfCred]; + } + } + } + } else if (n.credentials) { + node.credentials = {}; + // All other nodes have a well-defined list of possible credentials + for (var cred in n._def.credentials) { + if (n._def.credentials.hasOwnProperty(cred)) { + if (n._def.credentials[cred].type == 'password') { + if (!n.credentials._ || + n.credentials["has_"+cred] != n.credentials._["has_"+cred] || + (n.credentials["has_"+cred] && n.credentials[cred])) { + credentialSet[cred] = n.credentials[cred]; + } + } else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) { credentialSet[cred] = n.credentials[cred]; } - } else if (n.credentials[cred] != null && (!n.credentials._ || n.credentials[cred] != n.credentials._[cred])) { - credentialSet[cred] = n.credentials[cred]; } } } @@ -557,7 +586,7 @@ RED.nodes = (function() { return node; } - function convertSubflow(n) { + function convertSubflow(n, exportCreds) { var node = {}; node.id = n.id; node.type = n.type; @@ -568,6 +597,25 @@ RED.nodes = (function() { node.out = []; node.env = n.env; + if (exportCreds) { + var credentialSet = {}; + // A subflow node can have arbitrary creds + for (var sfCred in n.credentials) { + if (n.credentials.hasOwnProperty(sfCred)) { + if (!n.credentials._ || + n.credentials["has_"+sfCred] != n.credentials._["has_"+sfCred] || + (n.credentials["has_"+sfCred] && n.credentials[sfCred])) { + credentialSet[sfCred] = n.credentials[sfCred]; + } + } + } + if (Object.keys(credentialSet).length > 0) { + node.credentials = credentialSet; + } + } + + node.color = n.color; + n.in.forEach(function(p) { var nIn = {x:p.x,y:p.y,wires:[]}; var wires = links.filter(function(d) { return d.source === p }); @@ -681,7 +729,7 @@ RED.nodes = (function() { } for (i in subflows) { if (subflows.hasOwnProperty(i)) { - nns.push(convertSubflow(subflows[i])); + nns.push(convertSubflow(subflows[i], exportCredentials)); } } for (i in configNodes) { @@ -833,7 +881,7 @@ RED.nodes = (function() { var m = /^subflow:(.+)$/.exec(newNodes[i].type); if (m) { var subflowId = m[1]; - var parent = getSubflow(newNodes[i].z || activeWorkspace); + var parent = getSubflow(activeWorkspace); if (parent) { var err; if (subflowId === parent.id) { @@ -1182,6 +1230,7 @@ RED.nodes = (function() { var nodeTypeArrayReferences = { "catch":"scope", "status":"scope", + "complete": "scope", "link in":"links", "link out":"links" } @@ -1283,8 +1332,13 @@ RED.nodes = (function() { function filterNodes(filter) { var result = []; var searchSet = nodes; - if (filter.hasOwnProperty("z") && Object.hasOwnProperty("values") && nodeTabMap.hasOwnProperty(filter.z) ) { - searchSet = Object.values(nodeTabMap[filter.z]); + var doZFilter = false; + if (filter.hasOwnProperty("z")) { + if (Object.hasOwnProperty("values") && nodeTabMap.hasOwnProperty(filter.z) ) { + searchSet = Object.values(nodeTabMap[filter.z]); + } else { + doZFilter = true; + } } for (var n=0;n= 0 && index < items.length) { + return $(items[index]).data('data'); + } else { + return; + } + }, + indexOf: function(data) { + var items = this.items(); + for (var i=0;i'); + panel.css({ display: "none" }); + panel.appendTo(document.body); + content.appendTo(panel); + var closeCallback; + + function hide() { + $(document).off("mousedown.red-ui-popover-panel-close"); + panel.hide(); + panel.css({ + height: "auto" + }); + panel.remove(); + } + function show(options) { + var closeCallback = options.onclose; + var target = options.target; + var align = options.align || "left"; + + var pos = target.offset(); + var targetWidth = target.width(); + var targetHeight = target.height(); + var panelHeight = panel.height(); + var panelWidth = panel.width(); + + var top = (targetHeight+pos.top); + if (top+panelHeight > $(window).height()) { + top -= (top+panelHeight)-$(window).height() + 5; + } + if (top < 0) { + panelHeight.height(panelHeight+top) + top = 0; + } + if (align === "left") { + panel.css({ + top: top+"px", + left: (pos.left)+"px", + }); + } else if(align === "right") { + panel.css({ + top: top+"px", + left: (pos.left-panelWidth)+"px", + }); + } + panel.slideDown(100); + + $(document).on("mousedown.red-ui-popover-panel-close", function(event) { + if(!$(event.target).closest(panel).length && !$(event.target).closest(".red-ui-editor-dialog").length) { + if (closeCallback) { + closeCallback(); + } + hide(); + } + // if ($(event.target).closest(target).length) { + // event.preventDefault(); + // } + }) + } + return { + container: panel, + show:show, + hide:hide + } } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js index 288334c06..249d7daa7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js @@ -19,6 +19,9 @@ RED.tabs = (function() { var defaultTabIcon = "fa fa-lemon-o"; + var dragActive = false; + var dblClickTime; + var dblClickArmed = false; function createTabs(options) { var tabs = {}; @@ -201,12 +204,21 @@ RED.tabs = (function() { } function onTabClick(evt) { - evt.preventDefault(); + if (dragActive) { + return + } + if (dblClickTime && Date.now()-dblClickTime < 400) { + dblClickTime = 0; + dblClickArmed = true; + return onTabDblClick.call(this,evt); + } + dblClickTime = Date.now(); + var currentTab = ul.find("li.red-ui-tab.active"); var thisTab = $(this).parent(); var fireSelectionChanged = false; if (options.onselect) { - if (evt.metaKey) { + if (evt.metaKey || evt.ctrlKey) { if (thisTab.hasClass("selected")) { thisTab.removeClass("selected"); if (thisTab[0] !== currentTab[0]) { @@ -267,7 +279,6 @@ RED.tabs = (function() { if (fireSelectionChanged) { selectionChanged(); } - return false; } function updateScroll() { @@ -289,7 +300,6 @@ RED.tabs = (function() { } function onTabDblClick(evt) { evt.preventDefault(); - evt.stopPropagation(); if (evt.metaKey || evt.shiftKey) { return; } @@ -418,7 +428,11 @@ RED.tabs = (function() { } - ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick); + ul.find("li.red-ui-tab a") + .on("mouseup",onTabClick) + .on("click", function(evt) {evt.preventDefault(); }) + .on("dblclick", function(evt) {evt.stopPropagation(); evt.preventDefault(); }) + setTimeout(function() { updateTabWidths(); },0); @@ -524,8 +538,9 @@ RED.tabs = (function() { RED.popover.tooltip($(pinnedLink), tab.name, tab.action); } - link.on("click",onTabClick); - link.on("dblclick",onTabDblClick); + link.on("mouseup",onTabClick); + link.on("click", function(evt) { evt.preventDefault(); }) + link.on("dblclick", function(evt) { evt.stopPropagation(); evt.preventDefault(); }) if (tab.closeable) { @@ -560,6 +575,8 @@ RED.tabs = (function() { axis:"x", distance: 20, start: function(event,ui) { + if (dblClickArmed) { dblClickArmed = false; return false } + dragActive = true; originalTabOrder = []; tabElements = []; ul.children().each(function(i) { @@ -615,6 +632,7 @@ RED.tabs = (function() { } }, stop: function(event,ui) { + dragActive = false; ul.children().css({position:"relative",left:"",transition:""}); if (!li.hasClass('active')) { li.css({zIndex:""}); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js index 999606912..398d35c85 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js @@ -39,8 +39,8 @@ var baseClass = this.options.baseClass || "red-ui-button"; var enabledIcon = this.options.enabledIcon || "fa-check-square-o"; var disabledIcon = this.options.disabledIcon || "fa-square-o"; - var enabledLabel = this.options.enabledLabel || RED._("editor:workspace.enabled"); - var disabledLabel = this.options.disabledLabel || RED._("editor:workspace.disabled"); + var enabledLabel = this.options.hasOwnProperty('enabledLabel') ? this.options.enabledLabel : RED._("editor:workspace.enabled"); + var disabledLabel = this.options.hasOwnProperty('disabledLabel') ? this.options.disabledLabel : RED._("editor:workspace.disabled"); this.element.css("display","none"); this.element.on("focus", function() { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js index 0e391a5b8..ed04733c9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js @@ -410,7 +410,7 @@ return; } if (container.hasClass("expanded")) { - done && done(); + if (done) { done() } return; } if (!container.hasClass("built") && (item.deferBuild || typeof item.children === 'function')) { @@ -435,7 +435,7 @@ spinner.remove(); } } - done && done(); + if (done) { done() } that._trigger("childrenloaded",null,item) } if (typeof item.children === 'function') { @@ -457,7 +457,7 @@ } else { item.treeList.childList.slideDown('fast'); } - done && done(); + if (done) { done() } } container.addClass("expanded"); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index daef61f64..a3f066223 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -81,7 +81,7 @@ } }, re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"}, - date: {value:"date",label:"timestamp",hasValue:false}, + date: {value:"date",label:"timestamp",icon:"fa fa-clock-o",hasValue:false}, jsonata: { value: "jsonata", label: "expression", @@ -164,6 +164,84 @@ } }) } + }, + cred:{ + value:"cred", + label:"credential", + icon:"fa fa-lock", + inputType: "password", + valueLabel: function(container,value) { + var that = this; + container.css("pointer-events","none"); + this.elementDiv.hide(); + var buttons = $('
').css({ + position: "absolute", + right:"6px", + top: "6px", + "pointer-events":"all" + }).appendTo(container); + var eyeButton = $('').css({ + width:"20px" + }).appendTo(buttons).on("click", function(evt) { + evt.preventDefault(); + var currentType = that.input.attr("type"); + if (currentType === "text") { + that.input.attr("type","password"); + eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); + setTimeout(function() { + that.input.focus(); + },50); + } else { + that.input.attr("type","text"); + eyeCon.removeClass("fa-eye").addClass("fa-eye-slash"); + setTimeout(function() { + that.input.focus(); + },50); + } + }).hide(); + var eyeCon = $('').css("margin-left","-1px").appendTo(eyeButton); + + if (value === "__PWRD__") { + var innerContainer = $('
').css({ + padding:"6px 6px", + borderRadius:"4px" + }).addClass("red-ui-typedInput-value-label-inactive").appendTo(container); + var editButton = $('').appendTo(buttons).on("click", function(evt) { + evt.preventDefault(); + innerContainer.hide(); + container.css("background","none"); + container.css("pointer-events","none"); + that.input.val(""); + that.element.val(""); + that.elementDiv.show(); + editButton.hide(); + cancelButton.show(); + eyeButton.show(); + setTimeout(function() { + that.input.focus(); + },50); + }); + var cancelButton = $('').css("margin-left","3px").appendTo(buttons).on("click", function(evt) { + evt.preventDefault(); + innerContainer.show(); + container.css("background",""); + that.input.val("__PWRD__"); + that.element.val("__PWRD__"); + that.elementDiv.hide(); + editButton.show(); + cancelButton.hide(); + eyeButton.hide(); + that.input.attr("type","password"); + eyeCon.removeClass("fa-eye-slash").addClass("fa-eye"); + + }).hide(); + } else { + container.css("background","none"); + container.css("pointer-events","none"); + this.elementDiv.show(); + eyeButton.show(); + } + } } }; var nlsd = false; @@ -220,6 +298,8 @@ that.input.attr(d,m); }); + this.defaultInputType = this.input.attr('type'); + this.uiSelect.addClass("red-ui-typedInput-container"); this.element.attr('type','hidden'); @@ -298,7 +378,8 @@ that.uiSelect.addClass('red-ui-typedInput-focus'); }); - this.optionExpandButton = $('').appendTo(this.uiSelect); + this.optionExpandButton = $('').appendTo(this.uiSelect); + this.optionExpandButtonIcon = $('').appendTo(this.optionExpandButton); this.type(this.options.default||this.typeList[0].value); }catch(err) { console.log(err.stack); @@ -336,6 +417,17 @@ menu.css({ height: "auto" }); + + if (menu.opts.multiple) { + var selected = []; + menu.find('input[type="checkbox"]').each(function() { + if ($(this).prop("checked")) { + selected.push($(this).data('value')) + } + }) + menu.callback(selected); + } + if (this.elementDiv.is(":visible")) { this.input.trigger("focus"); } else if (this.optionSelectTrigger.is(":visible")){ @@ -344,10 +436,12 @@ this.selectTrigger.trigger("focus"); } }, - _createMenu: function(opts,callback) { + _createMenu: function(menuOptions,opts,callback) { var that = this; - var menu = $("
").addClass("red-ui-typedInput-options"); - opts.forEach(function(opt) { + var menu = $("
").addClass("red-ui-typedInput-options red-ui-editor-dialog"); + menu.opts = opts; + menu.callback = callback; + menuOptions.forEach(function(opt) { if (typeof opt === 'string') { opt = {value:opt,label:opt}; } @@ -369,12 +463,20 @@ if (!opt.icon && !opt.label) { op.text(opt.value); } + var cb; + if (opts.multiple) { + cb = $('').css("pointer-events","none").data('value',opt.value).prependTo(op).on("mousedown", function(evt) { evt.preventDefault() }); + } op.on("click", function(event) { event.preventDefault(); event.stopPropagation(); - callback(opt.value); - that._hideMenu(menu); + if (!opts.multiple) { + callback(opt.value); + that._hideMenu(menu); + } else { + cb.prop("checked",!cb.prop("checked")); + } }); }); menu.css({ @@ -398,9 +500,6 @@ } evt.stopPropagation(); }) - - - return menu; }, @@ -409,11 +508,22 @@ this.disarmClick = false; return } + if (menu.opts.multiple) { + var selected = {}; + this.value().split(",").forEach(function(f) { + selected[f] = true; + }) + menu.find('input[type="checkbox"]').each(function() { + $(this).prop("checked",selected[$(this).data('value')]) + }) + } + + var that = this; var pos = relativeTo.offset(); var height = relativeTo.height(); var menuHeight = menu.height(); - var top = (height+pos.top-3); + var top = (height+pos.top); if (top+menuHeight > $(window).height()) { top -= (top+menuHeight)-$(window).height()+5; } @@ -423,7 +533,7 @@ } menu.css({ top: top+"px", - left: (2+pos.left)+"px", + left: (pos.left)+"px", }); menu.slideDown(100); this._delay(function() { @@ -471,7 +581,7 @@ this._getLabelWidth(this.selectTrigger, function(labelWidth) { that.elementDiv.css('left',labelWidth+"px"); that.valueLabelContainer.css('left',labelWidth+"px"); - if (that.optionExpandButton.is(":visible")) { + if (that.optionExpandButton.shown) { that.elementDiv.css('right',"22px"); that.valueLabelContainer.css('right',"22px"); } else { @@ -496,7 +606,7 @@ } else { that.optionSelectLabel.css({'left':'0'}) that.optionSelectTrigger.css({'width':'calc( 100% - '+labelWidth+'px )'}); - if (!that.optionExpandButton.is(":visible")) { + if (!that.optionExpandButton.shown) { that.elementDiv.css({'right':0}); that.input.css({ 'border-top-right-radius': '4px', @@ -511,25 +621,35 @@ _updateOptionSelectLabel: function(o) { var opt = this.typeMap[this.propertyType]; this.optionSelectLabel.empty(); - if (o.icon) { - if (o.icon.indexOf("<") === 0) { - $(o.icon).prependTo(this.optionSelectLabel); - } else if (o.icon.indexOf("/") !== -1) { - // url - $('',{src:mapDeprecatedIcon(o.icon),style:"height: 18px;"}).prependTo(this.optionSelectLabel); + if (this.typeMap[this.propertyType].valueLabel) { + if (opt.multiple) { + this.typeMap[this.propertyType].valueLabel.call(this,this.optionSelectLabel,o); } else { - // icon class - $('',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel); + this.typeMap[this.propertyType].valueLabel.call(this,this.optionSelectLabel,o.value); + } + } else if (!opt.multiple) { + if (o.icon) { + if (o.icon.indexOf("<") === 0) { + $(o.icon).prependTo(this.optionSelectLabel); + } else if (o.icon.indexOf("/") !== -1) { + // url + $('',{src:mapDeprecatedIcon(o.icon),style:"height: 18px;"}).prependTo(this.optionSelectLabel); + } else { + // icon class + $('',{class:"red-ui-typedInput-icon "+o.icon}).prependTo(this.optionSelectLabel); + } + } else if (o.label) { + this.optionSelectLabel.text(o.label); + } else { + this.optionSelectLabel.text(o.value); + } + if (opt.hasValue) { + this.optionValue = o.value; + this._resize(); + this.input.trigger('change',this.propertyType,this.value()); } - } else if (o.label) { - this.optionSelectLabel.text(o.label); } else { - this.optionSelectLabel.text(o.value); - } - if (opt.hasValue) { - this.optionValue = o.value; - this._resize(); - this.input.trigger('change',this.propertyType,this.value()); + this.optionSelectLabel.text(o.length+" selected"); } }, _destroy: function() { @@ -558,7 +678,7 @@ if (this.menu) { this.menu.remove(); } - this.menu = this._createMenu(this.typeList, function(v) { that.type(v) }); + this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) }); if (currentType && !this.typeMap.hasOwnProperty(currentType)) { this.type(this.typeList[0].value); } else { @@ -572,38 +692,51 @@ this._resize(); }, value: function(value) { + var that = this; + var opt = this.typeMap[this.propertyType]; if (!arguments.length) { var v = this.input.val(); - if (this.typeMap[this.propertyType].export) { - v = this.typeMap[this.propertyType].export(v,this.optionValue) + if (opt.export) { + v = opt.export(v,this.optionValue) } return v; } else { - var selectedOption; - if (this.typeMap[this.propertyType].options) { - for (var i=0;i',{src:mapDeprecatedIcon(opt.icon),style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel); } else { - $('',{class:"red-ui-typedInput-icon "+opt.icon}).prependTo(this.selectLabel); + $('',{class:"red-ui-typedInput-icon "+opt.icon,style:"min-width: 13px; margin-right: 4px;"}).prependTo(this.selectLabel); } - } else { + } + if (opt.hasValue === false || (opt.showLabel !== false && !opt.icon)) { this.selectLabel.text(opt.label); } if (this.optionMenu) { @@ -646,6 +780,7 @@ if (opt.options) { if (this.optionExpandButton) { this.optionExpandButton.hide(); + this.optionExpandButton.shown = false; } if (this.optionSelectTrigger) { this.optionSelectTrigger.show(); @@ -668,36 +803,50 @@ if (!that.activeOptions.hasOwnProperty(that.optionValue)) { that.optionValue = null; } - this.optionMenu = this._createMenu(opt.options,function(v){ - that._updateOptionSelectLabel(that.activeOptions[v]); - if (!opt.hasValue) { - that.value(that.activeOptions[v].value) - } - }); + var op; if (!opt.hasValue) { - var currentVal = this.input.val(); var validValue = false; - for (var i=0;i'); + var content = opt.expand.content.call(that,container); + var panel = RED.popover.panel(container); + panel.container.css({ + width:that.valueLabelContainer.width() + }); + if (opt.expand.minWidth) { + panel.container.css({ + minWidth: opt.expand.minWidth+"px" + }); + } + panel.show({ + target:that.optionExpandButton, + onclose:content.onclose, + align: "right" + }); + } }) } else { + this.optionExpandButton.shown = false; this.optionExpandButton.hide(); } } + this._trigger("typechange",null,this.propertyType); this.input.trigger('change',this.propertyType,this.value()); } if (!image) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index cdf84d02f..9e91410a9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -334,8 +334,7 @@ RED.deploy = (function() { var invalidNodes = []; RED.nodes.eachNode(function(node) { - hasInvalid = hasInvalid || !node.valid; - if (!node.valid) { + if (!node.valid && !node.d) { invalidNodes.push(getNodeInfo(node)); } if (node.type === "unknown") { @@ -345,6 +344,7 @@ RED.deploy = (function() { } }); hasUnknown = unknownNodes.length > 0; + hasInvalid = invalidNodes.length > 0; var unusedConfigNodes = []; RED.nodes.eachConfig(function(node) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js b/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js index 4ebdcdce9..13a157e21 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/diff.js @@ -401,7 +401,7 @@ RED.diff = (function() { defaults:{}, icon:"subflow.svg", category: "subflows", - color: "#da9" + color: "#DDAA99" }, tab:currentConfig.subflows[subflowId] } @@ -424,7 +424,7 @@ RED.diff = (function() { defaults:{}, icon:"subflow.svg", category: "subflows", - color: "#da9" + color: "#DDAA99" }, tab:newConfig.subflows[subflowId], newTab:newConfig.subflows[subflowId] @@ -445,7 +445,7 @@ RED.diff = (function() { defaults:{}, icon:"subflow.svg", category: "subflows", - color: "#da9" + color: "#DDAA99" }, tab:remoteDiff.newConfig.subflows[subflowId], remoteTab: remoteDiff.newConfig.subflows[subflowId] @@ -551,7 +551,7 @@ RED.diff = (function() { def = { icon:"subflow.svg", category: "subflows", - color: "#da9", + color: "#DDAA99", defaults:{name:{value:""}} } } else { @@ -1029,9 +1029,9 @@ RED.diff = (function() { } var localSelectDiv = $('