Merge branch 'dev' into make-split/join-more-flexible
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -30,5 +30,5 @@ the [forum](https://discourse.nodered.org) or
|
|||||||
|
|
||||||
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
|
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
|
||||||
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
|
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
|
||||||
- [ ] I have run `grunt` to verify the unit tests pass
|
- [ ] I have run `npm run test` to verify the unit tests pass
|
||||||
- [ ] I have added suitable unit tests to cover the new/changed functionality
|
- [ ] I have added suitable unit tests to cover the new/changed functionality
|
||||||
|
15
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
groups:
|
||||||
|
github-actions:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
12
.github/workflows/release.yml
vendored
@ -14,25 +14,25 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out node-red repository
|
- name: Check out node-red repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: 'node-red'
|
path: 'node-red'
|
||||||
- name: Check out node-red-docker repository
|
- name: Check out node-red-docker repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'node-red/node-red-docker'
|
repository: 'node-red/node-red-docker'
|
||||||
path: 'node-red-docker'
|
path: 'node-red-docker'
|
||||||
- name: Check out node-red.github.io repository
|
- name: Check out node-red.github.io repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'node-red/node-red.github.io'
|
repository: 'node-red/node-red.github.io'
|
||||||
path: 'node-red.github.io'
|
path: 'node-red.github.io'
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
- run: node ./node-red/.github/scripts/update-node-red-docker.js
|
- run: node ./node-red/.github/scripts/update-node-red-docker.js
|
||||||
- name: Create Docker Pull Request
|
- name: Create Docker Pull Request
|
||||||
uses: peter-evans/create-pull-request@v2
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||||
committer: GitHub <noreply@github.com>
|
committer: GitHub <noreply@github.com>
|
||||||
@ -48,7 +48,7 @@ jobs:
|
|||||||
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
|
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
|
||||||
- run: node ./node-red/.github/scripts/update-node-red-website.js
|
- run: node ./node-red/.github/scripts/update-node-red-website.js
|
||||||
- name: Create Website Pull Request
|
- name: Create Website Pull Request
|
||||||
uses: peter-evans/create-pull-request@v2
|
uses: peter-evans/create-pull-request@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||||
committer: GitHub <noreply@github.com>
|
committer: GitHub <noreply@github.com>
|
||||||
|
12
.github/workflows/tests.yml
vendored
@ -12,16 +12,15 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
permissions:
|
permissions:
|
||||||
checks: write # for coverallsapp/github-action to create new checks
|
|
||||||
contents: read # for actions/checkout to fetch code
|
contents: read # for actions/checkout to fetch code
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [16, 18, 20]
|
node-version: [18, 20]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
@ -29,8 +28,3 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
npm run test
|
npm run test
|
||||||
# - name: Publish to coveralls.io
|
|
||||||
# if: ${{ matrix.node-version == 16 }}
|
|
||||||
# uses: coverallsapp/github-action@v1.1.2
|
|
||||||
# with:
|
|
||||||
# github-token: ${{ github.token }}
|
|
||||||
|
1
.gitignore
vendored
@ -27,3 +27,4 @@ docs
|
|||||||
.vscode
|
.vscode
|
||||||
.nyc_output
|
.nyc_output
|
||||||
sync.ffs_db
|
sync.ffs_db
|
||||||
|
package-lock.json
|
||||||
|
120
CHANGELOG.md
@ -1,3 +1,123 @@
|
|||||||
|
#### 3.1.5: Maintenance Release
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Fix require of dns module (#4562) @knolleary
|
||||||
|
- Ensure global creds object is initialised when adding first cred (#4561) @knolleary
|
||||||
|
|
||||||
|
#### 3.1.4: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Highlight errors in config node sidebar (#4529) @knolleary
|
||||||
|
- Improve feedback in import dialog to show conflicted nodes (#4550) @knolleary
|
||||||
|
- Modify node users info in config editor footer (#4528) @knolleary
|
||||||
|
- Handle modified-nodes deploy after replacing unknown config node (#4556) @knolleary
|
||||||
|
- Handle undefined default export when importing module (#4539) @knolleary
|
||||||
|
- Fix icon scaling for non .svg icons (#4491) @ralphwetzel
|
||||||
|
- (convertNode) Do not create the credentials object if there is nothing to export (#4544) @GogoVega
|
||||||
|
- Ensure subflow instance node has g property set (#4538) @knolleary
|
||||||
|
- Handle importing flow with existing subflow and instance node (#4546) @knolleary
|
||||||
|
- Update index.mst (#4483) @gorenje
|
||||||
|
- Include top level property name when copying path from context (#4527) @knolleary
|
||||||
|
- Add handling to disable items on context menu (#4500) @kazuhitoyokoi
|
||||||
|
- Focus Quick Add dialog from context menu (#4516) @kazuhitoyokoi
|
||||||
|
- Fix subflow ports in Quick Add dialog (#4518) @kazuhitoyokoi
|
||||||
|
- Fix location of subflow ports in palette (#4502) @kazuhitoyokoi
|
||||||
|
- Client/Editor Events: fix off-in-on pattern emulating once (#4484) @gorenje
|
||||||
|
- Restore caching busting functionality without using explict version number (#4512) @knolleary
|
||||||
|
- Do not translate the list of available languages (#4531) @GogoVega
|
||||||
|
- Add French translation of v3.1.3 changes (#4477) @GogoVega
|
||||||
|
- i18n(es-ES) Spanish Spain translation (#4495) @joebordes
|
||||||
|
- Add missing validation messages (#4487) @GogoVega
|
||||||
|
- Add Japanese translations for v3.1.3 (#4498) @kazuhitoyokoi
|
||||||
|
- Replace `rename` by `edit` for the menu flow label (#4506) @GogoVega
|
||||||
|
- Update editor.json fix typo in German translation (#4552) @guidoffm
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Bump the github-actions group with 1 update (#4554) @app/dependabot
|
||||||
|
- Clone objects types when getting env values (#4519) @knolleary
|
||||||
|
- Ensure global-config credential env vars are merged on deploy (#4526) @knolleary
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- 21-httprequest.js remove unused code, because of broken use of toLowercase (#4522) @gorenje
|
||||||
|
|
||||||
|
#### 3.1.3: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Add missing en-us messages (#4475) @knolleary
|
||||||
|
|
||||||
|
#### 3.1.2: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Relax some node validators to allow undefined value (#4471) @knolleary
|
||||||
|
- Fix switch validation of typeof field (#4465) @knolleary
|
||||||
|
- Use move cursor when hovering on group border (#4467) @knolleary
|
||||||
|
- Added action list Chinese (Simplified and Traditional) translation + v3.1.1 changes (#4470) @wangyiyi2056
|
||||||
|
- Add French translation of `action-list` + v3.1.1 changes (#4466) @GogoVega
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Ensure nested groups inside subflows have their g props remapped (#4472) @knolleary
|
||||||
|
|
||||||
|
#### 3.1.1: Maintenance Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Fix debug filter (#4461) @knolleary
|
||||||
|
- Fix various issues with debug pop-out window (#4459) @knolleary
|
||||||
|
- Ensure subflow instances keep track of their groups (#4457) @knolleary
|
||||||
|
- Fix `validateNodeProperty` without validator provided (#4455) @GogoVega
|
||||||
|
- Debounce node-removed notifications (#4453) @knolleary
|
||||||
|
- Don't try to load the parents of the first commit (#4448) @bonanitech
|
||||||
|
- Allow a theme to specifiy which theme mermaid should use (#4441) @knolleary
|
||||||
|
- Update browser title with flow name if set (#4427) @knolleary
|
||||||
|
- Ensure typeSearch handles undefined node definitions (#4423) @knolleary
|
||||||
|
- Ensure group w/h are imported if present (#4426) @knolleary
|
||||||
|
- Hide node status background when there is no status to show (#4425) @knolleary
|
||||||
|
- Add a close button to the restart-required notification (#4407) @knolleary
|
||||||
|
- Extend typedInput "num" type validity check to NaN, binary, octal & hex (#4371) @ralphwetzel
|
||||||
|
- Fix unintended new line in node name (#4399) @kazuhitoyokoi
|
||||||
|
- Ctrl-Enter does not close tray (Monaco) #4377 (#4382) @hazymat
|
||||||
|
- fix buffer viewer to handle 0b style binary (#4393) @dceejay
|
||||||
|
- Rework mermaid integration to support off-DOM rendering (#4364) @knolleary
|
||||||
|
- Add missing nls labels to context menu (#4365) @knolleary
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- Bump the github-actions group with 2 updates (#4404) @app/dependabot
|
||||||
|
- Handle unknown node reference inside subflow module (#4460) @knolleary
|
||||||
|
- Add modules.install audit event when external module installed (#4452) @knolleary
|
||||||
|
- Allow import of modules with subpath in specifier (#4451) @knolleary
|
||||||
|
- Update node-red-admin version (#4438) @knolleary
|
||||||
|
- Handle false-like env vars properly (#4411) @knolleary
|
||||||
|
- Only save settings once during node load process (#4409) @knolleary
|
||||||
|
- Ensure global-config nodes lookup cred values properly (#4405) @knolleary
|
||||||
|
- Handle credential env var evaluation when no value set (#4362) @knolleary
|
||||||
|
- Don't commit package-lock.json (#4354) @bonanitech
|
||||||
|
- Fix env evaluation when one env references another in the same object (#4361) @knolleary
|
||||||
|
- Add dependabot for Github Actions (#4312) @Rotzbua
|
||||||
|
- Update outdated Github Actions (#4311) @Rotzbua
|
||||||
|
- github: Request `npm run test` in PR template (#4348) @ZJvandeWeg
|
||||||
|
- Add French translation of v3.1.0-beta.4 changes + slight improvements (#4329) @GogoVega
|
||||||
|
- Handle nodes with multiple input handlers properly (#4332) @knolleary
|
||||||
|
- Soften the language around unrequited PRs (#4351) @knolleary
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- CSV: make CSV export way faster by not re-allocating and handling huge string (#4349) @Fadoli
|
||||||
|
- Delay: Fix regression in delay node to not pass on msg.reset (#4350) @dceejay
|
||||||
|
- Link Call: Handle undefined linkType value for existing link-call nodes (#4331) @knolleary
|
||||||
|
- MQTT: Guard against node.broker being undefined (#4454) @knolleary
|
||||||
|
- MQTT: check topic length > 0 before publish (#4416) @dceejay
|
||||||
|
- Switch/Change: Improve validation of switch/change node rules (#4368) @knolleary
|
||||||
|
- Template: Fix height of description editor in template node (#4346) @kazuhitoyokoi
|
||||||
|
- Various: Add validators to any fields using msg-typed Input (#4440) @knolleary
|
||||||
|
|
||||||
#### 3.1.0: Milestone Release
|
#### 3.1.0: Milestone Release
|
||||||
|
|
||||||
Editor
|
Editor
|
||||||
|
@ -16,6 +16,9 @@ behavior to the project's core team at team@nodered.org.
|
|||||||
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
||||||
search the list to see if your issue has already been raised.
|
search the list to see if your issue has already been raised.
|
||||||
|
|
||||||
|
If your issue is more of a question on how to do something with Node-RED, please
|
||||||
|
consider using the [community forum](https://discourse.nodered.org/).
|
||||||
|
|
||||||
A good bug report is one that make it easy for us to understand what you were
|
A good bug report is one that make it easy for us to understand what you were
|
||||||
trying to do and what went wrong.
|
trying to do and what went wrong.
|
||||||
|
|
||||||
@ -35,14 +38,18 @@ For feature requests, please raise them on the [forum](https://discourse.nodered
|
|||||||
## Pull-Requests
|
## Pull-Requests
|
||||||
|
|
||||||
If you want to raise a pull-request with a new feature, or a refactoring
|
If you want to raise a pull-request with a new feature, or a refactoring
|
||||||
of existing code, it may well get rejected if you haven't discussed it on
|
of existing code, please come and discuss it with us first. We prefer to
|
||||||
the [forum](https://discourse.nodered.org) first.
|
do it that way to make sure your time and effort is well spent on something
|
||||||
|
that fits with our goals.
|
||||||
|
|
||||||
|
If you've got a bug-fix or similar for us, then you are most welcome to
|
||||||
|
get it raised - just make sure you link back to the issue it's fixing and
|
||||||
|
try to include some tests!
|
||||||
|
|
||||||
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
|
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
|
||||||
It is an online process and quick to do. If you raise a pull-request without
|
It is an online process and quick to do. If you raise a pull-request without
|
||||||
having signed the CLA, you will be prompted to do so automatically.
|
having signed the CLA, you will be prompted to do so automatically.
|
||||||
|
|
||||||
|
|
||||||
### Code Branches
|
### Code Branches
|
||||||
|
|
||||||
When raising a PR for a fix or a new feature, it is important to target the right branch.
|
When raising a PR for a fix or a new feature, it is important to target the right branch.
|
||||||
|
@ -151,7 +151,6 @@ module.exports = function(grunt) {
|
|||||||
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
|
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/history.js",
|
"packages/node_modules/@node-red/editor-client/src/js/history.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
|
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js",
|
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
|
||||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
|
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
"is-utf8": "0.2.1",
|
"is-utf8": "0.2.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"jsonata": "1.8.6",
|
"jsonata": "2.0.4",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"media-typer": "1.1.0",
|
"media-typer": "1.1.0",
|
||||||
"memorystore": "1.6.7",
|
"memorystore": "1.6.7",
|
||||||
@ -64,7 +64,7 @@
|
|||||||
"mqtt": "4.3.7",
|
"mqtt": "4.3.7",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"mustache": "4.2.0",
|
"mustache": "4.2.0",
|
||||||
"node-red-admin": "^3.1.0",
|
"node-red-admin": "^3.1.2",
|
||||||
"node-watch": "0.7.4",
|
"node-watch": "0.7.4",
|
||||||
"nopt": "5.0.0",
|
"nopt": "5.0.0",
|
||||||
"oauth2orize": "1.11.1",
|
"oauth2orize": "1.11.1",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"jquery-i18next": "1.2.1",
|
"jquery-i18next": "1.2.1",
|
||||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||||
"marked": "4.3.0",
|
"marked": "4.3.0",
|
||||||
"mermaid": "^9.4.3",
|
"mermaid": "^10.4.0",
|
||||||
"minami": "1.2.3",
|
"minami": "1.2.3",
|
||||||
"mocha": "9.2.2",
|
"mocha": "9.2.2",
|
||||||
"node-red-node-test-helper": "^0.3.2",
|
"node-red-node-test-helper": "^0.3.2",
|
||||||
@ -122,6 +122,6 @@
|
|||||||
"supertest": "6.3.3"
|
"supertest": "6.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ module.exports = {
|
|||||||
store: req.query['store'],
|
store: req.query['store'],
|
||||||
req: apiUtils.getRequestLogObject(req)
|
req: apiUtils.getRequestLogObject(req)
|
||||||
}
|
}
|
||||||
|
if (req.query['keysOnly'] !== undefined) {
|
||||||
|
opts.keysOnly = true
|
||||||
|
}
|
||||||
runtimeAPI.context.getValue(opts).then(function(result) {
|
runtimeAPI.context.getValue(opts).then(function(result) {
|
||||||
res.json(result);
|
res.json(result);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
|
@ -51,7 +51,7 @@ module.exports = {
|
|||||||
|
|
||||||
var ui = require("./ui");
|
var ui = require("./ui");
|
||||||
|
|
||||||
ui.init(runtimeAPI);
|
ui.init(settings, runtimeAPI);
|
||||||
|
|
||||||
const editorApp = apiUtil.createExpressApp(settings)
|
const editorApp = apiUtil.createExpressApp(settings)
|
||||||
|
|
||||||
|
@ -339,6 +339,8 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
theme.codeEditor = theme.codeEditor || {}
|
theme.codeEditor = theme.codeEditor || {}
|
||||||
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
|
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
|
||||||
|
|
||||||
|
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
|
||||||
}
|
}
|
||||||
activeThemeInitialised = true;
|
activeThemeInitialised = true;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
const crypto = require('crypto')
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
@ -24,13 +25,16 @@ var apiUtils = require("../util");
|
|||||||
var theme = require("./theme");
|
var theme = require("./theme");
|
||||||
|
|
||||||
var runtimeAPI;
|
var runtimeAPI;
|
||||||
|
let settings;
|
||||||
var editorClientDir = path.dirname(require.resolve("@node-red/editor-client"));
|
var editorClientDir = path.dirname(require.resolve("@node-red/editor-client"));
|
||||||
var defaultNodeIcon = path.join(editorClientDir,"public","red","images","icons","arrow-in.svg");
|
var defaultNodeIcon = path.join(editorClientDir,"public","red","images","icons","arrow-in.svg");
|
||||||
var editorTemplatePath = path.join(editorClientDir,"templates","index.mst");
|
var editorTemplatePath = path.join(editorClientDir,"templates","index.mst");
|
||||||
var editorTemplate;
|
var editorTemplate;
|
||||||
|
let cacheBuster
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: function(_runtimeAPI) {
|
init: function(_settings, _runtimeAPI) {
|
||||||
|
settings = _settings;
|
||||||
runtimeAPI = _runtimeAPI;
|
runtimeAPI = _runtimeAPI;
|
||||||
editorTemplate = fs.readFileSync(editorTemplatePath,"utf8");
|
editorTemplate = fs.readFileSync(editorTemplatePath,"utf8");
|
||||||
Mustache.parse(editorTemplate);
|
Mustache.parse(editorTemplate);
|
||||||
@ -91,6 +95,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
editor: async function(req,res) {
|
editor: async function(req,res) {
|
||||||
|
if (!cacheBuster) {
|
||||||
|
// settings.instanceId is set asynchronously to the editor-api
|
||||||
|
// being initiaised. So we defer calculating the cacheBuster hash
|
||||||
|
// until the first load of the editor
|
||||||
|
cacheBuster = crypto.createHash('md5').update(`${settings.version || 'version'}-${settings.instanceId || 'instanceId'}`).digest("hex").substring(0,12)
|
||||||
|
}
|
||||||
|
|
||||||
let sessionMessages;
|
let sessionMessages;
|
||||||
if (req.session && req.session.messages) {
|
if (req.session && req.session.messages) {
|
||||||
@ -99,6 +109,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
res.send(Mustache.render(editorTemplate,{
|
res.send(Mustache.render(editorTemplate,{
|
||||||
sessionMessages,
|
sessionMessages,
|
||||||
|
cacheBuster,
|
||||||
...await theme.context()
|
...await theme.context()
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
@ -109,7 +109,6 @@
|
|||||||
"selectionToSubflow": "Auswahl in Subflow umwandeln",
|
"selectionToSubflow": "Auswahl in Subflow umwandeln",
|
||||||
"flows": "Flow",
|
"flows": "Flow",
|
||||||
"add": "Hinzufügen",
|
"add": "Hinzufügen",
|
||||||
"rename": "Umbenennen",
|
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"keyboardShortcuts": "Tastenkürzel",
|
"keyboardShortcuts": "Tastenkürzel",
|
||||||
"login": "Anmelden",
|
"login": "Anmelden",
|
||||||
@ -1076,7 +1075,7 @@
|
|||||||
"git-auth-error": "Git-Authentifizierungsfehler"
|
"git-auth-error": "Git-Authentifizierungsfehler"
|
||||||
},
|
},
|
||||||
"create-success": {
|
"create-success": {
|
||||||
"success": "Sie haben Ihr erstes Projekt erfolgreich erstduellt!",
|
"success": "Sie haben Ihr erstes Projekt erfolgreich erstellt!",
|
||||||
"desc0": "Sie können jetzt Node-RED wie bisher verwenden.",
|
"desc0": "Sie können jetzt Node-RED wie bisher verwenden.",
|
||||||
"desc1": "Im Tab 'Info' in der Seitenleiste wird angezeigt, welches das aktuelle Projekt ist. Über die Schaltfläche rechts neben dem Projektnamen gelangt man zu 'Projekteinstellungen'.",
|
"desc1": "Im Tab 'Info' in der Seitenleiste wird angezeigt, welches das aktuelle Projekt ist. Über die Schaltfläche rechts neben dem Projektnamen gelangt man zu 'Projekteinstellungen'.",
|
||||||
"desc2": "Im Tab 'Commit-Historie' in der Seitenleiste werden alle Dateien angezeigt, die sich in Ihrem Projekt geändert haben, und um sie ins lokale Repository zu übertragen (commit). Es zeigt Ihnen eine vollständige Historie Ihrer Commits an und ermöglicht es Ihnen, Ihre Commits in ein (remote) Server-Repository zu schieben (push)."
|
"desc2": "Im Tab 'Commit-Historie' in der Seitenleiste werden alle Dateien angezeigt, die sich in Ihrem Projekt geändert haben, und um sie ins lokale Repository zu übertragen (commit). Es zeigt Ihnen eine vollständige Historie Ihrer Commits an und ermöglicht es Ihnen, Ihre Commits in ein (remote) Server-Repository zu schieben (push)."
|
||||||
@ -1172,17 +1171,6 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "System-Informationen"
|
"title": "System-Informationen"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "Deutsch",
|
|
||||||
"en-US": "Englisch",
|
|
||||||
"fr": "Französisch",
|
|
||||||
"ja": "Japanisch",
|
|
||||||
"ko": "Koreanisch",
|
|
||||||
"pt-BR":"Portugiesisch",
|
|
||||||
"ru": "Russisch",
|
|
||||||
"zh-CN": "Chinesisch (Vereinfacht)",
|
|
||||||
"zh-TW": "Chinesisch (Traditionell)"
|
|
||||||
},
|
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "Ungültige JSON-Daten: __error__",
|
"invalid-json": "Ungültige JSON-Daten: __error__",
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
"displayStatus": "Show node status",
|
"displayStatus": "Show node status",
|
||||||
"displayConfig": "Configuration nodes",
|
"displayConfig": "Configuration nodes",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
"importExample": "Import Example Flow",
|
"importExample": "Import example flow",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"search": "Search flows",
|
"search": "Search flows",
|
||||||
"searchInput": "search your flows",
|
"searchInput": "search your flows",
|
||||||
@ -122,7 +122,6 @@
|
|||||||
"selectionToSubflow": "Selection to Subflow",
|
"selectionToSubflow": "Selection to Subflow",
|
||||||
"flows": "Flows",
|
"flows": "Flows",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"rename": "Rename",
|
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"keyboardShortcuts": "Keyboard shortcuts",
|
"keyboardShortcuts": "Keyboard shortcuts",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
@ -130,6 +129,11 @@
|
|||||||
"editPalette": "Manage palette",
|
"editPalette": "Manage palette",
|
||||||
"other": "Other",
|
"other": "Other",
|
||||||
"showTips": "Show tips",
|
"showTips": "Show tips",
|
||||||
|
"showNodeHelp": "Show node help",
|
||||||
|
"enableSelectedNodes": "Enable selected nodes",
|
||||||
|
"disableSelectedNodes": "Disable selected nodes",
|
||||||
|
"showSelectedNodeLabels": "Show selected node labels",
|
||||||
|
"hideSelectedNodeLabels": "Hide selected node labels",
|
||||||
"showWelcomeTours": "Show guided tours for new versions",
|
"showWelcomeTours": "Show guided tours for new versions",
|
||||||
"help": "Node-RED website",
|
"help": "Node-RED website",
|
||||||
"projects": "Projects",
|
"projects": "Projects",
|
||||||
@ -299,7 +303,8 @@
|
|||||||
"missingType": "Input not a valid flow - item __index__ missing 'type' property"
|
"missingType": "Input not a valid flow - item __index__ missing 'type' property"
|
||||||
},
|
},
|
||||||
"conflictNotification1": "Some of the nodes you are importing already exist in your workspace.",
|
"conflictNotification1": "Some of the nodes you are importing already exist in your workspace.",
|
||||||
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them."
|
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them.",
|
||||||
|
"alreadyExists": "This node already exists"
|
||||||
},
|
},
|
||||||
"copyMessagePath": "Path copied",
|
"copyMessagePath": "Path copied",
|
||||||
"copyMessageValue": "Value copied",
|
"copyMessageValue": "Value copied",
|
||||||
@ -511,8 +516,8 @@
|
|||||||
"selectAllConnected": "Select connected",
|
"selectAllConnected": "Select connected",
|
||||||
"addRemoveNode": "Add/remove node from selection",
|
"addRemoveNode": "Add/remove node from selection",
|
||||||
"editSelected": "Edit selected node",
|
"editSelected": "Edit selected node",
|
||||||
"deleteSelected": "Delete selected nodes or link",
|
"deleteSelected": "Delete selection",
|
||||||
"deleteReconnect": "Delete and Reconnect",
|
"deleteReconnect": "Delete and reconnect",
|
||||||
"importNode": "Import nodes",
|
"importNode": "Import nodes",
|
||||||
"exportNode": "Export nodes",
|
"exportNode": "Export nodes",
|
||||||
"nudgeNode": "Move selected nodes (1px)",
|
"nudgeNode": "Move selected nodes (1px)",
|
||||||
@ -703,7 +708,7 @@
|
|||||||
"triggerAction": "Trigger action",
|
"triggerAction": "Trigger action",
|
||||||
"find": "Find in workspace",
|
"find": "Find in workspace",
|
||||||
"copyItemUrl": "Copy item url",
|
"copyItemUrl": "Copy item url",
|
||||||
"copyURL2Clipboard": "Copied url to clipboard",
|
"copyURL2Clipboard": "Copied url to clipboard",
|
||||||
"showFlow": "Show",
|
"showFlow": "Show",
|
||||||
"hideFlow": "Hide"
|
"hideFlow": "Hide"
|
||||||
},
|
},
|
||||||
@ -920,6 +925,12 @@
|
|||||||
"jsonata": "expression",
|
"jsonata": "expression",
|
||||||
"env": "env variable",
|
"env": "env variable",
|
||||||
"cred": "credential"
|
"cred": "credential"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"format": {
|
||||||
|
"timestamp": "milliseconds since epoch",
|
||||||
|
"object": "JavaScript Date Object"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editableList": {
|
"editableList": {
|
||||||
@ -1202,22 +1213,22 @@
|
|||||||
"title": "System Info"
|
"title": "System Info"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"de": "German",
|
"de": "Deutsch",
|
||||||
"en-US": "English",
|
"en-US": "English",
|
||||||
"fr": "French",
|
"es-ES": "Español (España)",
|
||||||
"ja": "Japanese",
|
"fr": "Français",
|
||||||
|
"ja": "日本語",
|
||||||
"ko": "Korean",
|
"ko": "Korean",
|
||||||
"pt-BR":"Portuguese",
|
"pt-BR": "Português (Brasil)",
|
||||||
"ru": "Russian",
|
"ru": "Русский",
|
||||||
"zh-CN": "Chinese(Simplified)",
|
"zh-CN": "简体中文",
|
||||||
"zh-TW": "Chinese(Traditional)"
|
"zh-TW": "繁體中文"
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "Invalid JSON data: __error__",
|
"invalid-json": "Invalid JSON data: __error__",
|
||||||
"invalid-json-prop": "__prop__: invalid JSON data: __error__",
|
"invalid-expr": "Invalid JSONata expression: __error__",
|
||||||
"invalid-prop": "Invalid property expression",
|
"invalid-prop": "Invalid property expression",
|
||||||
"invalid-prop-prop": "__prop__: invalid property expression",
|
|
||||||
"invalid-num": "Invalid number",
|
"invalid-num": "Invalid number",
|
||||||
"invalid-num-prop": "__prop__: invalid number",
|
"invalid-num-prop": "__prop__: invalid number",
|
||||||
"invalid-regexp": "Invalid input pattern",
|
"invalid-regexp": "Invalid input pattern",
|
||||||
@ -1229,6 +1240,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
|
"showActionList": "Show action list",
|
||||||
"insert": "Insert",
|
"insert": "Insert",
|
||||||
"node": "Node",
|
"node": "Node",
|
||||||
"junction": "Junction",
|
"junction": "Junction",
|
||||||
|
1235
packages/node_modules/@node-red/editor-client/locales/es-ES/editor.json
vendored
Normal file
26
packages/node_modules/@node-red/editor-client/locales/es-ES/infotips.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"tip0": "Puedes eliminar los nodos o enlaces seleccionados con {{core:delete-selection}}",
|
||||||
|
"tip1": "Busca nodos con {{core:search}}",
|
||||||
|
"tip2": "{{core:toggle-sidebar}} alternará la vista de esta barra lateral",
|
||||||
|
"tip3": "Puedes gestionar tu paleta de nodos con {{core:manage-palette}}",
|
||||||
|
"tip4": "Tus nodos de configuración de flujo aparecen en el panel de la barra lateral. Se puede acceder desde el menú o con {{core:show-config-tab}}",
|
||||||
|
"tip5": "Activa o desactiva estos consejos desde la opción en la configuración",
|
||||||
|
"tip6": "Mueve los nodos seleccionados usando las teclas [izquierda] [arriba] [abajo] y [derecha]. Mantén pulsada [Mayús] para desplazarlos más",
|
||||||
|
"tip7": "Arrastrar un nodo a un cable lo insertará en el enlace",
|
||||||
|
"tip8": "Exporta los nodos seleccionados, o la pestaña actual con {{core:show-export-dialog}}",
|
||||||
|
"tip9": "Importa un flujo arrastrando su JSON al editor, o con {{core:show-import-dialog}}",
|
||||||
|
"tip10": "[shift][clic] y arrastrar en un puerto de nodo para mover todos los cables conectados o sólo el seleccionado",
|
||||||
|
"tip11": "Mostrar la pestaña Información con {{core:show-info-tab}} o la pestaña Depuración con {{core:show-debug-tab}}",
|
||||||
|
"tip12": "[ctrl] [clic] en el área de trabajo para abrir el diálogo de adición rápida",
|
||||||
|
"tip13": "Mantén pulsada [ctrl] cuando [haces clic] en un puerto de nodo para habilitar el enlazado rápido",
|
||||||
|
"tip14": "Mantén pulsada [shift] cuando [haces clic] en un nodo para seleccionar también todos sus nodos conectados",
|
||||||
|
"tip15": "Mantén pulsada [ctrl] cuando [haces clic] en un nodo para añadirlo o eliminarlo de la selección actual",
|
||||||
|
"tip16": "Cambia de pestaña de flujo con {{core:show-previous-tab}} y {{core:show-next-tab}}",
|
||||||
|
"tip17": "Puedes confirmar tus cambios en la bandeja de edición de nodos con {{core:confirm-edit-tray}} o cancelarlos con {{core:cancel-edit-tray}}",
|
||||||
|
"tip18": "Al pulsar {{core:edit-selected-node}} se editará el primer nodo de la selección actual"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
278
packages/node_modules/@node-red/editor-client/locales/es-ES/jsonata.json
vendored
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
{
|
||||||
|
"$string": {
|
||||||
|
"args": "arg[, prettify]",
|
||||||
|
"desc": "Convierte el parámetro `arg` a una cadena usando las siguientes reglas de conversión:\n\n - Las cadenas no cambian\n - Las funciones se convierten en una cadena vacía\n - El infinito numérico y NaN arrojan un error porque no se pueden representar como un número JSON\n: todos los demás valores se convierten a una cadena JSON usando la función `JSON.stringify`. Si `prettify` es verdadero, entonces se produce JSON \"prettified\". es decir, una línea por campo y las líneas se indentarán según la profundidad del campo."
|
||||||
|
},
|
||||||
|
"$length": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Devuelve el número de caracteres de la cadena `str`. Se genera un error si `str` no es una cadena."
|
||||||
|
},
|
||||||
|
"$substring": {
|
||||||
|
"args": "str, start[, length]",
|
||||||
|
"desc": "Devuelve una cadena que contiene los caracteres del primer parámetro `str` comenzando en la posición `start` (desplazamiento cero). Si se especifica 'longitud', la subcadena contendrá el máximo de caracteres de 'longitud'. Si 'inicio' es negativo, indica el número de caracteres desde el final de 'cadena'."
|
||||||
|
},
|
||||||
|
"$substringBefore": {
|
||||||
|
"args": "str, chars",
|
||||||
|
"desc": "Devuelve la subcadena antes de la primera aparición de la secuencia de caracteres `chars` en `str`. Si `str` no contiene `caracteres`, entonces devuelve `str`."
|
||||||
|
},
|
||||||
|
"$substringAfter": {
|
||||||
|
"args": "str, chars",
|
||||||
|
"desc": "Devuelve la subcadena después de la primera aparición de la secuencia de caracteres `chars` en `str`. Si `str` no contiene `caracteres`, entonces devuelve `str`."
|
||||||
|
},
|
||||||
|
"$uppercase": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Devuelve una cadena con todos los caracteres de `str` convertidos a mayúsculas."
|
||||||
|
},
|
||||||
|
"$lowercase": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Devuelve una cadena con todos los caracteres de `str` convertidos a minúsculas."
|
||||||
|
},
|
||||||
|
"$trim": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Normaliza y recorta todos los caracteres de espacio en blanco en `str` aplicando los siguientes pasos:\n\n - Todas las tabulaciones, retornos de carro y avances de línea se reemplazan con espacios.\n- Las secuencias contiguas de espacios se reducen a un solo espacio.\n- Se eliminan los espacios iniciales y finales.\n\n Si no se especifica `str` (es decir, esta función se invoca sin argumentos), entonces el valor de contexto se utiliza como el valor de `str`. Se genera un error si `str` no es una cadena."
|
||||||
|
},
|
||||||
|
"$contains": {
|
||||||
|
"args": "str, pattern",
|
||||||
|
"desc": "Devuelve 'verdadero' si 'cadena' coincide con 'patrón', de lo contrario, devuelve 'falso'. Si no se especifica `str` (es decir, esta función se invoca con un argumento), entonces el valor del contexto se utiliza como valor de `str`. El parámetro `patrón` puede ser una cadena o una expresión regular."
|
||||||
|
},
|
||||||
|
"$split": {
|
||||||
|
"args": "str[, separator][, limit]",
|
||||||
|
"desc": "Divide el parámetro `str` en una matriz de subcadenas. Es un error si `str` no es una cadena. El parámetro opcional `separador` especifica los caracteres dentro de la `cadena` sobre los cuales se debe dividir como una cadena o una expresión regular. Si no se especifica 'separador', se supone que la cadena está vacía y 'cadena' se dividirá en una matriz de caracteres individuales. Es un error si el 'separador' no es una cadena. El parámetro opcional 'límite' es un número que especifica el número máximo de subcadenas que se incluirán en la matriz resultante. Cualquier subcadena adicional se descarta. Si no se especifica `límite`, entonces `str` se divide completamente sin límite para el tamaño de la matriz resultante. Es un error si 'límite' no es un número positivo."
|
||||||
|
},
|
||||||
|
"$join": {
|
||||||
|
"args": "array[, separator]",
|
||||||
|
"desc": "Une una matriz de cadenas de componentes en una única cadena concatenada con cada cadena de componentes separada por el parámetro 'separador' opcional. Es un error si la 'matriz' de entrada contiene un elemento que no es una cadena. Si no se especifica 'separador', se supone que es una cadena vacía, es decir, que no hay 'separador' entre las cadenas componentes. Es un error si el 'separador' no es una cadena."
|
||||||
|
},
|
||||||
|
"$match": {
|
||||||
|
"args": "str, pattern [, limit]",
|
||||||
|
"desc": "Aplica la cadena `str` a la expresión regular `pattern` y devuelve una matriz de objetos, cada objeto contiene información sobre cada aparición de una coincidencia dentro de `str`."
|
||||||
|
},
|
||||||
|
"$replace": {
|
||||||
|
"args": "str, pattern, replacement [, limit]",
|
||||||
|
"desc": "Encuentra apariciones de `patrón` dentro de `str` y las reemplaza con `reemplazo`.\n\nEl parámetro opcional `límite` es el número máximo de reemplazos."
|
||||||
|
},
|
||||||
|
"$now": {
|
||||||
|
"args": "$[picture [, timezone]]",
|
||||||
|
"desc": "Genera una marca de tiempo en formato compatible con ISO 8601 y la devuelve como una cadena. Si se proporcionan los parámetros opcionales `picture` y `zona horaria`, entonces la marca de tiempo actual se formatea como se describe en la función `$fromMillis()`"
|
||||||
|
},
|
||||||
|
"$base64encode": {
|
||||||
|
"args": "string",
|
||||||
|
"desc": "Convierte una cadena ASCII a una representación base 64. Cada carácter de la cadena se trata como un byte de datos binarios. Esto requiere que todos los caracteres de la cadena estén en el rango de 0x00 a 0xFF, que incluye todos los caracteres de las cadenas codificadas con URI. No se admiten caracteres Unicode fuera de ese rango."
|
||||||
|
},
|
||||||
|
"$base64decode": {
|
||||||
|
"args": "string",
|
||||||
|
"desc": "Convierte bytes codificados en base 64 en una cadena, utilizando una página de códigos Unicode UTF-8."
|
||||||
|
},
|
||||||
|
"$number": {
|
||||||
|
"args": "arg",
|
||||||
|
"desc": "Convierte el parámetro `arg` a un número usando las siguientes reglas de conversión:\n\n - Los números no cambian\n - Las cadenas que contienen una secuencia de caracteres que representan un número JSON legal se convierten a ese número\n - Todos los demás valores provocar que se arroje un error."
|
||||||
|
},
|
||||||
|
"$abs": {
|
||||||
|
"args": "number",
|
||||||
|
"desc": "Devuelve el valor absoluto del parámetro 'número'."
|
||||||
|
},
|
||||||
|
"$floor": {
|
||||||
|
"args": "number",
|
||||||
|
"desc": "Devuelve el valor de 'número' redondeado hacia abajo al entero más cercano que sea menor o igual a 'número'."
|
||||||
|
},
|
||||||
|
"$ceil": {
|
||||||
|
"args": "number",
|
||||||
|
"desc": "Devuelve el valor de 'número' redondeado al número entero más cercano que sea mayor o igual a 'número'."
|
||||||
|
},
|
||||||
|
"$round": {
|
||||||
|
"args": "number [, precision]",
|
||||||
|
"desc": "Devuelve el valor del parámetro 'número' redondeado al número de decimales especificado por el parámetro opcional 'precisión'."
|
||||||
|
},
|
||||||
|
"$power": {
|
||||||
|
"args": "base, exponent",
|
||||||
|
"desc": "Devuelve el valor de 'base' elevado a la potencia de 'exponente'."
|
||||||
|
},
|
||||||
|
"$sqrt": {
|
||||||
|
"args": "number",
|
||||||
|
"desc": "Devuelve la raíz cuadrada del valor del parámetro 'número'."
|
||||||
|
},
|
||||||
|
"$random": {
|
||||||
|
"args": "",
|
||||||
|
"desc": "Devuelve un número pseudoaleatorio mayor o igual a cero y menor que uno."
|
||||||
|
},
|
||||||
|
"$millis": {
|
||||||
|
"args": "",
|
||||||
|
"desc": "Devuelve el número de milisegundos desde la época Unix (1 de enero de 1970 UTC) como un número. Todas las invocaciones de `$millis()` dentro de una evaluación de una expresión devolverán el mismo valor."
|
||||||
|
},
|
||||||
|
"$sum": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve la suma aritmética de una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
|
||||||
|
},
|
||||||
|
"$max": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve el número máximo en una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
|
||||||
|
},
|
||||||
|
"$min": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve el número mínimo en una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
|
||||||
|
},
|
||||||
|
"$average": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve el valor medio de una 'matriz' de números. Es un error si la 'matriz' de entrada contiene un elemento que no es un número."
|
||||||
|
},
|
||||||
|
"$boolean": {
|
||||||
|
"args": "arg",
|
||||||
|
"desc": "Convierte el argumento a un booleano usando las siguientes reglas:\n\n - `Booleano`: sin cambios\n - `cadena`: vacía: `falso`\n - `cadena`: no vacía: `verdadero`\n - `número`: `0`: `falso`\n - `número`: distinto de cero: `verdadero`\n - `nulo`: `falso`\n - `matriz`: vacía: `falso`\n - `array`: contiene un miembro que se convierte en `true`: `true`\n - `array`: todos los miembros se convierten en `false`: `false`\n - `object`: vacío: `false`\n - `objeto`: no vacío: `verdadero`\n - `función`: `falso`"
|
||||||
|
},
|
||||||
|
"$not": {
|
||||||
|
"args": "arg",
|
||||||
|
"desc": "Devuelve booleano NEGADO del argumento. `arg` se convierte antes en un booleano"
|
||||||
|
},
|
||||||
|
"$exists": {
|
||||||
|
"args": "arg",
|
||||||
|
"desc": "Devuelve booleano 'verdadero' si la expresión 'arg' se evalúa como un valor, o 'falso' si la expresión no coincide con nada (por ejemplo, una ruta a una referencia de campo inexistente)."
|
||||||
|
},
|
||||||
|
"$count": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve el número de elementos de la matriz."
|
||||||
|
},
|
||||||
|
"$append": {
|
||||||
|
"args": "array, array",
|
||||||
|
"desc": "Agrega dos matrices"
|
||||||
|
},
|
||||||
|
"$sort": {
|
||||||
|
"args": "array [, function]",
|
||||||
|
"desc": "Devuelve una matriz que contiene todos los valores en el parámetro `array`, pero ordenados.\n\nSi se proporciona una `función` de comparador, entonces debe ser una función que toma dos parámetros:\n\n`function(left , derecha)`\n\nEsta función es invocada por el algoritmo de clasificación para comparar dos valores `izquierda` y `derecha`. Si el valor de `izquierda` debe colocarse después del valor de `derecha` en el orden de clasificación deseado, entonces la función debe devolver un valor booleano 'verdadero' para indicar un intercambio. De lo contrario debe devolver 'falso'."
|
||||||
|
},
|
||||||
|
"$reverse": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve una matriz que contiene todos los valores del parámetro `matriz`, pero en orden inverso."
|
||||||
|
},
|
||||||
|
"$shuffle": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve una matriz que contiene todos los valores del parámetro `array`, pero mezclados en orden aleatorio."
|
||||||
|
},
|
||||||
|
"$zip": {
|
||||||
|
"args": "array, ...",
|
||||||
|
"desc": "Devuelve una matriz convolucionada (comprimida) que contiene matrices agrupadas de valores de los argumentos `matriz1`... `matrizN` del índice 0, 1, 2...."
|
||||||
|
},
|
||||||
|
"$keys": {
|
||||||
|
"args": "object",
|
||||||
|
"desc": "Devuelve una matriz que contiene las claves del objeto. Si el argumento es una matriz de objetos, entonces la matriz devuelta contiene una lista deduplicada de todas las claves de todos los objetos."
|
||||||
|
},
|
||||||
|
"$lookup": {
|
||||||
|
"args": "object, key",
|
||||||
|
"desc": "Devuelve el valor asociado con la clave en el objeto. Si el primer argumento es una matriz de objetos, entonces se buscan todos los objetos de la matriz y se devuelven los valores asociados con todas las apariciones de la clave."
|
||||||
|
},
|
||||||
|
"$spread": {
|
||||||
|
"args": "object",
|
||||||
|
"desc": "Divide un objeto que contiene pares clave/valor en una matriz de objetos, cada uno de los cuales tiene un único par clave/valor del objeto de entrada. Si el parámetro es una matriz de objetos, entonces la matriz resultante contiene un objeto para cada par clave/valor en cada objeto de la matriz proporcionada."
|
||||||
|
},
|
||||||
|
"$merge": {
|
||||||
|
"args": "array<object>",
|
||||||
|
"desc": "Fusiona una matriz de objetos en un único objeto que contiene todos los pares clave/valor de cada uno de los objetos en la matriz de entrada. Si alguno de los objetos de entrada contiene la misma clave, entonces el objeto devuelto contendrá el valor del último en la matriz. Es un error si la matriz de entrada contiene un elemento que no es un objeto."
|
||||||
|
},
|
||||||
|
"$sift": {
|
||||||
|
"args": "object, function",
|
||||||
|
"desc": "Devuelve un objeto que contiene solo los pares clave/valor del parámetro `objeto` que satisfacen el predicado `función` pasado como segundo parámetro.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función(valor [, clave [, objeto]])`"
|
||||||
|
},
|
||||||
|
"$each": {
|
||||||
|
"args": "object, function",
|
||||||
|
"desc": "Devuelve una matriz que contiene los valores devueltos por la función cuando se aplica a cada par clave/valor en el objeto."
|
||||||
|
},
|
||||||
|
"$map": {
|
||||||
|
"args": "array, function",
|
||||||
|
"desc": "Devuelve una matriz que contiene los resultados de aplicar el parámetro `función` a cada valor en el parámetro `matriz`.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función( valor [, índice [, matriz]])`"
|
||||||
|
},
|
||||||
|
"$filter": {
|
||||||
|
"args": "array, function",
|
||||||
|
"desc": "Devuelve una matriz que contiene solo los valores en el parámetro `matriz` que satisfacen el predicado `función`.\n\nLa `función` que se proporciona como segundo parámetro debe tener la siguiente firma:\n\n`función(valor [ , índice [, matriz]])`"
|
||||||
|
},
|
||||||
|
"$reduce": {
|
||||||
|
"args": "array, function [, init]",
|
||||||
|
"desc": "Devuelve un valor agregado derivado de aplicar el parámetro `función` sucesivamente a cada valor en `matriz` en combinación con el resultado de la aplicación anterior de la función.\n\nLa función debe aceptar dos argumentos y se comporta como un operador infijo entre cada valor dentro de la matriz. La firma de la `función` debe tener la forma: `myfunc($accumulator, $value[, $index[, $array]])`\n\nEl parámetro opcional `init` se utiliza como valor inicial en la agregación."
|
||||||
|
},
|
||||||
|
"$flowContext": {
|
||||||
|
"args": "string[, string]",
|
||||||
|
"desc": "Recupera una propiedad de contexto de flujo.\n\nEsta es una función definida por Node-RED."
|
||||||
|
},
|
||||||
|
"$globalContext": {
|
||||||
|
"args": "string[, string]",
|
||||||
|
"desc": "Recupera una propiedad de contexto global.\n\nEsta es una función definida por Node-RED."
|
||||||
|
},
|
||||||
|
"$pad": {
|
||||||
|
"args": "string, width [, char]",
|
||||||
|
"desc": "Devuelve una copia de la `cadena` con relleno adicional, si es necesario, de modo que su número total de caracteres sea al menos el valor absoluto del parámetro `ancho`.\n\nSi `ancho` es un número positivo, entonces la cadena está acolchado hacia la derecha; si es negativo, se rellena hacia la izquierda.\n\nEl argumento opcional `char` especifica los caracteres de relleno que se utilizarán. Si no se especifica, el valor predeterminado es el carácter de espacio."
|
||||||
|
},
|
||||||
|
"$fromMillis": {
|
||||||
|
"args": "number, [, picture [, timezone]]",
|
||||||
|
"desc": "Convierte el `número` que representa milisegundos desde la época Unix (1 de enero de 1970 UTC) en una representación de cadena formateada según la plantilla en picture.\n\nSi se omite el parámetro opcional `picture`, entonces la marca de tiempo es formateado en el formato ISO 8601.\n\nSi se proporciona la cadena opcional `picture`, entonces la marca de tiempo se formatea de acuerdo con la representación especificada en esa cadena. El comportamiento de esta función es consistente con la versión de dos argumentos de la función XPath/XQuery `format-dateTime` tal como se define en la especificación XPath F&O 3.1. El parámetro de plantilla define cómo se formatea la marca de tiempo y tiene la misma sintaxis que `format-dateTime`.\n\nSi se proporciona la cadena opcional `timezone`, entonces la marca de tiempo formateada estará en esa zona horaria. La cadena `timezone` debe tener el formato '±HHMM', donde ± es el signo más o menos y HHMM es el desplazamiento en horas y minutos desde UTC. Desplazamiento positivo para zonas horarias al este de UTC, desplazamiento negativo para zonas horarias al oeste de UTC."
|
||||||
|
},
|
||||||
|
"$formatNumber": {
|
||||||
|
"args": "number, picture [, options]",
|
||||||
|
"desc": "Convierte el `número` en una cadena y lo formatea en una representación decimal según lo especificado en la cadena `picture`.\n\n El comportamiento de esta función es coherente con la función XPath/XQuery `fn:format-number` tal como se define en la especificación XPath F&O 3.1. El parámetro de cadena `picture` define cómo se formatea el número y tiene la misma sintaxis que `fn:formato-número`.\n\nEl tercer argumento opcional `opciones` se utiliza para anular los caracteres de formato específicos de la configuración regional predeterminada, como el decimal. separador. Si se proporciona, este argumento debe ser un objeto que contenga pares de nombre/valor especificados en la sección de formato decimal de la especificación XPath F&O 3.1."
|
||||||
|
},
|
||||||
|
"$formatBase": {
|
||||||
|
"args": "number [, radix]",
|
||||||
|
"desc": "Convierte el número en una cadena y lo formatea como un número entero representado en la base numérica especificada por el argumento `radix`. Si no se especifica `radix`, el valor predeterminado es la base 10. `radix` puede estar entre 2 y 36; de lo contrario, se genera un error."
|
||||||
|
},
|
||||||
|
"$toMillis": {
|
||||||
|
"args": "timestamp",
|
||||||
|
"desc": "Convierte una cadena de `marca de tiempo` en el formato ISO 8601 al número de milisegundos desde la época Unix (1 de enero de 1970 UTC) como un número. Se genera un error si la cadena no tiene el formato correcto."
|
||||||
|
},
|
||||||
|
"$env": {
|
||||||
|
"args": "arg",
|
||||||
|
"desc": "Devuelve el valor de una variable de entorno.\n\nEsta es una función definida por Node-RED."
|
||||||
|
},
|
||||||
|
"$eval": {
|
||||||
|
"args": "expr [, context]",
|
||||||
|
"desc": "Analiza y evalúa la cadena `expr` que contiene JSON literal o una expresión JSONata utilizando el contexto actual como contexto para la evaluación."
|
||||||
|
},
|
||||||
|
"$formatInteger": {
|
||||||
|
"args": "number, picture",
|
||||||
|
"desc": "Convierte el número en una cadena y lo formatea en una representación entera como lo especifica la cadena `picture`. El parámetro de define cómo se formatea el número y tiene la misma sintaxis que `fn:format-integer` de la especificación XPath F&O 3.1."
|
||||||
|
},
|
||||||
|
"$parseInteger": {
|
||||||
|
"args": "string, picture",
|
||||||
|
"desc": "Analiza el contenido del parámetro cadena en un número entero (como un número JSON) utilizando el formato especificado por la cadena `picture`. El parámetro tiene el mismo formato que `$formatInteger`."
|
||||||
|
},
|
||||||
|
"$error": {
|
||||||
|
"args": "[str]",
|
||||||
|
"desc": "Lanza un error con un mensaje. El parámetro `str` opcional reemplazará el mensaje predeterminado de `$error() función evaluada`"
|
||||||
|
},
|
||||||
|
"$assert": {
|
||||||
|
"args": "arg, str",
|
||||||
|
"desc": "Si `arg` es `verdadero`, la función devuelve indefinido. Si `arg` es `falso`, se lanza una excepción con `str` como mensaje de excepción."
|
||||||
|
},
|
||||||
|
"$single": {
|
||||||
|
"args": "array, function",
|
||||||
|
"desc": "Devuelve el único valor en el parámetro `array` que satisface el predicado de `función` (es decir, la `función` devuelve booleano `verdadero` cuando se pasa el valor). Lanza una excepción si el número de valores coincidentes no es exactamente uno.\n\nLa función debe proporcionarse con la siguiente firma: `función(valor [, índice [, matriz]])` donde el valor es cada entrada de la matriz. El índice es la posición de ese valor y toda la matriz se pasa como tercer argumento."
|
||||||
|
},
|
||||||
|
"$encodeUrlComponent": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Codifica un componente de URL reemplazando cada instancia de ciertos caracteres por una, dos, tres o cuatro secuencias de escape que representan la codificación UTF-8 del carácter.\n\nEjemplo: `$encodeUrlComponent(\"?x=prueba\")` => `\"%3Fx%3Dprueba\"`"
|
||||||
|
},
|
||||||
|
"$encodeUrl": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Codifica una URL reemplazando cada instancia de ciertos caracteres por una, dos, tres o cuatro secuencias de escape que representan la codificación UTF-8 del carácter.\n\nEjemplo: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
|
||||||
|
},
|
||||||
|
"$decodeUrlComponent": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Decodifica un componente de URL creado previamente por encodeUrlComponent.\n\nEjemplo: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
|
||||||
|
},
|
||||||
|
"$decodeUrl": {
|
||||||
|
"args": "str",
|
||||||
|
"desc": "Decodifica una URL creado previamente por encodeUrl.\n\nEjemplo: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
|
||||||
|
},
|
||||||
|
"$distinct": {
|
||||||
|
"args": "array",
|
||||||
|
"desc": "Devuelve una matriz con valores duplicados eliminados de `matriz`"
|
||||||
|
},
|
||||||
|
"$type": {
|
||||||
|
"args": "value",
|
||||||
|
"desc": "Devuelve el tipo de `valor` como una cadena. Si `valor` no está definido, esto devolverá indefinido."
|
||||||
|
},
|
||||||
|
"$moment": {
|
||||||
|
"args": "[str]",
|
||||||
|
"desc": "Obtiene un objeto de fecha usando la biblioteca Moment."
|
||||||
|
},
|
||||||
|
"$clone": {
|
||||||
|
"args": "value",
|
||||||
|
"desc": "Clona un objeto de forma segura."
|
||||||
|
}
|
||||||
|
}
|
@ -119,10 +119,9 @@
|
|||||||
"searchInput": "Rechercher vos flux",
|
"searchInput": "Rechercher vos flux",
|
||||||
"subflows": "Sous-flux",
|
"subflows": "Sous-flux",
|
||||||
"createSubflow": "Créer un sous-flux",
|
"createSubflow": "Créer un sous-flux",
|
||||||
"selectionToSubflow": "Selection d'un sous-flux",
|
"selectionToSubflow": "Convertir en sous-flux",
|
||||||
"flows": "Flux",
|
"flows": "Flux",
|
||||||
"add": "Ajouter",
|
"add": "Ajouter",
|
||||||
"rename": "Renommer",
|
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"keyboardShortcuts": "Raccourcis clavier",
|
"keyboardShortcuts": "Raccourcis clavier",
|
||||||
"login": "Se connecter",
|
"login": "Se connecter",
|
||||||
@ -130,6 +129,11 @@
|
|||||||
"editPalette": "Gérer la palette",
|
"editPalette": "Gérer la palette",
|
||||||
"other": "Autre",
|
"other": "Autre",
|
||||||
"showTips": "Afficher les astuces",
|
"showTips": "Afficher les astuces",
|
||||||
|
"showNodeHelp": "Afficher l'aide du noeud",
|
||||||
|
"enableSelectedNodes": "Activer les noeuds sélectionnés",
|
||||||
|
"disableSelectedNodes": "Désactiver les noeuds sélectionnés",
|
||||||
|
"showSelectedNodeLabels": "Afficher les étiquettes des noeuds sélectionnés",
|
||||||
|
"hideSelectedNodeLabels": "Masquer les étiquettes des noeuds sélectionnés",
|
||||||
"showWelcomeTours": "Afficher les visites guidées pour les nouvelles versions",
|
"showWelcomeTours": "Afficher les visites guidées pour les nouvelles versions",
|
||||||
"help": "Site web de Node-RED",
|
"help": "Site web de Node-RED",
|
||||||
"projects": "Projets",
|
"projects": "Projets",
|
||||||
@ -274,23 +278,23 @@
|
|||||||
"recoveredNodesInfo": "Les noeuds importés sur ce flux contiennent un mauvais identifiant de flux. Ces noeuds ont été ajoutés à ce flux afin que vous puissiez les restaurer ou les supprimer.",
|
"recoveredNodesInfo": "Les noeuds importés sur ce flux contiennent un mauvais identifiant de flux. Ces noeuds ont été ajoutés à ce flux afin que vous puissiez les restaurer ou les supprimer.",
|
||||||
"recoveredNodesNotification": "<p>Noeuds importés sans identifiant de flux valide</p><p>Ils ont été ajoutés à un nouveau flux appelé '__flowName__'.</p>",
|
"recoveredNodesNotification": "<p>Noeuds importés sans identifiant de flux valide</p><p>Ils ont été ajoutés à un nouveau flux appelé '__flowName__'.</p>",
|
||||||
"export": {
|
"export": {
|
||||||
"selected": "noeuds sélectionnés",
|
"selected": "les noeuds sélectionnés",
|
||||||
"current": "flux actuel",
|
"current": "le flux actuel",
|
||||||
"all": "tous les flux",
|
"all": "tous les flux",
|
||||||
"compact": "condensé",
|
"compact": "Condensé",
|
||||||
"formatted": "formaté",
|
"formatted": "Formaté",
|
||||||
"copy": "Copier dans le presse-papier",
|
"copy": "Copier dans le presse-papier",
|
||||||
"export": "Exporter vers la bibliothèque",
|
"export": "Exporter vers la bibliothèque",
|
||||||
"exportAs": "Exporter en tant que",
|
"exportAs": "Exporter comme",
|
||||||
"overwrite": "Remplacer",
|
"overwrite": "Remplacer",
|
||||||
"exists": "<p><b>\"__file__\"</b> existe déjà.</p><p>Voulez-vous le remplacer ?</p>"
|
"exists": "<p><b>\"__file__\"</b> existe déjà.</p><p>Voulez-vous le remplacer ?</p>"
|
||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"import": "Importer vers",
|
"import": "Importer vers",
|
||||||
"importSelected": "Importation sélectionnée",
|
"importSelected": "Importer la sélection",
|
||||||
"importCopy": "Importer une copie",
|
"importCopy": "Importer une copie",
|
||||||
"viewNodes": "Afficher les noeuds...",
|
"viewNodes": "Vérifier ces noeuds",
|
||||||
"newFlow": "Nouveau flux",
|
"newFlow": "un nouveau flux",
|
||||||
"replace": "Remplacer",
|
"replace": "Remplacer",
|
||||||
"errors": {
|
"errors": {
|
||||||
"notArray": "L'entrée n'est pas un tableau JSON",
|
"notArray": "L'entrée n'est pas un tableau JSON",
|
||||||
@ -299,7 +303,8 @@
|
|||||||
"missingType": "L'entrée n'est pas un flux valide - l'élément '__index__' n'a pas de propriété 'type'"
|
"missingType": "L'entrée n'est pas un flux valide - l'élément '__index__' n'a pas de propriété 'type'"
|
||||||
},
|
},
|
||||||
"conflictNotification1": "Certains des noeuds que vous avez importés existent déjà dans votre espace de travail.",
|
"conflictNotification1": "Certains des noeuds que vous avez importés existent déjà dans votre espace de travail.",
|
||||||
"conflictNotification2": "Sélectionner les noeuds à importer et choisir s'il faut remplacer les noeuds existants ou en importer une copie."
|
"conflictNotification2": "Sélectionnez les noeuds à importer et choisissez s'il faut remplacer les noeuds existants ou en importer une copie.",
|
||||||
|
"alreadyExists": "Ce noeud existe déjà"
|
||||||
},
|
},
|
||||||
"copyMessagePath": "Chemin copié",
|
"copyMessagePath": "Chemin copié",
|
||||||
"copyMessageValue": "Valeur copiée",
|
"copyMessageValue": "Valeur copiée",
|
||||||
@ -391,10 +396,10 @@
|
|||||||
"subflowInstances": "Il existe __count__ instance de ce modèle de sous-flux",
|
"subflowInstances": "Il existe __count__ instance de ce modèle de sous-flux",
|
||||||
"subflowInstances_plural": "Il existe __count__ instances de ce modèle de sous-flux",
|
"subflowInstances_plural": "Il existe __count__ instances de ce modèle de sous-flux",
|
||||||
"editSubflowProperties": "modifier les propriétés",
|
"editSubflowProperties": "modifier les propriétés",
|
||||||
"input": "entrées:",
|
"input": "Entrées:",
|
||||||
"output": "sorties:",
|
"output": "Sorties:",
|
||||||
"status": "statut du noeud",
|
"status": "Statut du noeud",
|
||||||
"deleteSubflow": "supprimer le sous-flux",
|
"deleteSubflow": "Supprimer le sous-flux",
|
||||||
"confirmDelete": "Voulez-vous vraiment supprimer ce sous-flux ?",
|
"confirmDelete": "Voulez-vous vraiment supprimer ce sous-flux ?",
|
||||||
"info": "Description",
|
"info": "Description",
|
||||||
"category": "Catégorie",
|
"category": "Catégorie",
|
||||||
@ -416,6 +421,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noNodesSelected": "<strong>Impossible de créer un sous-flux</strong> : aucun noeud sélectionné",
|
"noNodesSelected": "<strong>Impossible de créer un sous-flux</strong> : aucun noeud sélectionné",
|
||||||
|
"acrossMultipleGroups": "Impossible de créer un sous-flux sur plusieurs groupes",
|
||||||
"multipleInputsToSelection": "<strong>Impossible de créer un sous-flux</strong> : plusieurs entrées pour la sélection"
|
"multipleInputsToSelection": "<strong>Impossible de créer un sous-flux</strong> : plusieurs entrées pour la sélection"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -447,8 +453,8 @@
|
|||||||
"default": "Par défaut",
|
"default": "Par défaut",
|
||||||
"noDefaultLabel": "Aucune",
|
"noDefaultLabel": "Aucune",
|
||||||
"defaultLabel": "Utiliser l'étiquette par défaut",
|
"defaultLabel": "Utiliser l'étiquette par défaut",
|
||||||
"searchIcons": "Icônes de recherche",
|
"searchIcons": "Rechercher une icône",
|
||||||
"useDefault": "Utilisation par défaut",
|
"useDefault": "Icône par défaut",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"show": "Afficher",
|
"show": "Afficher",
|
||||||
"hide": "Masquer",
|
"hide": "Masquer",
|
||||||
@ -498,19 +504,19 @@
|
|||||||
"keyboard": {
|
"keyboard": {
|
||||||
"title": "Raccourcis clavier",
|
"title": "Raccourcis clavier",
|
||||||
"keyboard": "Clavier",
|
"keyboard": "Clavier",
|
||||||
"filterActions": "Actions de filtrage",
|
"filterActions": "Rechercher l'action",
|
||||||
"shortcut": "raccourci",
|
"shortcut": "Raccourci",
|
||||||
"scope": "portée",
|
"scope": "Portée",
|
||||||
"unassigned": "Non attribué",
|
"unassigned": "Non attribué",
|
||||||
"global": "global",
|
"global": "Global",
|
||||||
"workspace": "espace de travail",
|
"workspace": "Espace de travail",
|
||||||
"editor": "boîte de dialogue d'édition",
|
"editor": "Boîte de dialogue d'édition",
|
||||||
"selectAll": "Tout sélectionner",
|
"selectAll": "Tout sélectionner",
|
||||||
"selectNone": "Ne rien sélectionner",
|
"selectNone": "Ne rien sélectionner",
|
||||||
"selectAllConnected": "Sélectionner tous les éléments connectés",
|
"selectAllConnected": "Sélectionner tous les éléments connectés",
|
||||||
"addRemoveNode": "Ajouter/supprimer un noeud de la sélection",
|
"addRemoveNode": "Ajouter/supprimer un noeud de la sélection",
|
||||||
"editSelected": "Modifier le noeud sélectionné",
|
"editSelected": "Modifier le noeud sélectionné",
|
||||||
"deleteSelected": "Supprimer les noeuds ou le lien sélectionné(s)",
|
"deleteSelected": "Supprimer la sélection",
|
||||||
"deleteReconnect": "Supprimer et reconnecter",
|
"deleteReconnect": "Supprimer et reconnecter",
|
||||||
"importNode": "Importer les noeuds",
|
"importNode": "Importer les noeuds",
|
||||||
"exportNode": "Exporter les noeuds",
|
"exportNode": "Exporter les noeuds",
|
||||||
@ -550,22 +556,22 @@
|
|||||||
},
|
},
|
||||||
"palette": {
|
"palette": {
|
||||||
"noInfo": "Pas d'information disponible",
|
"noInfo": "Pas d'information disponible",
|
||||||
"filter": "Filtrer les noeuds",
|
"filter": "Rechercher le noeud",
|
||||||
"search": "Rechercher les modules",
|
"search": "Rechercher les modules",
|
||||||
"addCategory": "Ajouter un nouveau...",
|
"addCategory": "Ajouter un nouveau...",
|
||||||
"label": {
|
"label": {
|
||||||
"subflows": "sous-flux",
|
"subflows": "Sous-flux",
|
||||||
"network": "réseau",
|
"network": "Réseau",
|
||||||
"common": "commun",
|
"common": "Commun",
|
||||||
"input": "entrée",
|
"input": "Entrée",
|
||||||
"output": "sortie",
|
"output": "Sortie",
|
||||||
"function": "fonction",
|
"function": "Fonction",
|
||||||
"sequence": "séquence",
|
"sequence": "Séquence",
|
||||||
"parser": "analyseur",
|
"parser": "Analyseur",
|
||||||
"social": "social",
|
"social": "Social",
|
||||||
"storage": "stockage",
|
"storage": "Stockage",
|
||||||
"analysis": "analyse",
|
"analysis": "Analyse",
|
||||||
"advanced": "avancé"
|
"advanced": "Avancé"
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"collapse-all": "Réduire toutes les catégories",
|
"collapse-all": "Réduire toutes les catégories",
|
||||||
@ -586,6 +592,7 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"title": "Gérer la palette",
|
"title": "Gérer la palette",
|
||||||
"palette": "Palette",
|
"palette": "Palette",
|
||||||
|
"allCatalogs": "Tous les catalogues",
|
||||||
"times": {
|
"times": {
|
||||||
"seconds": "il y a quelques secondes",
|
"seconds": "il y a quelques secondes",
|
||||||
"minutes": "il y a quelques minutes",
|
"minutes": "il y a quelques minutes",
|
||||||
@ -609,24 +616,25 @@
|
|||||||
"nodeCount_plural": "__label__ noeuds",
|
"nodeCount_plural": "__label__ noeuds",
|
||||||
"moduleCount": "__count__ module disponible",
|
"moduleCount": "__count__ module disponible",
|
||||||
"moduleCount_plural": "__count__ modules disponibles",
|
"moduleCount_plural": "__count__ modules disponibles",
|
||||||
"inuse": "en cours d'utilisation",
|
"inuse": "En cours d'utilisation",
|
||||||
"enableall": "activer tout",
|
"enableall": "Activer tout",
|
||||||
"disableall": "désactiver tout",
|
"disableall": "Désactiver tout",
|
||||||
"enable": "activer",
|
"enable": "Activer",
|
||||||
"disable": "désactiver",
|
"disable": "Désactiver",
|
||||||
"remove": "supprimer",
|
"remove": "Supprimer",
|
||||||
"update": "mettre à jour vers __version__",
|
"update": "Mettre à jour vers __version__",
|
||||||
"updated": "mis à jour",
|
"updated": "Mis à jour",
|
||||||
"install": "installer",
|
"install": "Installer",
|
||||||
"installed": "installé",
|
"installed": "Installé",
|
||||||
"conflict": "conflit",
|
"conflict": "Conflit",
|
||||||
"conflictTip": "<p>Ce module ne peut pas être installé car il inclut un<br/>type de noeud qui a déjà été installé</p><p>Conflits avec <code>__module__</code></p>",
|
"conflictTip": "<p>Ce module ne peut pas être installé car il inclut un<br/>type de noeud qui a déjà été installé</p><p>Conflits avec <code>__module__</code></p>",
|
||||||
"loading": "Chargement des catalogues...",
|
"loading": "Chargement des catalogues...",
|
||||||
"tab-nodes": "Noeuds",
|
"tab-nodes": "Noeuds",
|
||||||
"tab-install": "Installer",
|
"tab-install": "Installer",
|
||||||
"sort": "trier:",
|
"sort": "Trier:",
|
||||||
"sortAZ": "a-z",
|
"sortRelevance": "Pertinence",
|
||||||
"sortRecent": "récent",
|
"sortAZ": "A-Z",
|
||||||
|
"sortRecent": "Récent",
|
||||||
"more": "+ __count__ en plus",
|
"more": "+ __count__ en plus",
|
||||||
"upload": "Charger le fichier tgz du module",
|
"upload": "Charger le fichier tgz du module",
|
||||||
"refresh": "Actualiser la liste des modules",
|
"refresh": "Actualiser la liste des modules",
|
||||||
@ -667,7 +675,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"name": "Information",
|
"name": "Information",
|
||||||
"tabName": "Nom",
|
"tabName": "Nom",
|
||||||
"label": "info",
|
"label": "Info",
|
||||||
"node": "Noeud",
|
"node": "Noeud",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"group": "Groupe",
|
"group": "Groupe",
|
||||||
@ -681,10 +689,10 @@
|
|||||||
"properties": "Propriétés",
|
"properties": "Propriétés",
|
||||||
"info": "Information",
|
"info": "Information",
|
||||||
"desc": "Description",
|
"desc": "Description",
|
||||||
"blank": "vide",
|
"blank": "Vide",
|
||||||
"null": "nul",
|
"null": "Nul",
|
||||||
"showMore": "afficher en plus",
|
"showMore": "Afficher en plus",
|
||||||
"showLess": "afficher en moins",
|
"showLess": "Afficher en moins",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
"selection": "Sélection",
|
"selection": "Sélection",
|
||||||
"nodes": "__count__ noeuds",
|
"nodes": "__count__ noeuds",
|
||||||
@ -695,7 +703,7 @@
|
|||||||
"arrayItems": "__count__ éléments",
|
"arrayItems": "__count__ éléments",
|
||||||
"showTips": "Vous pouvez ouvrir les astuces à partir du panneau des paramètres",
|
"showTips": "Vous pouvez ouvrir les astuces à partir du panneau des paramètres",
|
||||||
"outline": "Plan",
|
"outline": "Plan",
|
||||||
"empty": "vide",
|
"empty": "Vide",
|
||||||
"globalConfig": "Noeuds de configuration globale",
|
"globalConfig": "Noeuds de configuration globale",
|
||||||
"triggerAction": "Déclencher une action",
|
"triggerAction": "Déclencher une action",
|
||||||
"find": "Rechercher dans l'espace de travail",
|
"find": "Rechercher dans l'espace de travail",
|
||||||
@ -706,7 +714,7 @@
|
|||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"name": "Aide",
|
"name": "Aide",
|
||||||
"label": "aide",
|
"label": "Aide",
|
||||||
"search": "Aide à la recherche",
|
"search": "Aide à la recherche",
|
||||||
"nodeHelp": "Aide sur les noeuds",
|
"nodeHelp": "Aide sur les noeuds",
|
||||||
"showHelp": "Afficher l'aide",
|
"showHelp": "Afficher l'aide",
|
||||||
@ -717,23 +725,23 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"name": "Noeuds de configuration",
|
"name": "Noeuds de configuration",
|
||||||
"label": "configuration",
|
"label": "Configuration",
|
||||||
"global": "Tous les flux",
|
"global": "Tous les flux",
|
||||||
"none": "aucun",
|
"none": "Aucun",
|
||||||
"subflows": "sous-flux",
|
"subflows": "Sous-flux",
|
||||||
"flows": "flux",
|
"flows": "Flux",
|
||||||
"filterAll": "tout",
|
"filterAll": "Tout",
|
||||||
"showAllConfigNodes": "Afficher tous les noeuds de configuration",
|
"showAllConfigNodes": "Afficher tous les noeuds de configuration",
|
||||||
"filterUnused": "inutilisé",
|
"filterUnused": "Inutilisé",
|
||||||
"showAllUnusedConfigNodes": "Afficher tous les noeuds de configuration inutilisés",
|
"showAllUnusedConfigNodes": "Afficher tous les noeuds de configuration inutilisés",
|
||||||
"filtered": "__count__ caché(s)"
|
"filtered": "__count__ caché(s)"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"name": "Données contextuelles",
|
"name": "Données contextuelles",
|
||||||
"label": "contexte",
|
"label": "Contexte",
|
||||||
"none": "aucune sélection",
|
"none": "Aucune sélection",
|
||||||
"refresh": "actualiser pour charger",
|
"refresh": "Actualiser pour charger",
|
||||||
"empty": "vide",
|
"empty": "Vide",
|
||||||
"node": "Noeud",
|
"node": "Noeud",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
"global": "Global",
|
"global": "Global",
|
||||||
@ -744,10 +752,10 @@
|
|||||||
},
|
},
|
||||||
"palette": {
|
"palette": {
|
||||||
"name": "Gestion des palettes",
|
"name": "Gestion des palettes",
|
||||||
"label": "palette"
|
"label": "Palette"
|
||||||
},
|
},
|
||||||
"project": {
|
"project": {
|
||||||
"label": "projet",
|
"label": "Projet",
|
||||||
"name": "Projet",
|
"name": "Projet",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"dependencies": "Dépendances",
|
"dependencies": "Dépendances",
|
||||||
@ -760,11 +768,11 @@
|
|||||||
"showProjectSettings": "Afficher les paramètres du projet",
|
"showProjectSettings": "Afficher les paramètres du projet",
|
||||||
"projectSettings": {
|
"projectSettings": {
|
||||||
"title": "Paramètres du projet",
|
"title": "Paramètres du projet",
|
||||||
"edit": "modifier",
|
"edit": "Modifier",
|
||||||
"none": "Vide",
|
"none": "Vide",
|
||||||
"install": "installer",
|
"install": "Installer",
|
||||||
"removeFromProject": "supprimer du projet",
|
"removeFromProject": "Supprimer du projet",
|
||||||
"addToProject": "ajouter au projet",
|
"addToProject": "Ajouter au projet",
|
||||||
"files": "Fichiers",
|
"files": "Fichiers",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
"credentials": "Identifiants",
|
"credentials": "Identifiants",
|
||||||
@ -812,7 +820,7 @@
|
|||||||
"workflowAutoTip": "Les modifications sont validées automatiquement à chaque déploiement",
|
"workflowAutoTip": "Les modifications sont validées automatiquement à chaque déploiement",
|
||||||
"sshKeys": "Clés SSH",
|
"sshKeys": "Clés SSH",
|
||||||
"sshKeysTip": "Vous permet de créer des connexions sécurisées aux référentiels Git distants.",
|
"sshKeysTip": "Vous permet de créer des connexions sécurisées aux référentiels Git distants.",
|
||||||
"add": "ajouter une clé",
|
"add": "Ajouter une clé",
|
||||||
"addSshKey": "Ajouter une clé SSH",
|
"addSshKey": "Ajouter une clé SSH",
|
||||||
"addSshKeyTip": "Générer une nouvelle paire de clés publique/privée",
|
"addSshKeyTip": "Générer une nouvelle paire de clés publique/privée",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
@ -826,7 +834,7 @@
|
|||||||
"copyPublicKey": "Copier la clé publique dans le presse-papiers",
|
"copyPublicKey": "Copier la clé publique dans le presse-papiers",
|
||||||
"delete": "Supprimer une clé",
|
"delete": "Supprimer une clé",
|
||||||
"gitConfig": "Configuration Git",
|
"gitConfig": "Configuration Git",
|
||||||
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer la clé SSH __nom__ ? Ça ne peut pas être annulé."
|
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer la clé SSH __name__ ? Ça ne peut pas être annulé."
|
||||||
},
|
},
|
||||||
"versionControl": {
|
"versionControl": {
|
||||||
"unstagedChanges": "Abandon des changements",
|
"unstagedChanges": "Abandon des changements",
|
||||||
@ -848,7 +856,7 @@
|
|||||||
"none": "Vide",
|
"none": "Vide",
|
||||||
"conflictResolve": "Tous les conflits ont été résolus. Valider les modifications pour terminer la fusion.",
|
"conflictResolve": "Tous les conflits ont été résolus. Valider les modifications pour terminer la fusion.",
|
||||||
"localFiles": "Fichiers locaux",
|
"localFiles": "Fichiers locaux",
|
||||||
"all": "tout",
|
"all": "Tout",
|
||||||
"unmergedChanges": "Modifications non fusionnées",
|
"unmergedChanges": "Modifications non fusionnées",
|
||||||
"abortMerge": "Abandonner la fusion",
|
"abortMerge": "Abandonner la fusion",
|
||||||
"commit": "Valider",
|
"commit": "Valider",
|
||||||
@ -1097,9 +1105,9 @@
|
|||||||
"desc8": "Le fichier contenant les identifiants ne sera pas crypté et son contenu sera facilement lisible",
|
"desc8": "Le fichier contenant les identifiants ne sera pas crypté et son contenu sera facilement lisible",
|
||||||
"create-project-files": "Créer des fichiers de projet",
|
"create-project-files": "Créer des fichiers de projet",
|
||||||
"create-project": "Créer un projet",
|
"create-project": "Créer un projet",
|
||||||
"already-exists": "existe déjà",
|
"already-exists": "Existe déjà",
|
||||||
"git-error": "Erreur Git",
|
"git-error": "Erreur Git",
|
||||||
"git-auth-error": "erreur d'authentification Git"
|
"git-auth-error": "Erreur d'authentification Git"
|
||||||
},
|
},
|
||||||
"create-success": {
|
"create-success": {
|
||||||
"success": "Vous avez créé avec succès votre premier projet !",
|
"success": "Vous avez créé avec succès votre premier projet !",
|
||||||
@ -1135,8 +1143,8 @@
|
|||||||
"desc2": "Avant de pouvoir cloner un référentiel sur ssh, vous devez ajouter une clé SSH pour y accéder.",
|
"desc2": "Avant de pouvoir cloner un référentiel sur ssh, vous devez ajouter une clé SSH pour y accéder.",
|
||||||
"add-ssh-key": "Ajouter une clé ssh",
|
"add-ssh-key": "Ajouter une clé ssh",
|
||||||
"credentials-encryption-key": "Clé de chiffrement des identifiants",
|
"credentials-encryption-key": "Clé de chiffrement des identifiants",
|
||||||
"already-exists-2": "existe déjà",
|
"already-exists-2": "Existe déjà",
|
||||||
"git-error": "erreur git",
|
"git-error": "Erreur git",
|
||||||
"con-failed": "La connexion a échoué",
|
"con-failed": "La connexion a échoué",
|
||||||
"not-git": "Ce n'est pas un dépôt git",
|
"not-git": "Ce n'est pas un dépôt git",
|
||||||
"no-resource": "Référentiel introuvable",
|
"no-resource": "Référentiel introuvable",
|
||||||
@ -1148,8 +1156,8 @@
|
|||||||
"confirm": "Voulez-vous vraiment supprimer ce projet ?"
|
"confirm": "Voulez-vous vraiment supprimer ce projet ?"
|
||||||
},
|
},
|
||||||
"create-project-list": {
|
"create-project-list": {
|
||||||
"search": "rechercher vos projets",
|
"search": "Rechercher vos projets",
|
||||||
"current": "actuel"
|
"current": "Actuel"
|
||||||
},
|
},
|
||||||
"require-clean": {
|
"require-clean": {
|
||||||
"confirm": "<p>Vous avez des modifications non déployées qui seront perdues.</p><p>Voulez-vous continuer ?</p>"
|
"confirm": "<p>Vous avez des modifications non déployées qui seront perdues.</p><p>Voulez-vous continuer ?</p>"
|
||||||
@ -1198,23 +1206,11 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "Information système"
|
"title": "Information système"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "Allemand",
|
|
||||||
"en-US": "Anglais",
|
|
||||||
"fr": "Français",
|
|
||||||
"ja": "Japonais",
|
|
||||||
"ko": "Coréen",
|
|
||||||
"pt-BR": "Portugais brésilien",
|
|
||||||
"ru": "Russe",
|
|
||||||
"zh-CN": "Chinois (Simplifié)",
|
|
||||||
"zh-TW": "Chinois (Traditionnel)"
|
|
||||||
},
|
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "Données JSON invalides : __error__",
|
"invalid-json": "Données JSON invalides : __error__",
|
||||||
"invalid-json-prop": "__prop__: données JSON invalides : __error__",
|
"invalid-expr": "Expression JSONata invalide : __error__",
|
||||||
"invalid-prop": "Expression de propriété non valide",
|
"invalid-prop": "Expression de propriété invalide",
|
||||||
"invalid-prop-prop": "__prop__: expression de propriété invalide",
|
|
||||||
"invalid-num": "Numéro invalide",
|
"invalid-num": "Numéro invalide",
|
||||||
"invalid-num-prop": "__prop__: numéro invalide",
|
"invalid-num-prop": "__prop__: numéro invalide",
|
||||||
"invalid-regexp": "Modèle d'entrée non valide",
|
"invalid-regexp": "Modèle d'entrée non valide",
|
||||||
@ -1226,6 +1222,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
|
"showActionList": "Afficher la liste des actions",
|
||||||
"insert": "Insérer",
|
"insert": "Insérer",
|
||||||
"node": "Noeud",
|
"node": "Noeud",
|
||||||
"junction": "Jonction",
|
"junction": "Jonction",
|
||||||
@ -1235,5 +1232,159 @@
|
|||||||
"environment": "Environment",
|
"environment": "Environment",
|
||||||
"header": "Variables d'environnement globales",
|
"header": "Variables d'environnement globales",
|
||||||
"revert": "Rétablir"
|
"revert": "Rétablir"
|
||||||
|
},
|
||||||
|
"action-list": {
|
||||||
|
"toggle-show-tips": "Basculer l'affichage des astuces",
|
||||||
|
"show-about": "Afficher la description de Node-RED",
|
||||||
|
"show-welcome-tour": "Afficher la visite de bienvenue",
|
||||||
|
"show-next-tab": "Afficher l'onglet suivant",
|
||||||
|
"show-previous-tab": "Afficher l'onglet précédent",
|
||||||
|
"add-flow": "Ajouter un flux",
|
||||||
|
"add-flow-to-right": "Ajouter un flux à droite",
|
||||||
|
"edit-flow": "Modifier le flux",
|
||||||
|
"remove-flow": "Supprimer le flux",
|
||||||
|
"enable-flow": "Activer le flux",
|
||||||
|
"disable-flow": "Désactiver le flux",
|
||||||
|
"hide-flow": "Masquer le flux",
|
||||||
|
"hide-other-flows": "Masquer les autres flux",
|
||||||
|
"hide-all-flows": "Masquer tous les flux",
|
||||||
|
"show-all-flows": "Afficher tous les flux",
|
||||||
|
"show-last-hidden-flow": "Afficher le dernier flux masqué",
|
||||||
|
"list-modified-nodes": "Afficher les flux modifiés",
|
||||||
|
"list-hidden-flows": "Afficher les flux cachés",
|
||||||
|
"list-flows": "Lister les flux",
|
||||||
|
"list-subflows": "Liste les sous-flux",
|
||||||
|
"go-to-previous-location": "Aller à l'emplacement précédent",
|
||||||
|
"go-to-next-location": "Aller à l'emplacement suivant",
|
||||||
|
"copy-selection-to-internal-clipboard": "Copier la sélection dans le presse-papiers",
|
||||||
|
"cut-selection-to-internal-clipboard": "Couper la sélection dans le presse-papiers",
|
||||||
|
"paste-from-internal-clipboard": "Coller depuis le presse-papiers",
|
||||||
|
"detach-selected-nodes": "Détacher les noeuds sélectionnés",
|
||||||
|
"delete-selection": "Supprimer la sélection",
|
||||||
|
"delete-selection-and-reconnect": "Supprimer la sélection et reconnecter",
|
||||||
|
"edit-selected-node": "Modifier le noeud sélectionné",
|
||||||
|
"go-to-selection": "Aller à la sélection",
|
||||||
|
"undo": "Annuler les modifications",
|
||||||
|
"redo": "Rétablir les modifications",
|
||||||
|
"select-all-nodes": "Sélectionner tous les noeuds",
|
||||||
|
"select-none": "Sélectionner un noeud",
|
||||||
|
"enable-selected-nodes": "Activer les noeuds sélectionnés",
|
||||||
|
"disable-selected-nodes": "Désactiver les noeuds sélectionnés",
|
||||||
|
"toggle-show-grid": "Basculer l'affichage de la grille",
|
||||||
|
"toggle-snap-grid": "Basculer l'aide au placement des noeuds",
|
||||||
|
"toggle-status": "Commuter l'état",
|
||||||
|
"show-selected-node-labels": "Afficher les étiquettes des noeuds sélectionnés",
|
||||||
|
"hide-selected-node-labels": "Masquer les étiquettes des noeuds sélectionnés",
|
||||||
|
"scroll-view-up": "Faire défiler vers le haut",
|
||||||
|
"scroll-view-right": "Faire défiler vers la droite",
|
||||||
|
"scroll-view-down": "Faire défiler vers le bas",
|
||||||
|
"scroll-view-left": "Faire défiler vers la gauche",
|
||||||
|
"step-view-up": "Faire défiler d'une unité vers le haut",
|
||||||
|
"step-view-right": "Faire défiler d'une unité vers la droite",
|
||||||
|
"step-view-down": "Faire défiler d'une unité vers le bas",
|
||||||
|
"step-view-left": "Faire défiler d'une unité vers la gauche",
|
||||||
|
"move-selection-up": "Déplacer la sélection vers le haut",
|
||||||
|
"move-selection-right": "Déplacer la sélection vers la droite",
|
||||||
|
"move-selection-down": "Déplacer la sélection vers le bas",
|
||||||
|
"move-selection-left": "Déplacer la sélection vers la gauche",
|
||||||
|
"move-selection-forwards": "Avancer la sélection",
|
||||||
|
"move-selection-backwards": "Reculer la sélection",
|
||||||
|
"move-selection-to-front": "Déplacer la sélection vers l'avant",
|
||||||
|
"move-selection-to-back": "Déplacer la sélection vers l'arrière",
|
||||||
|
"step-selection-up": "Déplacer la sélection d'une unité vers le haut",
|
||||||
|
"step-selection-right": "Déplacer la sélection d'une unité vers la droite",
|
||||||
|
"step-selection-down": "Déplacer la sélection d'une unité vers le bas",
|
||||||
|
"step-selection-left": "Déplacer la sélection d'une unité vers la gauche",
|
||||||
|
"select-connected-nodes": "Sélectionner les noeuds connectés",
|
||||||
|
"select-downstream-nodes": "Sélectionner les noeuds connectés en aval",
|
||||||
|
"select-upstream-nodes": "Sélectionner les noeuds connectés en amont",
|
||||||
|
"go-to-next-node": "Aller au noeud suivant",
|
||||||
|
"go-to-previous-node": "Aller au noeud précédent",
|
||||||
|
"go-to-next-sibling": "Aller au noeud frère suivant",
|
||||||
|
"go-to-previous-sibling": "Aller au noeud frère précédent",
|
||||||
|
"go-to-nearest-node-on-left": "Aller au noeud gauche le plus proche",
|
||||||
|
"go-to-nearest-node-on-right": "Aller au noeud droit le plus proche",
|
||||||
|
"go-to-nearest-node-above": "Aller au noeud supérieur le plus proche",
|
||||||
|
"go-to-nearest-node-below": "Aller au noeud le plus proche ci-dessous",
|
||||||
|
"align-selection-to-grid": "Aligner la sélection",
|
||||||
|
"align-selection-to-left": "Aligner la sélection à gauche",
|
||||||
|
"align-selection-to-right": "Aligner la sélection à droite",
|
||||||
|
"align-selection-to-top": "Aligner la sélection en haut",
|
||||||
|
"align-selection-to-bottom": "Aligner la sélection vers le bas",
|
||||||
|
"align-selection-to-middle": "Aligner la sélection au centre verticalement",
|
||||||
|
"align-selection-to-center": "Aligner la sélection au centre horizontalement",
|
||||||
|
"distribute-selection-horizontally": "Distribuer la sélection horizontalement",
|
||||||
|
"distribute-selection-vertical": "Distribuer la sélection verticalement",
|
||||||
|
"wire-series-of-nodes": "Connecter les noeuds en série",
|
||||||
|
"wire-node-to-multiple": "Connecter les noeuds à plusieurs",
|
||||||
|
"wire-multiple-to-node": "Connecter plusieurs au noeud",
|
||||||
|
"split-wire-with-link-nodes": "Diviser le fil avec des noeuds de liaison",
|
||||||
|
"generate-node-names": "Générer les noms de noeuds",
|
||||||
|
"show-user-settings": "Afficher les paramètres utilisateur",
|
||||||
|
"show-help": "Afficher l'aide",
|
||||||
|
"toggle-palette": "Basculer l'affichage de la palette",
|
||||||
|
"show-event-log": "Afficher le journal des événements",
|
||||||
|
"manage-palette": "Gérer la palette",
|
||||||
|
"toggle-sidebar": "Basculer l'affichage de la barre latérale",
|
||||||
|
"show-info-tab": "Afficher l'onglet d'informations sur le noeud",
|
||||||
|
"show-help-tab": "Afficher l'onglet d'aide du noeud",
|
||||||
|
"show-config-tab": "Afficher l'onglet du noeud de configuration",
|
||||||
|
"select-all-config-nodes": "Sélectionner tous les noeuds de configuration",
|
||||||
|
"delete-config-selection": "Supprimer le noeud de configuration sélectionné",
|
||||||
|
"show-context-tab": "Afficher l'onglet des données contextuelles",
|
||||||
|
"create-subflow": "Créer un sous-flux",
|
||||||
|
"convert-to-subflow": "Convertir la sélection en sous-flux",
|
||||||
|
"group-selection": "Grouper la sélection",
|
||||||
|
"ungroup-selection": "Dissocier la sélection",
|
||||||
|
"merge-selection-to-group": "Fusionner la sélection dans le groupe",
|
||||||
|
"remove-selection-from-group": "Supprimer la sélection du groupe",
|
||||||
|
"copy-group-style": "Copier le style du groupe",
|
||||||
|
"paste-group-style": "Coller le style du groupe",
|
||||||
|
"show-export-dialog": "Afficher la boîte de dialogue d'exportation",
|
||||||
|
"show-import-dialog": "Afficher la boîte de dialogue d'importation",
|
||||||
|
"show-library-export-dialog": "Afficher la boîte de dialogue d'exportation de la bibliothèque",
|
||||||
|
"show-library-import-dialog": "Afficher la boîte de dialogue d'importation de bibliothèque",
|
||||||
|
"show-examples-import-dialog": "Afficher la boîte de dialogue d'importation d'exemples",
|
||||||
|
"search": "Rechercher",
|
||||||
|
"search-previous": "Recherche précédente",
|
||||||
|
"search-next": "Recherche suivante",
|
||||||
|
"show-action-list": "Afficher la liste d'actions",
|
||||||
|
"confirm-edit-tray": "Confirmer la modification",
|
||||||
|
"cancel-edit-tray": "Annuler la modification",
|
||||||
|
"show-remote-diff": "Afficher les différences avec les modifications distantes",
|
||||||
|
"deploy-flows": "Déployer des flux",
|
||||||
|
"restart-flows": "Redémarrer les flux",
|
||||||
|
"set-deploy-type-to-full": "Définir le déploiement sur 'tout'",
|
||||||
|
"set-deploy-type-to-modified-flows": "Définir le déploiement sur 'flux modifiés'",
|
||||||
|
"set-deploy-type-to-modified-nodes": "Définir le déploiement sur 'noeuds modifiés'",
|
||||||
|
"show-debug-tab": "Afficher l'onglet de débogage",
|
||||||
|
"clear-debug-messages": "Supprimer les messages de débogage",
|
||||||
|
"clear-filtered-debug-messages": "Supprimer les messages de débogage filtrés",
|
||||||
|
"activate-selected-debug-nodes": "Activer les noeuds de débogage sélectionnés",
|
||||||
|
"activate-all-debug-nodes": "Activer tous les noeuds de débogage",
|
||||||
|
"activate-all-flow-debug-nodes": "Activer tous les noeuds de débogage dans un flux",
|
||||||
|
"deactivate-selected-debug-nodes": "Désactiver les noeuds de débogage sélectionnés",
|
||||||
|
"deactivate-all-debug-nodes": "Désactiver tous les noeuds de débogage",
|
||||||
|
"deactivate-all-flow-debug-nodes": "Désactiver tous les noeuds de débogage dans un flux",
|
||||||
|
"zoom-in": "Zoomer",
|
||||||
|
"zoom-out": "Dézoomer",
|
||||||
|
"zoom-reset": "Réinitialiser le zoom",
|
||||||
|
"toggle-navigator": "Basculer l'affichage du navigateur",
|
||||||
|
"show-system-info": "Afficher les informations système",
|
||||||
|
"split-wires-with-junctions": "Diviser les fils avec des jonctions",
|
||||||
|
"new-project": "Nouveau projet",
|
||||||
|
"open-project": "Ouvrir le projet",
|
||||||
|
"show-project-settings": "Afficher les paramètres du projet",
|
||||||
|
"show-version-control-tab": "Afficher l'onglet de contrôle de version",
|
||||||
|
"start-flows": "Démarrer les flux",
|
||||||
|
"stop-flows": "Arrêter les flux",
|
||||||
|
"copy-item-url": "Copier l'URL de l'élément",
|
||||||
|
"copy-item-edit-url": "Copier l'URL de modification de l'élément",
|
||||||
|
"move-flow-to-start": "Déplacer le flux jusqu'au début",
|
||||||
|
"move-flow-to-end": "Déplacer le flux jusqu'à la fin",
|
||||||
|
"show-global-env": "Afficher les variables d'environnement globales",
|
||||||
|
"lock-flow": "Verrouiller le flux",
|
||||||
|
"unlock-flow": "Déverrouiller le flux",
|
||||||
|
"show-node-help": "Afficher l'aide du noeud"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,5 +270,9 @@
|
|||||||
"$moment": {
|
"$moment": {
|
||||||
"args": "[str]",
|
"args": "[str]",
|
||||||
"desc": "Obtient un objet de date à l'aide de la bibliothèque Moment."
|
"desc": "Obtient un objet de date à l'aide de la bibliothèque Moment."
|
||||||
|
},
|
||||||
|
"$clone": {
|
||||||
|
"args": "valeur",
|
||||||
|
"desc": "Cloner un objet en toute sécurité."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,6 @@
|
|||||||
"selectionToSubflow": "選択部分をサブフロー化",
|
"selectionToSubflow": "選択部分をサブフロー化",
|
||||||
"flows": "フロー",
|
"flows": "フロー",
|
||||||
"add": "フローを新規追加",
|
"add": "フローを新規追加",
|
||||||
"rename": "フロー名を変更",
|
|
||||||
"delete": "フローを削除",
|
"delete": "フローを削除",
|
||||||
"keyboardShortcuts": "ショートカットキーの説明",
|
"keyboardShortcuts": "ショートカットキーの説明",
|
||||||
"login": "ログイン",
|
"login": "ログイン",
|
||||||
@ -130,6 +129,11 @@
|
|||||||
"editPalette": "パレットの管理",
|
"editPalette": "パレットの管理",
|
||||||
"other": "その他",
|
"other": "その他",
|
||||||
"showTips": "ヒントを表示",
|
"showTips": "ヒントを表示",
|
||||||
|
"showNodeHelp": "ノードのヘルプを表示",
|
||||||
|
"enableSelectedNodes": "選択したノードを有効化",
|
||||||
|
"disableSelectedNodes": "選択したノードを無効化",
|
||||||
|
"showSelectedNodeLabels": "選択したノードのラベル表示",
|
||||||
|
"hideSelectedNodeLabels": "選択したノードのラベル非表示",
|
||||||
"showWelcomeTours": "新バージョンのガイドツアーを表示",
|
"showWelcomeTours": "新バージョンのガイドツアーを表示",
|
||||||
"help": "Node-REDウェブサイト",
|
"help": "Node-REDウェブサイト",
|
||||||
"projects": "プロジェクト",
|
"projects": "プロジェクト",
|
||||||
@ -511,7 +515,7 @@
|
|||||||
"selectAllConnected": "接続されたノードを選択",
|
"selectAllConnected": "接続されたノードを選択",
|
||||||
"addRemoveNode": "ノードの選択、選択解除",
|
"addRemoveNode": "ノードの選択、選択解除",
|
||||||
"editSelected": "選択したノードを編集",
|
"editSelected": "選択したノードを編集",
|
||||||
"deleteSelected": "選択したノードや接続を削除",
|
"deleteSelected": "選択部分を削除",
|
||||||
"deleteReconnect": "削除と再接続",
|
"deleteReconnect": "削除と再接続",
|
||||||
"importNode": "フローの読み込み",
|
"importNode": "フローの読み込み",
|
||||||
"exportNode": "フローの書き出し",
|
"exportNode": "フローの書き出し",
|
||||||
@ -1201,23 +1205,11 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "システム情報"
|
"title": "システム情報"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "ドイツ語",
|
|
||||||
"en-US": "英語",
|
|
||||||
"fr": "フランス語",
|
|
||||||
"ja": "日本語",
|
|
||||||
"ko": "韓国語",
|
|
||||||
"pt-BR": "ポルトガル語",
|
|
||||||
"ru": "ロシア語",
|
|
||||||
"zh-CN": "中国語(簡体)",
|
|
||||||
"zh-TW": "中国語(繁体)"
|
|
||||||
},
|
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "JSONデータが不正: __error__",
|
"invalid-json": "JSONデータが不正: __error__",
|
||||||
"invalid-json-prop": "__prop__: JSONデータが不正: __error__",
|
"invalid-expr": "不正なJSONata式: __error__",
|
||||||
"invalid-prop": "プロパティ式が不正",
|
"invalid-prop": "プロパティ式が不正",
|
||||||
"invalid-prop-prop": "__prop__: プロパティ式が不正",
|
|
||||||
"invalid-num": "数値が不正",
|
"invalid-num": "数値が不正",
|
||||||
"invalid-num-prop": "__prop__: 数値が不正",
|
"invalid-num-prop": "__prop__: 数値が不正",
|
||||||
"invalid-regexp": "入力パターンが不正",
|
"invalid-regexp": "入力パターンが不正",
|
||||||
@ -1229,6 +1221,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
|
"showActionList": "動作一覧を表示",
|
||||||
"insert": "挿入",
|
"insert": "挿入",
|
||||||
"node": "ノード",
|
"node": "ノード",
|
||||||
"junction": "分岐点",
|
"junction": "分岐点",
|
||||||
|
@ -79,7 +79,6 @@
|
|||||||
"selectionToSubflow": "서브 플로우 선택",
|
"selectionToSubflow": "서브 플로우 선택",
|
||||||
"flows": "플로우",
|
"flows": "플로우",
|
||||||
"add": "추가",
|
"add": "추가",
|
||||||
"rename": "이름변경",
|
|
||||||
"delete": "삭제",
|
"delete": "삭제",
|
||||||
"keyboardShortcuts": "단축키",
|
"keyboardShortcuts": "단축키",
|
||||||
"login": "로그인",
|
"login": "로그인",
|
||||||
|
@ -109,7 +109,6 @@
|
|||||||
"selectionToSubflow": "Seleção para subfluxo",
|
"selectionToSubflow": "Seleção para subfluxo",
|
||||||
"flows": "Fluxos",
|
"flows": "Fluxos",
|
||||||
"add": "Adicionar",
|
"add": "Adicionar",
|
||||||
"rename": "Renomear",
|
|
||||||
"delete": "Apagar",
|
"delete": "Apagar",
|
||||||
"keyboardShortcuts": "Atalhos do teclado",
|
"keyboardShortcuts": "Atalhos do teclado",
|
||||||
"login": "Ingressar",
|
"login": "Ingressar",
|
||||||
@ -1173,22 +1172,10 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "informações do Sistema"
|
"title": "informações do Sistema"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "Alemão",
|
|
||||||
"en-US": "Inglês",
|
|
||||||
"ja": "Japonês",
|
|
||||||
"ko": "Coreano",
|
|
||||||
"pt-BR": "Português(Brasil)",
|
|
||||||
"ru": "Russo",
|
|
||||||
"zh-CN": "Chinês(Simplificado)",
|
|
||||||
"zh-TW": "Chinês(Tradicional)"
|
|
||||||
},
|
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "Dados JSON inválidos: __error__",
|
"invalid-json": "Dados JSON inválidos: __error__",
|
||||||
"invalid-json-prop": "__prop__: dados JSON inválidos: __error__",
|
|
||||||
"invalid-prop": "Expressão de propriedade inválida",
|
"invalid-prop": "Expressão de propriedade inválida",
|
||||||
"invalid-prop-prop": "__prop__: expressão de propriedade inválida",
|
|
||||||
"invalid-num": "Número inválido",
|
"invalid-num": "Número inválido",
|
||||||
"invalid-num-prop": "__prop__: número inválido",
|
"invalid-num-prop": "__prop__: número inválido",
|
||||||
"invalid-regexp": "Padrão de entrada inválido",
|
"invalid-regexp": "Padrão de entrada inválido",
|
||||||
|
@ -95,7 +95,6 @@
|
|||||||
"selectionToSubflow": "Выделение в подпоток",
|
"selectionToSubflow": "Выделение в подпоток",
|
||||||
"flows": "Потоки",
|
"flows": "Потоки",
|
||||||
"add": "Добавить",
|
"add": "Добавить",
|
||||||
"rename": "Переименовать",
|
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
"keyboardShortcuts": "Сочетания клавиш",
|
"keyboardShortcuts": "Сочетания клавиш",
|
||||||
"login": "Войти",
|
"login": "Войти",
|
||||||
@ -1129,16 +1128,5 @@
|
|||||||
"appearance": "Внешний вид",
|
"appearance": "Внешний вид",
|
||||||
"preview": "Предпросмотр редактора",
|
"preview": "Предпросмотр редактора",
|
||||||
"defaultValue": "Значение по умолчанию"
|
"defaultValue": "Значение по умолчанию"
|
||||||
},
|
|
||||||
"languages" : {
|
|
||||||
"de": "Немецкий",
|
|
||||||
"en-US": "Английский",
|
|
||||||
"fr": "Французский",
|
|
||||||
"ja": "Японский",
|
|
||||||
"ko": "Корейский",
|
|
||||||
"pt-BR":"португальский",
|
|
||||||
"ru": "Русский",
|
|
||||||
"zh-CN": "Китайский (упрощенный)",
|
|
||||||
"zh-TW": "Китайский (традиционный)"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,11 @@
|
|||||||
"position": "位置",
|
"position": "位置",
|
||||||
"enable": "启用",
|
"enable": "启用",
|
||||||
"disable": "禁用",
|
"disable": "禁用",
|
||||||
"upload": "上传"
|
"upload": "上传",
|
||||||
|
"lock": "锁定",
|
||||||
|
"unlock": "解锁",
|
||||||
|
"locked": "锁定",
|
||||||
|
"unlocked": "解锁"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"string": "字符串",
|
"string": "字符串",
|
||||||
@ -68,7 +72,13 @@
|
|||||||
"enabled": "有效",
|
"enabled": "有效",
|
||||||
"disabled": "无效",
|
"disabled": "无效",
|
||||||
"info": "详细描述",
|
"info": "详细描述",
|
||||||
"selectNodes": "点击节点来选择"
|
"selectNodes": "点击节点来选择",
|
||||||
|
"enableFlow": "启用流程",
|
||||||
|
"disableFlow": "禁用流程",
|
||||||
|
"lockFlow": "锁定流程",
|
||||||
|
"unlockFlow": "解除锁定",
|
||||||
|
"moveToStart": "移动到起始",
|
||||||
|
"moveToEnd": "移动到末尾"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"label": {
|
"label": {
|
||||||
@ -101,6 +111,7 @@
|
|||||||
"displayStatus": "显示节点状态",
|
"displayStatus": "显示节点状态",
|
||||||
"displayConfig": "修改节点配置",
|
"displayConfig": "修改节点配置",
|
||||||
"import": "导入",
|
"import": "导入",
|
||||||
|
"importExample": "导入示例流程",
|
||||||
"export": "导出",
|
"export": "导出",
|
||||||
"search": "查找流程",
|
"search": "查找流程",
|
||||||
"searchInput": "查找流程",
|
"searchInput": "查找流程",
|
||||||
@ -109,7 +120,6 @@
|
|||||||
"selectionToSubflow": "将选择部分更改为子流程",
|
"selectionToSubflow": "将选择部分更改为子流程",
|
||||||
"flows": "流程",
|
"flows": "流程",
|
||||||
"add": "增加",
|
"add": "增加",
|
||||||
"rename": "重命名",
|
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"keyboardShortcuts": "键盘快捷方式",
|
"keyboardShortcuts": "键盘快捷方式",
|
||||||
"login": "登录",
|
"login": "登录",
|
||||||
@ -142,7 +152,12 @@
|
|||||||
"moveToBack": "置于底层",
|
"moveToBack": "置于底层",
|
||||||
"moveToFront": "置于顶层",
|
"moveToFront": "置于顶层",
|
||||||
"moveBackwards": "向后移动",
|
"moveBackwards": "向后移动",
|
||||||
"moveForwards": "向前移动"
|
"moveForwards": "向前移动",
|
||||||
|
"showNodeHelp":"显示节点帮助",
|
||||||
|
"enableSelectedNodes":"启用当前选中节点",
|
||||||
|
"disableSelectedNodes":"禁用当前选中节点",
|
||||||
|
"showSelectedNodeLabels":"显示选中的节点标签",
|
||||||
|
"hideSelectedNodeLabels":"隐藏选中的节点标签"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -403,6 +418,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
|
"noNodesSelected": "<strong>无法创建子流程</strong>: 未选择节点",
|
||||||
|
"acrossMultipleGroups": "无法跨多个组创建子流",
|
||||||
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
|
"multipleInputsToSelection": "<strong>无法创建子流程</strong>: 多个输入到了选择"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -491,12 +507,14 @@
|
|||||||
"unassigned": "未分配",
|
"unassigned": "未分配",
|
||||||
"global": "全局",
|
"global": "全局",
|
||||||
"workspace": "工作区",
|
"workspace": "工作区",
|
||||||
|
"editor": "编辑对话框",
|
||||||
"selectAll": "选择所有节点",
|
"selectAll": "选择所有节点",
|
||||||
"selectNone": "取消所有选择",
|
"selectNone": "取消所有选择",
|
||||||
"selectAllConnected": "选择所有连接的节点",
|
"selectAllConnected": "选择所有连接的节点",
|
||||||
"addRemoveNode": "从选择中添加/删除节点",
|
"addRemoveNode": "从选择中添加/删除节点",
|
||||||
"editSelected": "编辑选定节点",
|
"editSelected": "编辑选定节点",
|
||||||
"deleteSelected": "删除选定节点或链接",
|
"deleteSelected": "删除选定节点或链接",
|
||||||
|
"deleteReconnect": "删除并重新连接",
|
||||||
"importNode": "导入节点",
|
"importNode": "导入节点",
|
||||||
"exportNode": "导出节点",
|
"exportNode": "导出节点",
|
||||||
"nudgeNode": "移动所选节点(1px)",
|
"nudgeNode": "移动所选节点(1px)",
|
||||||
@ -571,6 +589,7 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"title": "面板管理",
|
"title": "面板管理",
|
||||||
"palette": "控制板",
|
"palette": "控制板",
|
||||||
|
"allCatalogs": "所有目录",
|
||||||
"times": {
|
"times": {
|
||||||
"seconds": "秒前",
|
"seconds": "秒前",
|
||||||
"minutes": "分前",
|
"minutes": "分前",
|
||||||
@ -610,6 +629,7 @@
|
|||||||
"tab-nodes": "节点",
|
"tab-nodes": "节点",
|
||||||
"tab-install": "安装",
|
"tab-install": "安装",
|
||||||
"sort": "排序:",
|
"sort": "排序:",
|
||||||
|
"sortRelevance": "关联",
|
||||||
"sortAZ": "a-z顺序",
|
"sortAZ": "a-z顺序",
|
||||||
"sortRecent": "日期顺序",
|
"sortRecent": "日期顺序",
|
||||||
"more": "增加 __count__ 个",
|
"more": "增加 __count__ 个",
|
||||||
@ -683,7 +703,11 @@
|
|||||||
"empty": "空的",
|
"empty": "空的",
|
||||||
"globalConfig": "全局配置节点",
|
"globalConfig": "全局配置节点",
|
||||||
"triggerAction": "触发动作",
|
"triggerAction": "触发动作",
|
||||||
"find": "在工作区中查找"
|
"find": "在工作区中查找",
|
||||||
|
"copyItemUrl": "复制地址",
|
||||||
|
"copyURL2Clipboard": "复制地址到剪贴板",
|
||||||
|
"showFlow": "显示流程",
|
||||||
|
"hideFlow": "隐藏流程"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"name": "帮助",
|
"name": "帮助",
|
||||||
@ -984,7 +1008,10 @@
|
|||||||
"quote": "引用",
|
"quote": "引用",
|
||||||
"link": "链接",
|
"link": "链接",
|
||||||
"horizontal-rule": "水平线",
|
"horizontal-rule": "水平线",
|
||||||
"toggle-preview": "切换预览"
|
"toggle-preview": "切换预览",
|
||||||
|
"mermaid": {
|
||||||
|
"summary": "美人鱼图"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"bufferEditor": {
|
"bufferEditor": {
|
||||||
"title": "Buffer 编辑器",
|
"title": "Buffer 编辑器",
|
||||||
@ -1147,17 +1174,6 @@
|
|||||||
"create": "创建分支",
|
"create": "创建分支",
|
||||||
"current": "当前的"
|
"current": "当前的"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "德语",
|
|
||||||
"en-US": "英文",
|
|
||||||
"fr": "法语",
|
|
||||||
"ja": "日语",
|
|
||||||
"ko": "韩文",
|
|
||||||
"pt-BR":"葡萄牙语",
|
|
||||||
"ru":"俄語",
|
|
||||||
"zh-CN": "简体中文",
|
|
||||||
"zh-TW": "繁体中文"
|
|
||||||
},
|
|
||||||
"create-default-file-set": {
|
"create-default-file-set": {
|
||||||
"no-active": "没有活动项目就无法创建默认文件集",
|
"no-active": "没有活动项目就无法创建默认文件集",
|
||||||
"no-empty": "无法在非空项目上创建默认文件集",
|
"no-empty": "无法在非空项目上创建默认文件集",
|
||||||
@ -1187,21 +1203,11 @@
|
|||||||
"diagnostics": {
|
"diagnostics": {
|
||||||
"title": "系统信息"
|
"title": "系统信息"
|
||||||
},
|
},
|
||||||
"languages": {
|
|
||||||
"de": "德语-Deutsch",
|
|
||||||
"en-US": "英文-English",
|
|
||||||
"ja": "日语-日本",
|
|
||||||
"ko": "韩文-한국인",
|
|
||||||
"ru": "俄语-Русский",
|
|
||||||
"zh-CN": "简体中文",
|
|
||||||
"zh-TW": "繁體中文"
|
|
||||||
},
|
|
||||||
"validator": {
|
"validator": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"invalid-json": "无效的 JSON 数据: __error__",
|
"invalid-json": "无效的 JSON 数据: __error__",
|
||||||
"invalid-json-prop": "__prop__: 无效的 JSON 数据: __error__",
|
"invalid-expr": "无效的 JSONata 表达式: __error__",
|
||||||
"invalid-prop": "无效的属性表达式",
|
"invalid-prop": "无效的属性表达式",
|
||||||
"invalid-prop-prop": "__prop__: 无效的属性表达式",
|
|
||||||
"invalid-num": "无效的数字",
|
"invalid-num": "无效的数字",
|
||||||
"invalid-num-prop": "__prop__: 无效的数字",
|
"invalid-num-prop": "__prop__: 无效的数字",
|
||||||
"invalid-regexp": "输入格式无效",
|
"invalid-regexp": "输入格式无效",
|
||||||
@ -1213,9 +1219,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"contextMenu": {
|
"contextMenu": {
|
||||||
|
"showActionList":"显示动作列表",
|
||||||
"insert": "插入",
|
"insert": "插入",
|
||||||
"node": "节点",
|
"node": "节点",
|
||||||
"junction": "连接点",
|
"junction": "连接点",
|
||||||
"linkNodes": "链接节点"
|
"linkNodes": "链接节点"
|
||||||
|
},
|
||||||
|
"env-var": {
|
||||||
|
"environment": "环境配置",
|
||||||
|
"header": "全局环境变量",
|
||||||
|
"revert": "重置"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,5 +270,9 @@
|
|||||||
"$moment": {
|
"$moment": {
|
||||||
"args": "[str]",
|
"args": "[str]",
|
||||||
"desc": "使用Moment库获取日期对象。"
|
"desc": "使用Moment库获取日期对象。"
|
||||||
|
},
|
||||||
|
"$clone": {
|
||||||
|
"args": "value",
|
||||||
|
"desc": "安全克隆对象."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,11 @@
|
|||||||
"position": "位置",
|
"position": "位置",
|
||||||
"enable": "啟用",
|
"enable": "啟用",
|
||||||
"disable": "禁用",
|
"disable": "禁用",
|
||||||
"upload": "上傳"
|
"upload": "上傳",
|
||||||
|
"lock": "鎖定",
|
||||||
|
"unlock": "解鎖",
|
||||||
|
"locked": "鎖定",
|
||||||
|
"unlocked": "解鎖"
|
||||||
},
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"string": "字符串",
|
"string": "字符串",
|
||||||
@ -38,11 +42,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"event": {
|
"event": {
|
||||||
|
"loadPlugins": "加載插件",
|
||||||
"loadPalette": "加載控制板",
|
"loadPalette": "加載控制板",
|
||||||
"loadNodeCatalogs": "加載節點目錄",
|
"loadNodeCatalogs": "加載節點目錄",
|
||||||
"loadNodes": "加載 __count__ 個節點",
|
"loadNodes": "加載 __count__ 個節點",
|
||||||
"loadFlows": "加載流程",
|
"loadFlows": "加載流程",
|
||||||
"importFlows": "往工作區中加載流程"
|
"importFlows": "往工作區中加載流程",
|
||||||
|
"importError": "<p>加載流程錯誤</p><p>__message__</p>",
|
||||||
|
"loadingProject": "加載項目"
|
||||||
},
|
},
|
||||||
"workspace": {
|
"workspace": {
|
||||||
"defaultName": "流程__number__",
|
"defaultName": "流程__number__",
|
||||||
@ -51,18 +58,35 @@
|
|||||||
"delete": "確定想要刪除 '__label__'?",
|
"delete": "確定想要刪除 '__label__'?",
|
||||||
"dropFlowHere": "把流程放到這裡",
|
"dropFlowHere": "把流程放到這裡",
|
||||||
"addFlow": "新增流程",
|
"addFlow": "新增流程",
|
||||||
"listFlows": "流程列表",
|
"addFlowToRight": "在右側新增流程",
|
||||||
|
"hideFlow": "隱藏流程",
|
||||||
|
"hideOtherFlows": "隱藏其它流程",
|
||||||
|
"showAllFlows": "顯示所有流程",
|
||||||
|
"hideAllFlows": "隱藏所有流程",
|
||||||
|
"hiddenFlows": "列出 __count__ 個隱藏流程",
|
||||||
|
"hiddenFlows_plural": "列出 __count__ 個隱藏流程",
|
||||||
|
"showLastHiddenFlow": "顯示最後一個隱藏流程",
|
||||||
|
" ": "流程列表",
|
||||||
|
"listSubflows": "列出子流程",
|
||||||
"status": "狀態",
|
"status": "狀態",
|
||||||
"enabled": "有效",
|
"enabled": "有效",
|
||||||
"disabled": "無效",
|
"disabled": "無效",
|
||||||
"info": "詳細描述",
|
"info": "詳細描述",
|
||||||
"selectNodes": "點擊節點用於選擇"
|
"selectNodes": "點擊節點用於選擇",
|
||||||
|
"enableFlow": "啟用流程",
|
||||||
|
"disableFlow": "禁用流程",
|
||||||
|
"lockFlow": "鎖定流程",
|
||||||
|
"unlockFlow": "解除鎖定",
|
||||||
|
"moveToStart": "移動到起始",
|
||||||
|
"moveToEnd": "移動到末尾"
|
||||||
},
|
},
|
||||||
"menu": {
|
"menu": {
|
||||||
"label": {
|
"label": {
|
||||||
"view": {
|
"view": {
|
||||||
"view": "顯示",
|
"view": "顯示",
|
||||||
"grid": "格線",
|
"grid": "格線",
|
||||||
|
"storeZoom": "加載時還原縮放尺寸",
|
||||||
|
"storePosition": "加載時還原滾動位置",
|
||||||
"showGrid": "顯示格線",
|
"showGrid": "顯示格線",
|
||||||
"snapGrid": "對齊格線",
|
"snapGrid": "對齊格線",
|
||||||
"gridSize": "格線尺寸",
|
"gridSize": "格線尺寸",
|
||||||
@ -80,12 +104,14 @@
|
|||||||
"palette": {
|
"palette": {
|
||||||
"show": "顯示控制板"
|
"show": "顯示控制板"
|
||||||
},
|
},
|
||||||
|
"edit": "編輯",
|
||||||
"settings": "設置",
|
"settings": "設置",
|
||||||
"userSettings": "使用者設置",
|
"userSettings": "使用者設置",
|
||||||
"nodes": "節點",
|
"nodes": "節點",
|
||||||
"displayStatus": "顯示節點狀態",
|
"displayStatus": "顯示節點狀態",
|
||||||
"displayConfig": "修改節點配置",
|
"displayConfig": "修改節點配置",
|
||||||
"import": "匯入",
|
"import": "匯入",
|
||||||
|
"importExample": "導入示例流程",
|
||||||
"export": "匯出",
|
"export": "匯出",
|
||||||
"search": "搜尋流程",
|
"search": "搜尋流程",
|
||||||
"searchInput": "搜尋流程",
|
"searchInput": "搜尋流程",
|
||||||
@ -94,7 +120,6 @@
|
|||||||
"selectionToSubflow": "將選擇部分更改為子流程",
|
"selectionToSubflow": "將選擇部分更改為子流程",
|
||||||
"flows": "流程",
|
"flows": "流程",
|
||||||
"add": "增加",
|
"add": "增加",
|
||||||
"rename": "重新命名",
|
|
||||||
"delete": "刪除",
|
"delete": "刪除",
|
||||||
"keyboardShortcuts": "鍵盤快速鍵",
|
"keyboardShortcuts": "鍵盤快速鍵",
|
||||||
"login": "登入",
|
"login": "登入",
|
||||||
@ -102,24 +127,48 @@
|
|||||||
"editPalette": "節點管理",
|
"editPalette": "節點管理",
|
||||||
"other": "其他",
|
"other": "其他",
|
||||||
"showTips": "顯示小提示",
|
"showTips": "顯示小提示",
|
||||||
"help": "Node-RED website",
|
"showWelcomeTours": "顯示新版本向導",
|
||||||
|
"help": "Node-RED 文檔主頁",
|
||||||
"projects": "專案",
|
"projects": "專案",
|
||||||
"projects-new": "新專案",
|
"projects-new": "新專案",
|
||||||
"projects-open": "開啟專案",
|
"projects-open": "開啟專案",
|
||||||
"projects-settings": "專案設定",
|
"projects-settings": "專案設定",
|
||||||
"showNodeLabelDefault": "顯示新添加節點的標籤",
|
"showNodeLabelDefault": "顯示新添加節點的標籤",
|
||||||
|
"codeEditor": "代碼編輯器",
|
||||||
"groups": "組",
|
"groups": "組",
|
||||||
"groupSelection": "選擇組",
|
"groupSelection": "選擇組",
|
||||||
"ungroupSelection": "取消選擇組",
|
"ungroupSelection": "取消選擇組",
|
||||||
"groupMergeSelection": "合并選擇",
|
"groupMergeSelection": "合并選擇",
|
||||||
"groupRemoveSelection": "從組中移除"
|
"groupRemoveSelection": "從組中移除",
|
||||||
|
"arrange": "布局",
|
||||||
|
"alignLeft": "左對齊",
|
||||||
|
"alignCenter": "居中對齊",
|
||||||
|
"alignRight": "右對齊",
|
||||||
|
"alignTop": "頂部對齊",
|
||||||
|
"alignMiddle": "垂直居中對齊",
|
||||||
|
"alignBottom": "底部對齊",
|
||||||
|
"distributeHorizontally": "横向分布",
|
||||||
|
"distributeVertically": "垂直分布",
|
||||||
|
"moveToBack": "置於底層",
|
||||||
|
"moveToFront": "置於頂層",
|
||||||
|
"moveBackwards": "向後移動",
|
||||||
|
"moveForwards": "向前移動",
|
||||||
|
"showNodeHelp":"顯示節點幫助",
|
||||||
|
"enableSelectedNodes":"啟用當前選中節點",
|
||||||
|
"disableSelectedNodes":"禁用當前選中節點",
|
||||||
|
"showSelectedNodeLabels":"顯示選中的節點標簽",
|
||||||
|
"hideSelectedNodeLabels":"隱藏選中的節點標簽"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"toggle-navigator": "切換導航器",
|
"toggle-navigator": "切換導航器",
|
||||||
"zoom-out": "縮小",
|
"zoom-out": "縮小",
|
||||||
"zoom-reset": "重置縮放",
|
"zoom-reset": "重置縮放",
|
||||||
"zoom-in": "放大"
|
"zoom-in": "放大",
|
||||||
|
"search-flows": "搜索流程",
|
||||||
|
"search-prev": "上一個",
|
||||||
|
"search-next": "下一個",
|
||||||
|
"search-counter": "\"__term__\" __result__ of __count__"
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"loggedInAs": "作為 __name__ 登入",
|
"loggedInAs": "作為 __name__ 登入",
|
||||||
@ -135,12 +184,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
|
"state": {
|
||||||
|
"flowsStopped": "流程已停止",
|
||||||
|
"flowsStarted": "流程已啟動"
|
||||||
|
},
|
||||||
"warning": "<strong>警告</strong>: __message__",
|
"warning": "<strong>警告</strong>: __message__",
|
||||||
"warnings": {
|
"warnings": {
|
||||||
"undeployedChanges": "節點中存在未部署的更改",
|
"undeployedChanges": "節點中存在未部署的更改",
|
||||||
"nodeActionDisabled": "節點動作在子流程中被禁用",
|
"nodeActionDisabled": "節點動作在子流程中被禁用",
|
||||||
"nodeActionDisabledSubflow": "子流程中禁用了節點操作",
|
"nodeActionDisabledSubflow": "子流程中禁用了節點操作",
|
||||||
"missing-types": "流程由於缺少節點類型而停止。請檢查日誌的詳細資訊",
|
"missing-types": "流程由於缺少節點類型而停止。請檢查日誌的詳細資訊",
|
||||||
|
"missing-modules": "<p>流程因缺少模塊而停止。</p>",
|
||||||
"safe-mode": "<p>流程在安全模式下停止。</p><p>您可以修改流程並部署更改以重新啟動。</p>",
|
"safe-mode": "<p>流程在安全模式下停止。</p><p>您可以修改流程並部署更改以重新啟動。</p>",
|
||||||
"restartRequired": "Node-RED必須重新啟動,以啟用升級的模組",
|
"restartRequired": "Node-RED必須重新啟動,以啟用升級的模組",
|
||||||
"credentials_load_failed": "<p>流程由於無法解密證書而停止。</p> <p>流程證書文件已加密,但是項目的加密密鑰丟失或無效。</p>",
|
"credentials_load_failed": "<p>流程由於無法解密證書而停止。</p> <p>流程證書文件已加密,但是項目的加密密鑰丟失或無效。</p>",
|
||||||
@ -151,7 +205,7 @@
|
|||||||
"project_not_found": "<p>找不到項目的'__project__'</p>",
|
"project_not_found": "<p>找不到項目的'__project__'</p>",
|
||||||
"git_merge_conflict": "<p>自動合併更改失敗。</p><p>修復未合併的衝突,然後提交結果。</p>"
|
"git_merge_conflict": "<p>自動合併更改失敗。</p><p>修復未合併的衝突,然後提交結果。</p>"
|
||||||
},
|
},
|
||||||
"error": "<strong>Error</strong>: __message__",
|
"error": "<strong>錯誤</strong>: __message__",
|
||||||
"errors": {
|
"errors": {
|
||||||
"lostConnection": "丟失與伺服器的連接,重新連接...",
|
"lostConnection": "丟失與伺服器的連接,重新連接...",
|
||||||
"lostConnectionReconnect": "丟失與伺服器的連接,__time__ 秒後重新連接",
|
"lostConnectionReconnect": "丟失與伺服器的連接,__time__ 秒後重新連接",
|
||||||
@ -208,6 +262,8 @@
|
|||||||
"download": "下載",
|
"download": "下載",
|
||||||
"importUnrecognised": "匯入了無法識別的類型:",
|
"importUnrecognised": "匯入了無法識別的類型:",
|
||||||
"importUnrecognised_plural": "匯入了無法識別的類型:",
|
"importUnrecognised_plural": "匯入了無法識別的類型:",
|
||||||
|
"importDuplicate": "導入了重復節點:",
|
||||||
|
"importDuplicate_plural": "導入了重復節點:",
|
||||||
"nodesExported": "節點匯出到了剪貼簿",
|
"nodesExported": "節點匯出到了剪貼簿",
|
||||||
"nodesImported": "已匯入:",
|
"nodesImported": "已匯入:",
|
||||||
"nodeCopied": "已複製 __count__ 個節點",
|
"nodeCopied": "已複製 __count__ 個節點",
|
||||||
@ -259,6 +315,10 @@
|
|||||||
"modifiedFlowsDesc": "只部署包含已更改節點的流程",
|
"modifiedFlowsDesc": "只部署包含已更改節點的流程",
|
||||||
"modifiedNodes": "已更改的節點",
|
"modifiedNodes": "已更改的節點",
|
||||||
"modifiedNodesDesc": "只部署已經更改的節點",
|
"modifiedNodesDesc": "只部署已經更改的節點",
|
||||||
|
"startFlows": "啟動",
|
||||||
|
"startFlowsDesc": "啟動流程",
|
||||||
|
"stopFlows": "停止",
|
||||||
|
"stopFlowsDesc": "停止流程",
|
||||||
"restartFlows": "重新啟動流程",
|
"restartFlows": "重新啟動流程",
|
||||||
"restartFlowsDesc": "重新啟動當前部署的流程",
|
"restartFlowsDesc": "重新啟動當前部署的流程",
|
||||||
"successfulDeploy": "部署成功",
|
"successfulDeploy": "部署成功",
|
||||||
@ -337,14 +397,28 @@
|
|||||||
"output": "輸出:",
|
"output": "輸出:",
|
||||||
"status": "狀態節點",
|
"status": "狀態節點",
|
||||||
"deleteSubflow": "刪除子流程",
|
"deleteSubflow": "刪除子流程",
|
||||||
|
"confirmDelete": "您確定要刪除此子流程?",
|
||||||
"info": "詳細描述",
|
"info": "詳細描述",
|
||||||
"category": "類別",
|
"category": "類別",
|
||||||
|
"module": "模塊",
|
||||||
|
"license": "許可",
|
||||||
|
"licenseNone": "無",
|
||||||
|
"licenseOther": "其它",
|
||||||
|
"type": "節點類型",
|
||||||
|
"version": "版本",
|
||||||
|
"versionPlaceholder": "x.y.z",
|
||||||
|
"keys": "關鍵字",
|
||||||
|
"keysPlaceholder": "使用英文逗號分隔關鍵字",
|
||||||
|
"author": "作者",
|
||||||
|
"authorPlaceholder": "名字 <email@example.com>",
|
||||||
|
"desc": "描述",
|
||||||
"env": {
|
"env": {
|
||||||
"restore": "恢復為默認子流程",
|
"restore": "恢復為默認子流程",
|
||||||
"remove": "類別刪除環境變量"
|
"remove": "類別刪除環境變量"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noNodesSelected": "<strong>無法創建子流程</strong>: 未選擇節點",
|
"noNodesSelected": "<strong>無法創建子流程</strong>: 未選擇節點",
|
||||||
|
"acrossMultipleGroups": "無法跨多個組創建子流",
|
||||||
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
|
"multipleInputsToSelection": "<strong>無法創建子流程</strong>: 多個輸入到了選擇"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -367,12 +441,12 @@
|
|||||||
"editConfig": "編輯 __type__ 配置",
|
"editConfig": "編輯 __type__ 配置",
|
||||||
"addNewType": "添加新的 __type__ 節點",
|
"addNewType": "添加新的 __type__ 節點",
|
||||||
"nodeProperties": "節點屬性",
|
"nodeProperties": "節點屬性",
|
||||||
"label": "Label",
|
"label": "標簽",
|
||||||
"color": "顏色",
|
"color": "顏色",
|
||||||
"portLabels": "埠標籤",
|
"portLabels": "埠標籤",
|
||||||
"labelInputs": "輸入",
|
"labelInputs": "輸入",
|
||||||
"labelOutputs": "輸出",
|
"labelOutputs": "輸出",
|
||||||
"settingIcon": "Icon",
|
"settingIcon": "圖標",
|
||||||
"default": "默認",
|
"default": "默認",
|
||||||
"noDefaultLabel": "無",
|
"noDefaultLabel": "無",
|
||||||
"defaultLabel": "使用默認標籤",
|
"defaultLabel": "使用默認標籤",
|
||||||
@ -385,6 +459,7 @@
|
|||||||
"icon": "圖標",
|
"icon": "圖標",
|
||||||
"inputType": "輸入類型",
|
"inputType": "輸入類型",
|
||||||
"selectType": "選擇類型...",
|
"selectType": "選擇類型...",
|
||||||
|
"loadCredentials": "加載節點憑證",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"input": "輸入",
|
"input": "輸入",
|
||||||
"select": "選擇",
|
"select": "選擇",
|
||||||
@ -419,7 +494,8 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"scopeChange": "更改範圍將使其他流程中的節點無法使用",
|
"scopeChange": "更改範圍將使其他流程中的節點無法使用",
|
||||||
"invalidProperties": "無效的屬性:"
|
"invalidProperties": "無效的屬性:",
|
||||||
|
"credentialLoadFailed": "無法加載節點憑據"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keyboard": {
|
"keyboard": {
|
||||||
@ -431,11 +507,14 @@
|
|||||||
"unassigned": "未分配",
|
"unassigned": "未分配",
|
||||||
"global": "全局",
|
"global": "全局",
|
||||||
"workspace": "工作區",
|
"workspace": "工作區",
|
||||||
|
"editor": "編輯對話框",
|
||||||
"selectAll": "選擇所有節點",
|
"selectAll": "選擇所有節點",
|
||||||
|
"selectNone": "取消所有選擇",
|
||||||
"selectAllConnected": "選擇所有連接的節點",
|
"selectAllConnected": "選擇所有連接的節點",
|
||||||
"addRemoveNode": "從選擇中添加/刪除節點",
|
"addRemoveNode": "從選擇中添加/刪除節點",
|
||||||
"editSelected": "編輯選定節點",
|
"editSelected": "編輯選定節點",
|
||||||
"deleteSelected": "刪除選定節點或連結",
|
"deleteSelected": "刪除選定節點或連結",
|
||||||
|
"deleteReconnect": "刪除並重新連接",
|
||||||
"importNode": "匯入節點",
|
"importNode": "匯入節點",
|
||||||
"exportNode": "匯出節點",
|
"exportNode": "匯出節點",
|
||||||
"nudgeNode": "移動所選節點(1px)",
|
"nudgeNode": "移動所選節點(1px)",
|
||||||
@ -445,10 +524,14 @@
|
|||||||
"copyNode": "複製所選節點",
|
"copyNode": "複製所選節點",
|
||||||
"cutNode": "剪切所選節點",
|
"cutNode": "剪切所選節點",
|
||||||
"pasteNode": "粘貼節點",
|
"pasteNode": "粘貼節點",
|
||||||
|
"copyGroupStyle": "復製組樣式",
|
||||||
|
"pasteGroupStyle": "粘貼組樣式",
|
||||||
"undoChange": "撤銷上次執行的更改",
|
"undoChange": "撤銷上次執行的更改",
|
||||||
|
"redoChange": "重做",
|
||||||
"searchBox": "打開搜尋框",
|
"searchBox": "打開搜尋框",
|
||||||
"managePalette": "管理面板",
|
"managePalette": "管理面板",
|
||||||
"actionList": "動作列表"
|
"actionList": "動作列表",
|
||||||
|
"splitWireWithLinks": "使用Link節點拆分已選項"
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"library": "庫",
|
"library": "庫",
|
||||||
@ -466,12 +549,11 @@
|
|||||||
"types": {
|
"types": {
|
||||||
"local": "本地",
|
"local": "本地",
|
||||||
"examples": "例子"
|
"examples": "例子"
|
||||||
},
|
}
|
||||||
"exportToLibrary": "將節點匯出到庫"
|
|
||||||
},
|
},
|
||||||
"palette": {
|
"palette": {
|
||||||
"noInfo": "無可用資訊",
|
"noInfo": "無可用資訊",
|
||||||
"filter": "過濾節點",
|
"filter": "過濾已安裝模組",
|
||||||
"search": "搜尋模組",
|
"search": "搜尋模組",
|
||||||
"addCategory": "添加新的...",
|
"addCategory": "添加新的...",
|
||||||
"label": {
|
"label": {
|
||||||
@ -501,11 +583,13 @@
|
|||||||
"nodeEnabled_plural": "啟用多個節點:",
|
"nodeEnabled_plural": "啟用多個節點:",
|
||||||
"nodeDisabled": "禁用節點:",
|
"nodeDisabled": "禁用節點:",
|
||||||
"nodeDisabled_plural": "禁用多個節點:",
|
"nodeDisabled_plural": "禁用多個節點:",
|
||||||
"nodeUpgraded": "節點模組__module__升級到__version__版本"
|
"nodeUpgraded": "節點模組__module__升級到__version__版本",
|
||||||
|
"unknownNodeRegistered": "加載節點錯誤: <ul><li>__type__<br>__error__</li></ul>"
|
||||||
},
|
},
|
||||||
"editor": {
|
"editor": {
|
||||||
"title": "面板管理",
|
"title": "面板管理",
|
||||||
"palette": "Palette",
|
"palette": "控製板",
|
||||||
|
"allCatalogs": "所有目錄",
|
||||||
"times": {
|
"times": {
|
||||||
"seconds": "秒前",
|
"seconds": "秒前",
|
||||||
"minutes": "分前",
|
"minutes": "分前",
|
||||||
@ -545,10 +629,12 @@
|
|||||||
"tab-nodes": "節點",
|
"tab-nodes": "節點",
|
||||||
"tab-install": "安裝",
|
"tab-install": "安裝",
|
||||||
"sort": "排序:",
|
"sort": "排序:",
|
||||||
|
"sortRelevance": "關聯",
|
||||||
"sortAZ": "a-z順序",
|
"sortAZ": "a-z順序",
|
||||||
"sortRecent": "日期順序",
|
"sortRecent": "日期順序",
|
||||||
"more": "增加 __count__ 個",
|
"more": "增加 __count__ 個",
|
||||||
"upload": "上傳模塊tgz文件",
|
"upload": "上傳模塊tgz文件",
|
||||||
|
"refresh": "更新模塊列表",
|
||||||
"errors": {
|
"errors": {
|
||||||
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
|
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
|
||||||
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
|
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
|
||||||
@ -617,7 +703,11 @@
|
|||||||
"empty": "空的",
|
"empty": "空的",
|
||||||
"globalConfig": "全局配置節點",
|
"globalConfig": "全局配置節點",
|
||||||
"triggerAction": "觸發動作",
|
"triggerAction": "觸發動作",
|
||||||
"find": "在工作區中查找"
|
"find": "在工作區中查找",
|
||||||
|
"copyItemUrl": "復製地址",
|
||||||
|
"copyURL2Clipboard": "復製地址到剪貼板",
|
||||||
|
"showFlow": "顯示流程",
|
||||||
|
"hideFlow": "隱藏流程"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"name": "幫助",
|
"name": "幫助",
|
||||||
@ -627,7 +717,8 @@
|
|||||||
"showHelp": "顯示幫助",
|
"showHelp": "顯示幫助",
|
||||||
"showInOutline": "在大綱中顯示",
|
"showInOutline": "在大綱中顯示",
|
||||||
"showTopics": "顯示主題",
|
"showTopics": "顯示主題",
|
||||||
"noHelp": "未選擇幫助主題"
|
"noHelp": "未選擇幫助主題",
|
||||||
|
"changeLog": "更新日誌"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"name": "配置節點",
|
"name": "配置節點",
|
||||||
@ -828,31 +919,37 @@
|
|||||||
"json": "JSON",
|
"json": "JSON",
|
||||||
"bin": "二進位流",
|
"bin": "二進位流",
|
||||||
"date": "時間戳記",
|
"date": "時間戳記",
|
||||||
"jsonata": "expression",
|
"jsonata": "表達式",
|
||||||
"env": "env variable",
|
"env": "環境變量",
|
||||||
"cred": "證書"
|
"cred": "證書"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"editableList": {
|
"editableList": {
|
||||||
"add": "添加"
|
"add": "添加",
|
||||||
|
"addTitle": "添加項"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"empty": "找不到匹配",
|
"history": "搜索歷史",
|
||||||
|
"clear": "清除所有",
|
||||||
|
"empty": "找不到匹配項",
|
||||||
"addNode": "添加一個節點...",
|
"addNode": "添加一個節點...",
|
||||||
"options": {
|
"options": {
|
||||||
"configNodes": "配置節點",
|
"configNodes": "配置節點",
|
||||||
"unusedConfigNodes": "未使用的配置節點",
|
"unusedConfigNodes": "未使用的配置節點",
|
||||||
"invalidNodes": "無效的節點",
|
"invalidNodes": "無效的節點",
|
||||||
"uknownNodes": "未知的節點",
|
"uknownNodes": "未知的節點",
|
||||||
"unusedSubflows": "未使用的子流程"
|
"unusedSubflows": "未使用的子流程",
|
||||||
|
"hiddenFlows": "隱藏的流程",
|
||||||
|
"modifiedNodes": "已修改的節點或流程",
|
||||||
|
"thisFlow": "當前流程"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expressionEditor": {
|
"expressionEditor": {
|
||||||
"functions": "功能",
|
"functions": "功能",
|
||||||
"functionReference": "Function reference",
|
"functionReference": "功能參考",
|
||||||
"insert": "插入",
|
"insert": "插入",
|
||||||
"title": "JSONata運算式編輯器",
|
"title": "JSONata運算式編輯器",
|
||||||
"test": "Test",
|
"test": "測試",
|
||||||
"data": "示例消息",
|
"data": "示例消息",
|
||||||
"result": "結果",
|
"result": "結果",
|
||||||
"format": "格式表達方法",
|
"format": "格式表達方法",
|
||||||
@ -863,20 +960,28 @@
|
|||||||
"invalid-expr": "無效的JSONata運算式:\n __message__",
|
"invalid-expr": "無效的JSONata運算式:\n __message__",
|
||||||
"invalid-msg": "無效的示例JSON消息:\n __message__",
|
"invalid-msg": "無效的示例JSON消息:\n __message__",
|
||||||
"context-unsupported": "無法測試上下文函數\n $flowContext 或 $globalContext",
|
"context-unsupported": "無法測試上下文函數\n $flowContext 或 $globalContext",
|
||||||
|
"env-unsupported": "無法測試 $env 函數",
|
||||||
|
"moment-unsupported": "無法測試 $moment 函數",
|
||||||
|
"clone-unsupported": "無法測試 $clone 函數",
|
||||||
"eval": "評估運算式錯誤:\n __message__"
|
"eval": "評估運算式錯誤:\n __message__"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"monaco": {
|
||||||
|
"setTheme": "設置主題"
|
||||||
|
},
|
||||||
"jsEditor": {
|
"jsEditor": {
|
||||||
"title": "JavaScript 編輯器"
|
"title": "JavaScript 編輯器"
|
||||||
},
|
},
|
||||||
"textEditor": {
|
"textEditor": {
|
||||||
"title": "Text 編輯器"
|
"title": "文本編輯器"
|
||||||
},
|
},
|
||||||
"jsonEditor": {
|
"jsonEditor": {
|
||||||
"title": "JSON編輯器",
|
"title": "JSON編輯器",
|
||||||
"format": "格式化JSON",
|
"format": "格式化JSON",
|
||||||
"rawMode": "編輯 JSON",
|
"rawMode": "編輯 JSON",
|
||||||
"uiMode": "Visual編輯器",
|
"uiMode": "可視化編輯器",
|
||||||
|
"rawMode-readonly": "原始JSON",
|
||||||
|
"uiMode-readonly": "可視化",
|
||||||
"insertAbove": "在上方插入",
|
"insertAbove": "在上方插入",
|
||||||
"insertBelow": "在下方插入",
|
"insertBelow": "在下方插入",
|
||||||
"addItem": "添加項目",
|
"addItem": "添加項目",
|
||||||
@ -892,9 +997,9 @@
|
|||||||
"title": "Markdown 編輯器",
|
"title": "Markdown 編輯器",
|
||||||
"expand": "展開",
|
"expand": "展開",
|
||||||
"format": "F使用markdown格式化",
|
"format": "F使用markdown格式化",
|
||||||
"heading1": "Heading 1",
|
"heading1": "標題 1",
|
||||||
"heading2": "Heading 2",
|
"heading2": "標題 2",
|
||||||
"heading3": "Heading 3",
|
"heading3": "標題 3",
|
||||||
"bold": "粗體",
|
"bold": "粗體",
|
||||||
"italic": "斜體",
|
"italic": "斜體",
|
||||||
"code": "程式碼",
|
"code": "程式碼",
|
||||||
@ -903,7 +1008,10 @@
|
|||||||
"quote": "引用",
|
"quote": "引用",
|
||||||
"link": "連結",
|
"link": "連結",
|
||||||
"horizontal-rule": "分隔線",
|
"horizontal-rule": "分隔線",
|
||||||
"toggle-preview": "預覽"
|
"toggle-preview": "切換預覽",
|
||||||
|
"mermaid": {
|
||||||
|
"summary": "美人魚圖"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"bufferEditor": {
|
"bufferEditor": {
|
||||||
"title": "緩衝區編輯器",
|
"title": "緩衝區編輯器",
|
||||||
@ -1038,7 +1146,8 @@
|
|||||||
"not-git": "不是git倉庫",
|
"not-git": "不是git倉庫",
|
||||||
"no-resource": "找不到存儲庫",
|
"no-resource": "找不到存儲庫",
|
||||||
"cant-get-ssh-key-path": "錯誤! 無法獲取所選的SSH密鑰路徑。",
|
"cant-get-ssh-key-path": "錯誤! 無法獲取所選的SSH密鑰路徑。",
|
||||||
"unexpected_error": "意外的錯誤"
|
"unexpected_error": "意外的錯誤",
|
||||||
|
"clearContext": "更改項目時清除上下文"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"confirm": "您確定要刪除此項目嗎?"
|
"confirm": "您確定要刪除此項目嗎?"
|
||||||
@ -1068,7 +1177,7 @@
|
|||||||
"create-default-file-set": {
|
"create-default-file-set": {
|
||||||
"no-active": "沒有活動項目就無法創建默認文件集",
|
"no-active": "沒有活動項目就無法創建默認文件集",
|
||||||
"no-empty": "無法在非空項目上創建默認文件集",
|
"no-empty": "無法在非空項目上創建默認文件集",
|
||||||
"git-error": "git error"
|
"git-error": "git錯誤"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"no-username-email": "您的Git客戶端未配置用戶名/電子郵件。",
|
"no-username-email": "您的Git客戶端未配置用戶名/電子郵件。",
|
||||||
@ -1079,21 +1188,45 @@
|
|||||||
"editor-tab": {
|
"editor-tab": {
|
||||||
"properties": "屬性",
|
"properties": "屬性",
|
||||||
"envProperties": "環境變量",
|
"envProperties": "環境變量",
|
||||||
|
"module": "模塊屬性",
|
||||||
"description": "描述",
|
"description": "描述",
|
||||||
"appearance": "外觀",
|
"appearance": "外觀",
|
||||||
"preview": "UI預覽",
|
"preview": "UI預覽",
|
||||||
"defaultValue": "默認值",
|
"defaultValue": "默認值"
|
||||||
"env": "環境變量"
|
|
||||||
},
|
},
|
||||||
"languages": {
|
"tourGuide": {
|
||||||
"de": "德語",
|
"takeATour": "查看更新內容",
|
||||||
"en-US": "英語",
|
"start": "開始",
|
||||||
"fr": "法語",
|
"next": "下一個",
|
||||||
"ja": "日語",
|
"welcomeTours": "歡迎使用 Node-RED"
|
||||||
"ko": "韓語",
|
},
|
||||||
"pt-BR":"葡萄牙语",
|
"diagnostics": {
|
||||||
"ru":"俄語",
|
"title": "系统信息"
|
||||||
"zh-CN": "簡體中文",
|
},
|
||||||
"zh-TW": "繁體中文"
|
"validator": {
|
||||||
|
"errors": {
|
||||||
|
"invalid-json": "無效的 JSON 數據: __error__",
|
||||||
|
"invalid-expr": "無效的 JSONata 表達式: __error__",
|
||||||
|
"invalid-prop": "無效的屬性表達式",
|
||||||
|
"invalid-num": "無效的數字",
|
||||||
|
"invalid-regexp": "輸入格式無效",
|
||||||
|
"invalid-regex-prop": "__prop__: 輸入格式無效",
|
||||||
|
"missing-required-prop": "__prop__: 缺少屬性值",
|
||||||
|
"invalid-config": "__prop__: 無效的配置節點",
|
||||||
|
"missing-config": "__prop__: 缺少配置節點",
|
||||||
|
"validation-error": "__prop__: 驗證錯誤: __node__, __id__: __error__"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contextMenu": {
|
||||||
|
"showActionList":"顯示動作列表",
|
||||||
|
"insert": "插入",
|
||||||
|
"node": "節點",
|
||||||
|
"junction": "連接點",
|
||||||
|
"linkNodes": "鏈接節點"
|
||||||
|
},
|
||||||
|
"env-var": {
|
||||||
|
"environment": "環境配置",
|
||||||
|
"header": "全局環境變量",
|
||||||
|
"revert": "重置"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,5 +270,9 @@
|
|||||||
"$moment": {
|
"$moment": {
|
||||||
"args": "[str]",
|
"args": "[str]",
|
||||||
"desc": "使用Moment庫獲取日期對象。"
|
"desc": "使用Moment庫獲取日期對象。"
|
||||||
|
},
|
||||||
|
"$clone": {
|
||||||
|
"args": "value",
|
||||||
|
"desc": "安全克隆對象."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,15 +39,16 @@
|
|||||||
console.warn(evt,args);
|
console.warn(evt,args);
|
||||||
}
|
}
|
||||||
if (handlers[evt]) {
|
if (handlers[evt]) {
|
||||||
for (var i=0;i<handlers[evt].length;i++) {
|
let cpyHandlers = [...handlers[evt]];
|
||||||
|
|
||||||
|
for (var i=0;i<cpyHandlers.length;i++) {
|
||||||
try {
|
try {
|
||||||
handlers[evt][i].apply(null, args);
|
cpyHandlers[i].apply(null, args);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
||||||
console.warn(err);
|
console.warn(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -797,8 +797,8 @@ RED.nodes = (function() {
|
|||||||
|
|
||||||
if (node && node._def.onremove) {
|
if (node && node._def.onremove) {
|
||||||
// Deprecated: never documented but used by some early nodes
|
// Deprecated: never documented but used by some early nodes
|
||||||
console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditremove - please report");
|
console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditdelete - please report");
|
||||||
node._def.onremove.call(n);
|
node._def.onremove.call(node);
|
||||||
}
|
}
|
||||||
return {links:removedLinks,nodes:removedNodes};
|
return {links:removedLinks,nodes:removedNodes};
|
||||||
}
|
}
|
||||||
@ -1228,7 +1228,6 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (n.credentials) {
|
} else if (n.credentials) {
|
||||||
node.credentials = {};
|
|
||||||
// All other nodes have a well-defined list of possible credentials
|
// All other nodes have a well-defined list of possible credentials
|
||||||
for (var cred in n._def.credentials) {
|
for (var cred in n._def.credentials) {
|
||||||
if (n._def.credentials.hasOwnProperty(cred)) {
|
if (n._def.credentials.hasOwnProperty(cred)) {
|
||||||
@ -2198,6 +2197,12 @@ RED.nodes = (function() {
|
|||||||
}
|
}
|
||||||
node._config.x = node.x;
|
node._config.x = node.x;
|
||||||
node._config.y = node.y;
|
node._config.y = node.y;
|
||||||
|
if (n.hasOwnProperty('w')) {
|
||||||
|
node.w = n.w
|
||||||
|
}
|
||||||
|
if (n.hasOwnProperty('h')) {
|
||||||
|
node.h = n.h
|
||||||
|
}
|
||||||
} else if (n.type.substring(0,7) === "subflow") {
|
} else if (n.type.substring(0,7) === "subflow") {
|
||||||
var parentId = n.type.split(":")[1];
|
var parentId = n.type.split(":")[1];
|
||||||
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
||||||
@ -2211,7 +2216,7 @@ RED.nodes = (function() {
|
|||||||
set: registry.getNodeSet("node-red/unknown")
|
set: registry.getNodeSet("node-red/unknown")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
if (subflow_denylist[parentId] || createNewIds || options.importMap[n.id] === "copy") {
|
||||||
parentId = subflow.id;
|
parentId = subflow.id;
|
||||||
node.type = "subflow:"+parentId;
|
node.type = "subflow:"+parentId;
|
||||||
node._def = registry.getNodeType(node.type);
|
node._def = registry.getNodeType(node.type);
|
||||||
|
@ -498,6 +498,15 @@ var RED = (function() {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (notificationId === 'restart-required') {
|
||||||
|
options.buttons = [
|
||||||
|
{
|
||||||
|
text: RED._("common.label.close"),
|
||||||
|
click: function() {
|
||||||
|
persistentNotifications[notificationId].hideNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
||||||
persistentNotifications[notificationId] = RED.notify(text,options);
|
persistentNotifications[notificationId] = RED.notify(text,options);
|
||||||
@ -525,6 +534,10 @@ var RED = (function() {
|
|||||||
RED.view.redrawStatus(node);
|
RED.view.redrawStatus(node);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let pendingNodeRemovedNotifications = []
|
||||||
|
let pendingNodeRemovedTimeout
|
||||||
|
|
||||||
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
||||||
var i,m;
|
var i,m;
|
||||||
var typeList;
|
var typeList;
|
||||||
@ -562,8 +575,15 @@ var RED = (function() {
|
|||||||
m = msg[i];
|
m = msg[i];
|
||||||
info = RED.nodes.removeNodeSet(m.id);
|
info = RED.nodes.removeNodeSet(m.id);
|
||||||
if (info.added) {
|
if (info.added) {
|
||||||
typeList = "<ul><li>"+m.types.map(RED.utils.sanitize).join("</li><li>")+"</li></ul>";
|
pendingNodeRemovedNotifications = pendingNodeRemovedNotifications.concat(m.types.map(RED.utils.sanitize))
|
||||||
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
if (pendingNodeRemovedTimeout) {
|
||||||
|
clearTimeout(pendingNodeRemovedTimeout)
|
||||||
|
}
|
||||||
|
pendingNodeRemovedTimeout = setTimeout(function () {
|
||||||
|
typeList = "<ul><li>"+pendingNodeRemovedNotifications.join("</li><li>")+"</li></ul>";
|
||||||
|
RED.notify(RED._("palette.event.nodeRemoved", {count:pendingNodeRemovedNotifications.length})+typeList,"success");
|
||||||
|
pendingNodeRemovedNotifications = []
|
||||||
|
}, 200)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadIconList();
|
loadIconList();
|
||||||
@ -702,7 +722,7 @@ var RED = (function() {
|
|||||||
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
|
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
|
||||||
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
||||||
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
|
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
|
||||||
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
|
{id:"menu-item-workspace-edit",label:RED._("menu.label.edit"),onselect:"core:edit-flow"},
|
||||||
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
|
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
|
||||||
]});
|
]});
|
||||||
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||||
|
@ -819,7 +819,7 @@ RED.clipboard = (function() {
|
|||||||
flow.forEach(function(node) {
|
flow.forEach(function(node) {
|
||||||
if (node.type === "tab") {
|
if (node.type === "tab") {
|
||||||
flows[node.id] = {
|
flows[node.id] = {
|
||||||
element: getFlowLabel(node,false),
|
element: getFlowLabel(node),
|
||||||
deferBuild: type !== "flow",
|
deferBuild: type !== "flow",
|
||||||
expanded: type === "flow",
|
expanded: type === "flow",
|
||||||
children: []
|
children: []
|
||||||
@ -1000,7 +1000,6 @@ RED.clipboard = (function() {
|
|||||||
try {
|
try {
|
||||||
RED.view.importNodes(newNodes, importOptions);
|
RED.view.importNodes(newNodes, importOptions);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.log(error.importConfig)
|
|
||||||
// Thrown for import_conflict
|
// Thrown for import_conflict
|
||||||
confirmImport(error.importConfig, newNodes, importOptions);
|
confirmImport(error.importConfig, newNodes, importOptions);
|
||||||
}
|
}
|
||||||
@ -1170,9 +1169,9 @@ RED.clipboard = (function() {
|
|||||||
function getNodeElement(n, isConflicted, isSelected, parent) {
|
function getNodeElement(n, isConflicted, isSelected, parent) {
|
||||||
var element;
|
var element;
|
||||||
if (n.type === "tab") {
|
if (n.type === "tab") {
|
||||||
element = getFlowLabel(n, isSelected);
|
element = getFlowLabel(n, isConflicted);
|
||||||
} else {
|
} else {
|
||||||
element = getNodeLabel(n, isConflicted, isSelected);
|
element = getNodeLabel(n, isConflicted, isSelected, parent);
|
||||||
}
|
}
|
||||||
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
|
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
|
||||||
controls.on("click", function(evt) { evt.stopPropagation(); });
|
controls.on("click", function(evt) { evt.stopPropagation(); });
|
||||||
@ -1222,14 +1221,14 @@ RED.clipboard = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFlowLabel(n) {
|
function getFlowLabel(n, isConflicted) {
|
||||||
n = JSON.parse(JSON.stringify(n));
|
n = JSON.parse(JSON.stringify(n));
|
||||||
n._def = RED.nodes.getType(n.type) || {};
|
n._def = RED.nodes.getType(n.type) || {};
|
||||||
if (n._def) {
|
if (n._def) {
|
||||||
n._ = n._def._;
|
n._ = n._def._;
|
||||||
}
|
}
|
||||||
|
|
||||||
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
|
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow red-ui-node-list-item"});
|
||||||
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
|
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
|
||||||
var label = (typeof n === "string")? n : n.label;
|
var label = (typeof n === "string")? n : n.label;
|
||||||
var newlineIndex = label.indexOf("\\n");
|
var newlineIndex = label.indexOf("\\n");
|
||||||
@ -1237,11 +1236,17 @@ RED.clipboard = (function() {
|
|||||||
label = label.substring(0,newlineIndex)+"...";
|
label = label.substring(0,newlineIndex)+"...";
|
||||||
}
|
}
|
||||||
contentDiv.text(label);
|
contentDiv.text(label);
|
||||||
|
|
||||||
|
if (!!isConflicted) {
|
||||||
|
const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
|
||||||
|
RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
|
||||||
|
}
|
||||||
|
|
||||||
// A conflicted flow should not be imported by default.
|
// A conflicted flow should not be imported by default.
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodeLabel(n, isConflicted) {
|
function getNodeLabel(n, isConflicted, isSelected, parent) {
|
||||||
n = JSON.parse(JSON.stringify(n));
|
n = JSON.parse(JSON.stringify(n));
|
||||||
n._def = RED.nodes.getType(n.type) || {};
|
n._def = RED.nodes.getType(n.type) || {};
|
||||||
if (n._def) {
|
if (n._def) {
|
||||||
@ -1249,6 +1254,11 @@ RED.clipboard = (function() {
|
|||||||
}
|
}
|
||||||
var div = $('<div>',{class:"red-ui-node-list-item"});
|
var div = $('<div>',{class:"red-ui-node-list-item"});
|
||||||
RED.utils.createNodeIcon(n,true).appendTo(div);
|
RED.utils.createNodeIcon(n,true).appendTo(div);
|
||||||
|
|
||||||
|
if (!parent && !!isConflicted) {
|
||||||
|
const conflictIcon = $('<span style="padding: 0 10px;"><i class="fa fa-exclamation-circle"></span>').appendTo(div)
|
||||||
|
RED.popover.tooltip(conflictIcon, RED._('clipboard.import.alreadyExists'))
|
||||||
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,25 +54,26 @@
|
|||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
var autoComplete = function(options) {
|
function getMatch(value, searchValue) {
|
||||||
function getMatch(value, searchValue) {
|
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
|
||||||
const idx = value.toLowerCase().indexOf(searchValue.toLowerCase());
|
const len = idx > -1 ? searchValue.length : 0;
|
||||||
const len = idx > -1 ? searchValue.length : 0;
|
return {
|
||||||
return {
|
index: idx,
|
||||||
index: idx,
|
found: idx > -1,
|
||||||
found: idx > -1,
|
pre: value.substring(0,idx),
|
||||||
pre: value.substring(0,idx),
|
match: value.substring(idx,idx+len),
|
||||||
match: value.substring(idx,idx+len),
|
post: value.substring(idx+len),
|
||||||
post: value.substring(idx+len),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function generateSpans(match) {
|
|
||||||
const els = [];
|
|
||||||
if(match.pre) { els.push($('<span/>').text(match.pre)); }
|
|
||||||
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
|
|
||||||
if(match.post) { els.push($('<span/>').text(match.post)); }
|
|
||||||
return els;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
function generateSpans(match) {
|
||||||
|
const els = [];
|
||||||
|
if(match.pre) { els.push($('<span/>').text(match.pre)); }
|
||||||
|
if(match.match) { els.push($('<span/>',{style:"font-weight: bold; color: var(--red-ui-text-color-link);"}).text(match.match)); }
|
||||||
|
if(match.post) { els.push($('<span/>').text(match.post)); }
|
||||||
|
return els;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msgAutoComplete = function(options) {
|
||||||
return function(val) {
|
return function(val) {
|
||||||
var matches = [];
|
var matches = [];
|
||||||
options.forEach(opt => {
|
options.forEach(opt => {
|
||||||
@ -102,6 +103,197 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getEnvVars (obj, envVars = {}) {
|
||||||
|
contextKnownKeys.env = contextKnownKeys.env || {}
|
||||||
|
if (contextKnownKeys.env[obj.id]) {
|
||||||
|
return contextKnownKeys.env[obj.id]
|
||||||
|
}
|
||||||
|
let parent
|
||||||
|
if (obj.type === 'tab' || obj.type === 'subflow') {
|
||||||
|
RED.nodes.eachConfig(function (conf) {
|
||||||
|
if (conf.type === "global-config") {
|
||||||
|
parent = conf;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (obj.g) {
|
||||||
|
parent = RED.nodes.group(obj.g)
|
||||||
|
} else if (obj.z) {
|
||||||
|
parent = RED.nodes.workspace(obj.z) || RED.nodes.subflow(obj.z)
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
getEnvVars(parent, envVars)
|
||||||
|
}
|
||||||
|
if (obj.env) {
|
||||||
|
obj.env.forEach(env => {
|
||||||
|
envVars[env.name] = obj
|
||||||
|
})
|
||||||
|
}
|
||||||
|
contextKnownKeys.env[obj.id] = envVars
|
||||||
|
return envVars
|
||||||
|
}
|
||||||
|
|
||||||
|
const envAutoComplete = function (val) {
|
||||||
|
const editStack = RED.editor.getEditStack()
|
||||||
|
if (editStack.length === 0) {
|
||||||
|
done([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const editingNode = editStack.pop()
|
||||||
|
if (!editingNode) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const envVarsMap = getEnvVars(editingNode)
|
||||||
|
const envVars = Object.keys(envVarsMap)
|
||||||
|
const matches = []
|
||||||
|
const i = val.lastIndexOf('${')
|
||||||
|
let searchKey = val
|
||||||
|
let isSubkey = false
|
||||||
|
if (i > -1) {
|
||||||
|
if (val.lastIndexOf('}') < i) {
|
||||||
|
searchKey = val.substring(i+2)
|
||||||
|
isSubkey = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
envVars.forEach(v => {
|
||||||
|
let valMatch = getMatch(v, searchKey);
|
||||||
|
if (valMatch.found) {
|
||||||
|
const optSrc = envVarsMap[v]
|
||||||
|
const element = $('<div>',{style: "display: flex"});
|
||||||
|
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
||||||
|
valEl.append(generateSpans(valMatch))
|
||||||
|
valEl.appendTo(element)
|
||||||
|
|
||||||
|
if (optSrc) {
|
||||||
|
const optEl = $('<div>').css({ "font-size": "0.8em" });
|
||||||
|
let label
|
||||||
|
if (optSrc.type === 'global-config') {
|
||||||
|
label = RED._('sidebar.context.global')
|
||||||
|
} else if (optSrc.type === 'group') {
|
||||||
|
label = RED.utils.getNodeLabel(optSrc) || (RED._('sidebar.info.group') + ': '+optSrc.id)
|
||||||
|
} else {
|
||||||
|
label = RED.utils.getNodeLabel(optSrc) || optSrc.id
|
||||||
|
}
|
||||||
|
|
||||||
|
optEl.append(generateSpans({ match: label }));
|
||||||
|
optEl.appendTo(element);
|
||||||
|
}
|
||||||
|
matches.push({
|
||||||
|
value: isSubkey ? val + v + '}' : v,
|
||||||
|
label: element,
|
||||||
|
i: valMatch.index
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
matches.sort(function(A,B){return A.i-B.i})
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
let contextKnownKeys = {}
|
||||||
|
let contextCache = {}
|
||||||
|
if (RED.events) {
|
||||||
|
RED.events.on("editor:close", function () {
|
||||||
|
contextCache = {}
|
||||||
|
contextKnownKeys = {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const contextAutoComplete = function() {
|
||||||
|
const that = this
|
||||||
|
const getContextKeysFromRuntime = function(scope, store, searchKey, done) {
|
||||||
|
contextKnownKeys[scope] = contextKnownKeys[scope] || {}
|
||||||
|
contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set()
|
||||||
|
if (searchKey.length > 0) {
|
||||||
|
try {
|
||||||
|
RED.utils.normalisePropertyExpression(searchKey)
|
||||||
|
} catch (err) {
|
||||||
|
// Not a valid context key, so don't try looking up
|
||||||
|
done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const url = `context/${scope}/${encodeURIComponent(searchKey)}?store=${store}&keysOnly`
|
||||||
|
if (contextCache[url]) {
|
||||||
|
// console.log('CACHED', url)
|
||||||
|
done()
|
||||||
|
} else {
|
||||||
|
// console.log('GET', url)
|
||||||
|
$.getJSON(url, function(data) {
|
||||||
|
// console.log(data)
|
||||||
|
contextCache[url] = true
|
||||||
|
const result = data[store] || {}
|
||||||
|
const keys = result.keys || []
|
||||||
|
const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '')
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) {
|
||||||
|
contextKnownKeys[scope][store].add(keyPrefix + key)
|
||||||
|
} else {
|
||||||
|
contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getContextKeys = function(key, done) {
|
||||||
|
const keyParts = key.split('.')
|
||||||
|
const partialKey = keyParts.pop()
|
||||||
|
let scope = that.propertyType
|
||||||
|
if (scope === 'flow') {
|
||||||
|
// Get the flow id of the node we're editing
|
||||||
|
const editStack = RED.editor.getEditStack()
|
||||||
|
if (editStack.length === 0) {
|
||||||
|
done([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const editingNode = editStack.pop()
|
||||||
|
if (editingNode.z) {
|
||||||
|
scope = `${scope}/${editingNode.z}`
|
||||||
|
} else {
|
||||||
|
done([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const store = (contextStoreOptions.length === 1) ? contextStoreOptions[0].value : that.optionValue
|
||||||
|
const searchKey = keyParts.join('.')
|
||||||
|
|
||||||
|
getContextKeysFromRuntime(scope, store, searchKey, function() {
|
||||||
|
if (contextKnownKeys[scope][store].has(key) || key.endsWith(']')) {
|
||||||
|
getContextKeysFromRuntime(scope, store, key, function() {
|
||||||
|
done(contextKnownKeys[scope][store])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
done(contextKnownKeys[scope][store])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(val, done) {
|
||||||
|
getContextKeys(val, function (keys) {
|
||||||
|
const matches = []
|
||||||
|
keys.forEach(v => {
|
||||||
|
let optVal = v
|
||||||
|
let valMatch = getMatch(optVal, val);
|
||||||
|
if (!valMatch.found && val.length > 0 && val.endsWith('.')) {
|
||||||
|
// Search key ends in '.' - but doesn't match. Check again
|
||||||
|
// with [" at the end instead so we match bracket notation
|
||||||
|
valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["')
|
||||||
|
}
|
||||||
|
if (valMatch.found) {
|
||||||
|
const element = $('<div>',{style: "display: flex"});
|
||||||
|
const valEl = $('<div/>',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"});
|
||||||
|
valEl.append(generateSpans(valMatch))
|
||||||
|
valEl.appendTo(element)
|
||||||
|
matches.push({
|
||||||
|
value: optVal,
|
||||||
|
label: element,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
matches.sort(function(a, b) { return a.value.localeCompare(b.value) });
|
||||||
|
done(matches);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is a hand-generated list of completions for the core nodes (based on the node help html).
|
// This is a hand-generated list of completions for the core nodes (based on the node help html).
|
||||||
var msgCompletions = [
|
var msgCompletions = [
|
||||||
{ value: "payload" },
|
{ value: "payload" },
|
||||||
@ -166,23 +358,27 @@
|
|||||||
{ value: "_session", source: ["websocket out","tcp out"] },
|
{ value: "_session", source: ["websocket out","tcp out"] },
|
||||||
]
|
]
|
||||||
var allOptions = {
|
var allOptions = {
|
||||||
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: autoComplete(msgCompletions)},
|
msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression, autoComplete: msgAutoComplete(msgCompletions)},
|
||||||
flow: {value:"flow",label:"flow.",hasValue:true,
|
flow: {value:"flow",label:"flow.",hasValue:true,
|
||||||
options:[],
|
options:[],
|
||||||
validate:RED.utils.validatePropertyExpression,
|
validate:RED.utils.validatePropertyExpression,
|
||||||
parse: contextParse,
|
parse: contextParse,
|
||||||
export: contextExport,
|
export: contextExport,
|
||||||
valueLabel: contextLabel
|
valueLabel: contextLabel,
|
||||||
|
autoComplete: contextAutoComplete
|
||||||
},
|
},
|
||||||
global: {value:"global",label:"global.",hasValue:true,
|
global: {value:"global",label:"global.",hasValue:true,
|
||||||
options:[],
|
options:[],
|
||||||
validate:RED.utils.validatePropertyExpression,
|
validate:RED.utils.validatePropertyExpression,
|
||||||
parse: contextParse,
|
parse: contextParse,
|
||||||
export: contextExport,
|
export: contextExport,
|
||||||
valueLabel: contextLabel
|
valueLabel: contextLabel,
|
||||||
|
autoComplete: contextAutoComplete
|
||||||
},
|
},
|
||||||
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
|
str: {value:"str",label:"string",icon:"red/images/typedInput/az.svg"},
|
||||||
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
|
num: {value:"num",label:"number",icon:"red/images/typedInput/09.svg",validate: function(v) {
|
||||||
|
return (true === RED.utils.validateTypedProperty(v, "num"));
|
||||||
|
} },
|
||||||
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
|
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.svg",options:["true","false"]},
|
||||||
json: {
|
json: {
|
||||||
value:"json",
|
value:"json",
|
||||||
@ -212,7 +408,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.svg"},
|
||||||
date: {value:"date",label:"timestamp",icon:"fa fa-clock-o",hasValue:false},
|
date: {
|
||||||
|
value:"date",
|
||||||
|
label:"timestamp",
|
||||||
|
icon:"fa fa-clock-o",
|
||||||
|
options:[
|
||||||
|
{
|
||||||
|
label: 'milliseconds since epoch',
|
||||||
|
value: ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'YYYY-MM-DDTHH:mm:ss.sssZ',
|
||||||
|
value: 'iso'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'JavaScript Date Object',
|
||||||
|
value: 'object'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
jsonata: {
|
jsonata: {
|
||||||
value: "jsonata",
|
value: "jsonata",
|
||||||
label: "expression",
|
label: "expression",
|
||||||
@ -249,7 +463,8 @@
|
|||||||
env: {
|
env: {
|
||||||
value: "env",
|
value: "env",
|
||||||
label: "env variable",
|
label: "env variable",
|
||||||
icon: "red/images/typedInput/env.svg"
|
icon: "red/images/typedInput/env.svg",
|
||||||
|
autoComplete: envAutoComplete
|
||||||
},
|
},
|
||||||
node: {
|
node: {
|
||||||
value: "node",
|
value: "node",
|
||||||
@ -425,6 +640,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nlsd = false;
|
var nlsd = false;
|
||||||
|
let contextStoreOptions;
|
||||||
|
|
||||||
$.widget( "nodered.typedInput", {
|
$.widget( "nodered.typedInput", {
|
||||||
_create: function() {
|
_create: function() {
|
||||||
@ -436,7 +652,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var contextStores = RED.settings.context.stores;
|
var contextStores = RED.settings.context.stores;
|
||||||
var contextOptions = contextStores.map(function(store) {
|
contextStoreOptions = contextStores.map(function(store) {
|
||||||
return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database"></i>'}
|
return {value:store,label: store, icon:'<i class="red-ui-typedInput-icon fa fa-database"></i>'}
|
||||||
}).sort(function(A,B) {
|
}).sort(function(A,B) {
|
||||||
if (A.value === RED.settings.context.default) {
|
if (A.value === RED.settings.context.default) {
|
||||||
@ -447,13 +663,17 @@
|
|||||||
return A.value.localeCompare(B.value);
|
return A.value.localeCompare(B.value);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (contextOptions.length < 2) {
|
if (contextStoreOptions.length < 2) {
|
||||||
allOptions.flow.options = [];
|
allOptions.flow.options = [];
|
||||||
allOptions.global.options = [];
|
allOptions.global.options = [];
|
||||||
} else {
|
} else {
|
||||||
allOptions.flow.options = contextOptions;
|
allOptions.flow.options = contextStoreOptions;
|
||||||
allOptions.global.options = contextOptions;
|
allOptions.global.options = contextStoreOptions;
|
||||||
}
|
}
|
||||||
|
// Translate timestamp options
|
||||||
|
allOptions.date.options.forEach(opt => {
|
||||||
|
opt.label = RED._("typedInput.date.format." + (opt.value || 'timestamp'), {defaultValue: opt.label})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
nlsd = true;
|
nlsd = true;
|
||||||
var that = this;
|
var that = this;
|
||||||
@ -542,7 +762,7 @@
|
|||||||
that.element.trigger('paste',evt);
|
that.element.trigger('paste',evt);
|
||||||
});
|
});
|
||||||
this.input.on('keydown', function(evt) {
|
this.input.on('keydown', function(evt) {
|
||||||
if (that.typeMap[that.propertyType].autoComplete) {
|
if (that.typeMap[that.propertyType].autoComplete || that.input.hasClass('red-ui-autoComplete')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
|
if (evt.keyCode >= 37 && evt.keyCode <= 40) {
|
||||||
@ -965,6 +1185,9 @@
|
|||||||
// If previousType is !null, then this is a change of the type, rather than the initialisation
|
// If previousType is !null, then this is a change of the type, rather than the initialisation
|
||||||
var previousType = this.typeMap[this.propertyType];
|
var previousType = this.typeMap[this.propertyType];
|
||||||
previousValue = this.input.val();
|
previousValue = this.input.val();
|
||||||
|
if (this.input.hasClass('red-ui-autoComplete')) {
|
||||||
|
this.input.autoComplete("destroy");
|
||||||
|
}
|
||||||
|
|
||||||
if (previousType && this.typeChanged) {
|
if (previousType && this.typeChanged) {
|
||||||
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
|
if (this.options.debug) { console.log(this.identifier,"typeChanged",{previousType,previousValue}) }
|
||||||
@ -1011,7 +1234,9 @@
|
|||||||
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
|
this.input.val(this.oldValues.hasOwnProperty("_")?this.oldValues["_"]:(opt.default||""))
|
||||||
}
|
}
|
||||||
if (previousType.autoComplete) {
|
if (previousType.autoComplete) {
|
||||||
this.input.autoComplete("destroy");
|
if (this.input.hasClass('red-ui-autoComplete')) {
|
||||||
|
this.input.autoComplete("destroy");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.propertyType = type;
|
this.propertyType = type;
|
||||||
@ -1139,6 +1364,16 @@
|
|||||||
} else {
|
} else {
|
||||||
this.optionSelectTrigger.hide();
|
this.optionSelectTrigger.hide();
|
||||||
}
|
}
|
||||||
|
if (opt.autoComplete) {
|
||||||
|
let searchFunction = opt.autoComplete
|
||||||
|
if (searchFunction.length === 0) {
|
||||||
|
searchFunction = opt.autoComplete.call(this)
|
||||||
|
}
|
||||||
|
this.input.autoComplete({
|
||||||
|
search: searchFunction,
|
||||||
|
minLength: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.optionMenu = this._createMenu(opt.options,opt,function(v){
|
this.optionMenu = this._createMenu(opt.options,opt,function(v){
|
||||||
if (!opt.multiple) {
|
if (!opt.multiple) {
|
||||||
@ -1181,8 +1416,12 @@
|
|||||||
this.valueLabelContainer.hide();
|
this.valueLabelContainer.hide();
|
||||||
this.elementDiv.show();
|
this.elementDiv.show();
|
||||||
if (opt.autoComplete) {
|
if (opt.autoComplete) {
|
||||||
|
let searchFunction = opt.autoComplete
|
||||||
|
if (searchFunction.length === 0) {
|
||||||
|
searchFunction = opt.autoComplete.call(this)
|
||||||
|
}
|
||||||
this.input.autoComplete({
|
this.input.autoComplete({
|
||||||
search: opt.autoComplete,
|
search: searchFunction,
|
||||||
minLength: 0
|
minLength: 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,26 @@ RED.contextMenu = (function () {
|
|||||||
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
|
const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
|
||||||
const canEdit = !RED.workspaces.isLocked()
|
const canEdit = !RED.workspaces.isLocked()
|
||||||
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
|
const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
|
||||||
const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
|
let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode;
|
||||||
const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
|
if (hasSelection) {
|
||||||
|
selection.nodes.forEach(n => {
|
||||||
|
if (n.type === 'group') {
|
||||||
|
hasGroup = true;
|
||||||
|
} else {
|
||||||
|
isAllGroups = false;
|
||||||
|
}
|
||||||
|
if (n.d) {
|
||||||
|
hasDisabledNode = true;
|
||||||
|
} else {
|
||||||
|
hasEnabledNode = true;
|
||||||
|
}
|
||||||
|
if (n.l === undefined || n.l) {
|
||||||
|
hasLabeledNode = true;
|
||||||
|
} else {
|
||||||
|
hasUnlabeledNode = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
const offset = $("#red-ui-workspace-chart").offset()
|
const offset = $("#red-ui-workspace-chart").offset()
|
||||||
|
|
||||||
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
|
let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
|
||||||
@ -44,7 +62,7 @@ RED.contextMenu = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
menuItems.push(
|
menuItems.push(
|
||||||
{ onselect: 'core:show-action-list', onpostselect: function () { } }
|
{ onselect: 'core:show-action-list', label: RED._("contextMenu.showActionList"), onpostselect: function () { } }
|
||||||
)
|
)
|
||||||
|
|
||||||
const insertOptions = []
|
const insertOptions = []
|
||||||
@ -55,7 +73,7 @@ RED.contextMenu = (function () {
|
|||||||
onselect: function () {
|
onselect: function () {
|
||||||
RED.view.showQuickAddDialog({
|
RED.view.showQuickAddDialog({
|
||||||
position: [addX, addY],
|
position: [addX, addY],
|
||||||
touchTrigger: true,
|
touchTrigger: 'ontouchstart' in window,
|
||||||
splice: isSingleLink ? selection.links[0] : undefined,
|
splice: isSingleLink ? selection.links[0] : undefined,
|
||||||
// spliceMultiple: isMultipleLinks
|
// spliceMultiple: isMultipleLinks
|
||||||
})
|
})
|
||||||
@ -108,16 +126,16 @@ RED.contextMenu = (function () {
|
|||||||
const nodeOptions = []
|
const nodeOptions = []
|
||||||
if (!hasMultipleSelection && !isGroup) {
|
if (!hasMultipleSelection && !isGroup) {
|
||||||
nodeOptions.push(
|
nodeOptions.push(
|
||||||
{ onselect: 'core:show-node-help' },
|
{ onselect: 'core:show-node-help', label: RED._('menu.label.showNodeHelp') },
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
nodeOptions.push(
|
nodeOptions.push(
|
||||||
{ onselect: 'core:enable-selected-nodes' },
|
{ onselect: 'core:enable-selected-nodes', label: RED._('menu.label.enableSelectedNodes'), disabled: !hasDisabledNode },
|
||||||
{ onselect: 'core:disable-selected-nodes' },
|
{ onselect: 'core:disable-selected-nodes', label: RED._('menu.label.disableSelectedNodes'), disabled: !hasEnabledNode },
|
||||||
null,
|
null,
|
||||||
{ onselect: 'core:show-selected-node-labels' },
|
{ onselect: 'core:show-selected-node-labels', label: RED._('menu.label.showSelectedNodeLabels'), disabled: !hasUnlabeledNode },
|
||||||
{ onselect: 'core:hide-selected-node-labels' }
|
{ onselect: 'core:hide-selected-node-labels', label: RED._('menu.label.hideSelectedNodeLabels'), disabled: !hasLabeledNode }
|
||||||
)
|
)
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
label: RED._('sidebar.info.node'),
|
label: RED._('sidebar.info.node'),
|
||||||
@ -126,8 +144,8 @@ RED.contextMenu = (function () {
|
|||||||
menuItems.push({
|
menuItems.push({
|
||||||
label: RED._('sidebar.info.group'),
|
label: RED._('sidebar.info.group'),
|
||||||
options: [
|
options: [
|
||||||
{ onselect: 'core:group-selection' },
|
{ onselect: 'core:group-selection', label: RED._("menu.label.groupSelection") },
|
||||||
{ onselect: 'core:ungroup-selection', disabled: !hasGroup },
|
{ onselect: 'core:ungroup-selection', label: RED._("menu.label.ungroupSelection"), disabled: !hasGroup },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
if (hasGroup) {
|
if (hasGroup) {
|
||||||
@ -143,8 +161,8 @@ RED.contextMenu = (function () {
|
|||||||
}
|
}
|
||||||
menuItems[menuItems.length - 1].options.push(
|
menuItems[menuItems.length - 1].options.push(
|
||||||
null,
|
null,
|
||||||
{ onselect: 'core:copy-group-style', disabled: !hasGroup },
|
{ onselect: 'core:copy-group-style', label: RED._("keyboard.copyGroupStyle"), disabled: !hasGroup },
|
||||||
{ onselect: 'core:paste-group-style', disabled: !hasGroup}
|
{ onselect: 'core:paste-group-style', label: RED._("keyboard.pasteGroupStyle"), disabled: !hasGroup}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (canEdit && hasMultipleSelection) {
|
if (canEdit && hasMultipleSelection) {
|
||||||
@ -168,16 +186,16 @@ RED.contextMenu = (function () {
|
|||||||
|
|
||||||
menuItems.push(
|
menuItems.push(
|
||||||
null,
|
null,
|
||||||
{ onselect: 'core:undo', disabled: RED.history.list().length === 0 },
|
{ onselect: 'core:undo', label: RED._("keyboard.undoChange"), disabled: RED.history.list().length === 0 },
|
||||||
{ onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
|
{ onselect: 'core:redo', label: RED._("keyboard.redoChange"), disabled: RED.history.listRedo().length === 0 },
|
||||||
null,
|
null,
|
||||||
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
|
{ onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
|
||||||
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
|
{ onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
|
||||||
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
||||||
{ onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
|
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
|
||||||
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
||||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
||||||
{ onselect: 'core:select-all-nodes' },
|
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -989,9 +989,10 @@ RED.diff = (function() {
|
|||||||
}
|
}
|
||||||
if (localNode && remoteNode && typeof localNode[d] === "string") {
|
if (localNode && remoteNode && typeof localNode[d] === "string") {
|
||||||
if (/\n/.test(localNode[d]) || /\n/.test(remoteNode[d])) {
|
if (/\n/.test(localNode[d]) || /\n/.test(remoteNode[d])) {
|
||||||
$('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
|
var textDiff = $('<button class="red-ui-button red-ui-button-small red-ui-diff-text-diff-button"><i class="fa fa-file-o"> <i class="fa fa-caret-left"></i> <i class="fa fa-caret-right"></i> <i class="fa fa-file-o"></i></button>').on("click", function() {
|
||||||
showTextDiff(localNode[d],remoteNode[d]);
|
showTextDiff(localNode[d],remoteNode[d]);
|
||||||
}).appendTo(propertyNameCell);
|
}).appendTo(propertyNameCell);
|
||||||
|
RED.popover.tooltip(textDiff, RED._("diff.compareChanges"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +115,9 @@ RED.editor = (function() {
|
|||||||
var valid = validateNodeProperty(node, definition, prop, properties[prop]);
|
var valid = validateNodeProperty(node, definition, prop, properties[prop]);
|
||||||
if ((typeof valid) === "string") {
|
if ((typeof valid) === "string") {
|
||||||
result.push(valid);
|
result.push(valid);
|
||||||
}
|
} else if (Array.isArray(valid)) {
|
||||||
else if(!valid) {
|
result = result.concat(valid)
|
||||||
|
} else if(!valid) {
|
||||||
result.push(prop);
|
result.push(prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,7 +166,7 @@ RED.editor = (function() {
|
|||||||
// If the validator takes two arguments, it is a 3.x validator that
|
// If the validator takes two arguments, it is a 3.x validator that
|
||||||
// can return a String to mean 'invalid' and provide a reason
|
// can return a String to mean 'invalid' and provide a reason
|
||||||
if ((definition[property].validate.length === 2) &&
|
if ((definition[property].validate.length === 2) &&
|
||||||
((typeof valid) === "string")) {
|
((typeof valid) === "string") || Array.isArray(valid)) {
|
||||||
return valid;
|
return valid;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, a 2.x returns a truth-like/false-like value that
|
// Otherwise, a 2.x returns a truth-like/false-like value that
|
||||||
@ -181,6 +182,17 @@ RED.editor = (function() {
|
|||||||
error: err.message
|
error: err.message
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (valid) {
|
||||||
|
// If the validator is not provided in node property => Check if the input has a validator
|
||||||
|
if ("category" in node._def) {
|
||||||
|
const isConfig = node._def.category === "config";
|
||||||
|
const prefix = isConfig ? "node-config-input" : "node-input";
|
||||||
|
const input = $("#"+prefix+"-"+property);
|
||||||
|
const isTypedInput = input.length > 0 && input.next(".red-ui-typedInput-container").length > 0;
|
||||||
|
if (isTypedInput) {
|
||||||
|
valid = input.typedInput("validate");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
|
if (valid && definition[property].type && RED.nodes.getType(definition[property].type) && !("validate" in definition[property])) {
|
||||||
if (!value || value == "_ADD_") {
|
if (!value || value == "_ADD_") {
|
||||||
@ -1219,7 +1231,11 @@ RED.editor = (function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (node_def.hasUsers !== false) {
|
if (node_def.hasUsers !== false) {
|
||||||
$('<span><i class="fa fa-info-circle"></i> <span id="red-ui-editor-config-user-count"></span></span>').css("margin-left", "10px").appendTo(trayFooterLeft);
|
// $('<span><i class="fa fa-info-circle"></i> <span id="red-ui-editor-config-user-count"></span></span>').css("margin-left", "10px").appendTo(trayFooterLeft);
|
||||||
|
$('<button type="button" class="red-ui-button"><i class="fa fa-user"></i><span id="red-ui-editor-config-user-count"></span></button>').on('click', function() {
|
||||||
|
RED.sidebar.info.outliner.search('uses:'+editing_config_node.id)
|
||||||
|
RED.sidebar.info.show()
|
||||||
|
}).appendTo(trayFooterLeft);
|
||||||
}
|
}
|
||||||
trayFooter.append('<span class="red-ui-tray-footer-right"><span id="red-ui-editor-config-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="red-ui-editor-config-scope"></select></span>');
|
trayFooter.append('<span class="red-ui-tray-footer-right"><span id="red-ui-editor-config-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="red-ui-editor-config-scope"></select></span>');
|
||||||
|
|
||||||
@ -1277,7 +1293,8 @@ RED.editor = (function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (node_def.hasUsers !== false) {
|
if (node_def.hasUsers !== false) {
|
||||||
$("#red-ui-editor-config-user-count").text(RED._("editor.nodesUse", {count:editing_config_node.users.length})).parent().show();
|
$("#red-ui-editor-config-user-count").text(editing_config_node.users.length).parent().show();
|
||||||
|
RED.popover.tooltip($("#red-ui-editor-config-user-count").parent(), function() { return RED._('editor.nodesUse',{count:editing_config_node.users.length})});
|
||||||
}
|
}
|
||||||
trayBody.i18n();
|
trayBody.i18n();
|
||||||
trayFooter.i18n();
|
trayFooter.i18n();
|
||||||
@ -2070,6 +2087,7 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
editBuffer: function(options) { showTypeEditor("_buffer", options) },
|
editBuffer: function(options) { showTypeEditor("_buffer", options) },
|
||||||
|
getEditStack: function () { return [...editStack] },
|
||||||
buildEditForm: buildEditForm,
|
buildEditForm: buildEditForm,
|
||||||
validateNode: validateNode,
|
validateNode: validateNode,
|
||||||
updateNodeProperties: updateNodeProperties,
|
updateNodeProperties: updateNodeProperties,
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
var i=0,l=bufferBinValue.length;
|
var i=0,l=bufferBinValue.length;
|
||||||
var c = 0;
|
var c = 0;
|
||||||
for(i=0;i<l;i++) {
|
for(i=0;i<l;i++) {
|
||||||
var d = parseInt(bufferBinValue[i]);
|
var d = parseInt(Number(bufferBinValue[i]));
|
||||||
if (!isString && (isNaN(d) || d < 0 || d > 255)) {
|
if (!isString && (isNaN(d) || d < 0 || d > 255)) {
|
||||||
valid = false;
|
valid = false;
|
||||||
break;
|
break;
|
||||||
|
@ -966,12 +966,10 @@ RED.editor.codeEditor.monaco = (function() {
|
|||||||
|
|
||||||
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
|
//Unbind ctrl-Enter (default action is to insert a newline in editor) This permits the shortcut to close the tray.
|
||||||
try {
|
try {
|
||||||
ed._standaloneKeybindingService.addDynamicKeybinding(
|
monaco.editor.addKeybindingRule({keybinding: 0, command: "-editor.action.insertLineAfter"});
|
||||||
'-editor.action.insertLineAfter', // command ID prefixed by '-'
|
} catch (error) {
|
||||||
null, // keybinding
|
console.warn(error)
|
||||||
() => {} // need to pass an empty handler
|
}
|
||||||
);
|
|
||||||
} catch (error) { }
|
|
||||||
|
|
||||||
ed.nodered = {
|
ed.nodered = {
|
||||||
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
|
refreshModuleLibs: refreshModuleLibs //expose this for function node externalModules refresh
|
||||||
|
@ -169,7 +169,7 @@
|
|||||||
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
|
var currentScrollTop = $(".red-ui-editor-type-markdown-panel-preview").scrollTop();
|
||||||
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
||||||
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
|
$(".red-ui-editor-type-markdown-panel-preview").scrollTop(currentScrollTop);
|
||||||
mermaid.init();
|
RED.editor.mermaid.render()
|
||||||
},200);
|
},200);
|
||||||
})
|
})
|
||||||
if (options.header) {
|
if (options.header) {
|
||||||
@ -178,7 +178,7 @@
|
|||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
$(".red-ui-editor-type-markdown-panel-preview").html(RED.utils.renderMarkdown(expressionEditor.getValue()));
|
||||||
mermaid.init();
|
RED.editor.mermaid.render()
|
||||||
}
|
}
|
||||||
panels = RED.panels.create({
|
panels = RED.panels.create({
|
||||||
id:"red-ui-editor-type-markdown-panels",
|
id:"red-ui-editor-type-markdown-panels",
|
||||||
|
54
packages/node_modules/@node-red/editor-client/src/js/ui/editors/mermaid.js
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
RED.editor.mermaid = (function () {
|
||||||
|
let initializing = false
|
||||||
|
let loaded = false
|
||||||
|
let pendingEvals = []
|
||||||
|
let diagramIds = 0
|
||||||
|
|
||||||
|
function render(selector = '.mermaid') {
|
||||||
|
// $(selector).hide()
|
||||||
|
if (!loaded) {
|
||||||
|
pendingEvals.push(selector)
|
||||||
|
|
||||||
|
if (!initializing) {
|
||||||
|
initializing = true
|
||||||
|
$.getScript(
|
||||||
|
'vendor/mermaid/mermaid.min.js',
|
||||||
|
function (data, stat, jqxhr) {
|
||||||
|
mermaid.initialize({
|
||||||
|
startOnLoad: false,
|
||||||
|
theme: RED.settings.get('mermaid', {}).theme
|
||||||
|
})
|
||||||
|
loaded = true
|
||||||
|
while(pendingEvals.length > 0) {
|
||||||
|
const pending = pendingEvals.shift()
|
||||||
|
render(pending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const nodes = document.querySelectorAll(selector)
|
||||||
|
|
||||||
|
nodes.forEach(async node => {
|
||||||
|
if (!node.getAttribute('mermaid-processed')) {
|
||||||
|
const mermaidContent = node.innerText
|
||||||
|
node.setAttribute('mermaid-processed', true)
|
||||||
|
try {
|
||||||
|
const { svg } = await mermaid.render('mermaid-render-'+Date.now()+'-'+(diagramIds++), mermaidContent);
|
||||||
|
node.innerHTML = svg
|
||||||
|
} catch (err) {
|
||||||
|
$('<div>').css({
|
||||||
|
fontSize: '0.8em',
|
||||||
|
border: '1px solid var(--red-ui-border-color-error)',
|
||||||
|
padding: '5px',
|
||||||
|
marginBottom: '10px',
|
||||||
|
}).text(err.toString()).prependTo(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
render: render,
|
||||||
|
};
|
||||||
|
})();
|
@ -196,7 +196,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('<div class="form-row">'+
|
$('<div class="form-row">'+
|
||||||
'<label for="node-input-show-label-btn" data-i18n="editor.label"></label>'+
|
'<label for="node-input-show-label" data-i18n="editor.label"></label>'+
|
||||||
'<span style="margin-right: 2px;"/>'+
|
'<span style="margin-right: 2px;"/>'+
|
||||||
'<input type="checkbox" id="node-input-show-label"/>'+
|
'<input type="checkbox" id="node-input-show-label"/>'+
|
||||||
'</div>').appendTo(dialogForm);
|
'</div>').appendTo(dialogForm);
|
||||||
|
@ -71,7 +71,7 @@ RED.envVar = (function() {
|
|||||||
};
|
};
|
||||||
if (item.name.trim() !== "") {
|
if (item.name.trim() !== "") {
|
||||||
new_env.push(item);
|
new_env.push(item);
|
||||||
if ((item.type === "cred") && (item.value !== "__PWRD__")) {
|
if (item.type === "cred") {
|
||||||
credentials.map[item.name] = item.value;
|
credentials.map[item.name] = item.value;
|
||||||
credentials.map["has_"+item.name] = (item.value !== "");
|
credentials.map["has_"+item.name] = (item.value !== "");
|
||||||
item.value = "__PWRD__";
|
item.value = "__PWRD__";
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
// Mermaid diagram stub library for on-demand dynamic loading
|
|
||||||
// Will be overwritten after script loading by $.getScript
|
|
||||||
var mermaid = (function () {
|
|
||||||
var enabled /* = undefined */;
|
|
||||||
|
|
||||||
var initializing = false;
|
|
||||||
var initCalled = false;
|
|
||||||
|
|
||||||
function initialize(opt) {
|
|
||||||
if (enabled === undefined) {
|
|
||||||
if (RED.settings.markdownEditor &&
|
|
||||||
RED.settings.markdownEditor.mermaid) {
|
|
||||||
enabled = RED.settings.markdownEditor.mermaid.enabled;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (enabled) {
|
|
||||||
initializing = true;
|
|
||||||
$.getScript("vendor/mermaid/mermaid.min.js",
|
|
||||||
function (data, stat, jqxhr) {
|
|
||||||
$(".mermaid").show();
|
|
||||||
// invoke loaded mermaid API
|
|
||||||
initializing = false;
|
|
||||||
mermaid.initialize(opt);
|
|
||||||
if (initCalled) {
|
|
||||||
mermaid.init();
|
|
||||||
initCalled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function init() {
|
|
||||||
if (initializing) {
|
|
||||||
$(".mermaid").hide();
|
|
||||||
initCalled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
initialize: initialize,
|
|
||||||
init: init,
|
|
||||||
};
|
|
||||||
})();
|
|
@ -484,7 +484,7 @@ RED.palette = (function() {
|
|||||||
var currentLabel = paletteNode.attr("data-palette-label");
|
var currentLabel = paletteNode.attr("data-palette-label");
|
||||||
var currentInfo = paletteNode.attr("data-palette-info");
|
var currentInfo = paletteNode.attr("data-palette-info");
|
||||||
|
|
||||||
if (currentLabel !== sf.name || currentInfo !== sf.info) {
|
if (currentLabel !== sf.name || currentInfo !== sf.info || sf.in.length > 0 || sf.out.length > 0) {
|
||||||
paletteNode.attr("data-palette-info",sf.info);
|
paletteNode.attr("data-palette-info",sf.info);
|
||||||
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||""));
|
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||""));
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ RED.projects.settings = (function() {
|
|||||||
var description = addTargetToExternalLinks($('<span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span>')).appendTo(container);
|
var description = addTargetToExternalLinks($('<span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span>')).appendTo(container);
|
||||||
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
|
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
mermaid.init();
|
RED.editor.mermaid.render()
|
||||||
}, 200);
|
}, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,9 +647,9 @@ RED.sidebar.versionControl = (function() {
|
|||||||
$.getJSON("projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
|
$.getJSON("projects/"+activeProject.name+"/commits/"+entry.sha,function(result) {
|
||||||
result.project = activeProject;
|
result.project = activeProject;
|
||||||
result.parents = entry.parents;
|
result.parents = entry.parents;
|
||||||
result.oldRev = entry.sha+"~1";
|
result.oldRev = entry.parents[0].length !== 0 ? entry.sha+"~1" : entry.sha;
|
||||||
result.newRev = entry.sha;
|
result.newRev = entry.sha;
|
||||||
result.oldRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1";
|
result.oldRevTitle = entry.parents[0].length !== 0 ? RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7)+"~1" : " ";
|
||||||
result.newRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7);
|
result.newRevTitle = RED._("sidebar.project.versionControl.commitCapital")+" "+entry.sha.substring(0,7);
|
||||||
result.date = humanizeSinceDate(parseInt(entry.date));
|
result.date = humanizeSinceDate(parseInt(entry.date));
|
||||||
RED.diff.showCommitDiff(result);
|
RED.diff.showCommitDiff(result);
|
||||||
|
@ -158,6 +158,7 @@ RED.sidebar.config = (function() {
|
|||||||
entry.data('node',node.id);
|
entry.data('node',node.id);
|
||||||
nodeDiv.data('node',node.id);
|
nodeDiv.data('node',node.id);
|
||||||
var label = $('<div class="red-ui-palette-label"></div>').text(labelText).appendTo(nodeDiv);
|
var label = $('<div class="red-ui-palette-label"></div>').text(labelText).appendTo(nodeDiv);
|
||||||
|
|
||||||
if (node.d) {
|
if (node.d) {
|
||||||
nodeDiv.addClass("red-ui-palette-node-config-disabled");
|
nodeDiv.addClass("red-ui-palette-node-config-disabled");
|
||||||
$('<i class="fa fa-ban"></i>').prependTo(label);
|
$('<i class="fa fa-ban"></i>').prependTo(label);
|
||||||
@ -179,6 +180,20 @@ RED.sidebar.config = (function() {
|
|||||||
nodeDiv.addClass("red-ui-palette-node-config-unused");
|
nodeDiv.addClass("red-ui-palette-node-config-unused");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!node.valid) {
|
||||||
|
nodeDiv.addClass("red-ui-palette-node-config-invalid")
|
||||||
|
const nodeDivAnnotations = $('<svg class="red-ui-palette-node-annotations red-ui-flow-node-error" width="10" height="10"></svg>').appendTo(nodeDiv)
|
||||||
|
const errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path");
|
||||||
|
errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z");
|
||||||
|
nodeDivAnnotations.append($(errorBadge))
|
||||||
|
RED.popover.tooltip(nodeDivAnnotations, function () {
|
||||||
|
if (node.validationErrors && node.validationErrors.length > 0) {
|
||||||
|
return RED._("editor.errors.invalidProperties")+"<br> - "+node.validationErrors.join("<br> - ")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
nodeDiv.on('click',function(e) {
|
nodeDiv.on('click',function(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
RED.view.select(false);
|
RED.view.select(false);
|
||||||
|
@ -232,7 +232,7 @@ RED.sidebar.context = (function() {
|
|||||||
typeHint: data.format,
|
typeHint: data.format,
|
||||||
sourceId: id+"."+k,
|
sourceId: id+"."+k,
|
||||||
tools: tools,
|
tools: tools,
|
||||||
path: ""
|
path: k
|
||||||
}).appendTo(propRow.children()[1]);
|
}).appendTo(propRow.children()[1]);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -278,7 +278,7 @@ RED.sidebar.context = (function() {
|
|||||||
typeHint: data.format,
|
typeHint: data.format,
|
||||||
sourceId: id+"."+k,
|
sourceId: id+"."+k,
|
||||||
tools: tools,
|
tools: tools,
|
||||||
path: ""
|
path: k
|
||||||
}).appendTo(propRow.children()[1]);
|
}).appendTo(propRow.children()[1]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -299,7 +299,7 @@ RED.sidebar.context = (function() {
|
|||||||
typeHint: v.format,
|
typeHint: v.format,
|
||||||
sourceId: id+"."+k,
|
sourceId: id+"."+k,
|
||||||
tools: tools,
|
tools: tools,
|
||||||
path: ""
|
path: k
|
||||||
}).appendTo(propRow.children()[1]);
|
}).appendTo(propRow.children()[1]);
|
||||||
if (contextStores.length > 1) {
|
if (contextStores.length > 1) {
|
||||||
$("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))
|
$("<span>",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0]))
|
||||||
|
@ -383,6 +383,7 @@ RED.sidebar.help = (function() {
|
|||||||
$(this).toggleClass('expanded',!isExpanded);
|
$(this).toggleClass('expanded',!isExpanded);
|
||||||
})
|
})
|
||||||
helpSection.parent().scrollTop(0);
|
helpSection.parent().scrollTop(0);
|
||||||
|
RED.editor.mermaid.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
function set(html,title) {
|
function set(html,title) {
|
||||||
|
@ -464,7 +464,7 @@ RED.sidebar.info = (function() {
|
|||||||
}
|
}
|
||||||
$(this).toggleClass('expanded',!isExpanded);
|
$(this).toggleClass('expanded',!isExpanded);
|
||||||
});
|
});
|
||||||
mermaid.init();
|
RED.editor.mermaid.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
var tips = (function() {
|
var tips = (function() {
|
||||||
|
@ -186,8 +186,15 @@ RED.typeSearch = (function() {
|
|||||||
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
var iconContainer = $('<div/>',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv);
|
||||||
RED.utils.createIconElement(icon_url, iconContainer, false);
|
RED.utils.createIconElement(icon_url, iconContainer, false);
|
||||||
|
|
||||||
|
if (/^subflow:/.test(object.type)) {
|
||||||
if (!/^_action_:/.test(object.type) && object.type !== "junction") {
|
var sf = RED.nodes.subflow(object.type.substring(8));
|
||||||
|
if (sf.in.length > 0) {
|
||||||
|
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
|
||||||
|
}
|
||||||
|
if (sf.out.length > 0) {
|
||||||
|
$('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv);
|
||||||
|
}
|
||||||
|
} else if (!/^_action_:/.test(object.type) && object.type !== "junction") {
|
||||||
if (def.inputs > 0) {
|
if (def.inputs > 0) {
|
||||||
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
|
$('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv);
|
||||||
}
|
}
|
||||||
@ -323,7 +330,7 @@ RED.typeSearch = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function applyFilter(filter,type,def) {
|
function applyFilter(filter,type,def) {
|
||||||
return !filter ||
|
return !def || !filter ||
|
||||||
(
|
(
|
||||||
(!filter.spliceMultiple) &&
|
(!filter.spliceMultiple) &&
|
||||||
(!filter.type || type === filter.type) &&
|
(!filter.type || type === filter.type) &&
|
||||||
|
@ -101,28 +101,8 @@ RED.utils = (function() {
|
|||||||
|
|
||||||
renderer.code = function (code, lang) {
|
renderer.code = function (code, lang) {
|
||||||
if(lang === "mermaid") {
|
if(lang === "mermaid") {
|
||||||
// mermaid diagram rendering
|
return `<pre class='mermaid'>${code}</pre>`;
|
||||||
if (mermaidIsEnabled === undefined) {
|
} else {
|
||||||
if (RED.settings.markdownEditor &&
|
|
||||||
RED.settings.markdownEditor.mermaid) {
|
|
||||||
mermaidIsEnabled = RED.settings.markdownEditor.mermaid.enabled;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mermaidIsEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mermaidIsEnabled) {
|
|
||||||
if (!mermaidIsInitialized) {
|
|
||||||
mermaidIsInitialized = true;
|
|
||||||
mermaid.initialize({startOnLoad:false});
|
|
||||||
}
|
|
||||||
return `<pre class='mermaid'>${code}</pre>`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return `<details><summary>${RED._("markdownEditor.mermaid.summary")}</summary><pre><code>${code}</code></pre></details>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "<pre><code>" +code +"</code></pre>";
|
return "<pre><code>" +code +"</code></pre>";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -917,6 +897,51 @@ RED.utils = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks a typed property is valid according to the type.
|
||||||
|
* Returns true if valid.
|
||||||
|
* Return String error message if invalid
|
||||||
|
* @param {*} propertyType
|
||||||
|
* @param {*} propertyValue
|
||||||
|
* @returns true if valid, String if invalid
|
||||||
|
*/
|
||||||
|
function validateTypedProperty(propertyValue, propertyType, opt) {
|
||||||
|
|
||||||
|
let error
|
||||||
|
if (propertyType === 'json') {
|
||||||
|
try {
|
||||||
|
JSON.parse(propertyValue);
|
||||||
|
} catch(err) {
|
||||||
|
error = RED._("validator.errors.invalid-json", {
|
||||||
|
error: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (propertyType === 'msg' || propertyType === 'flow' || propertyType === 'global' ) {
|
||||||
|
if (!RED.utils.validatePropertyExpression(propertyValue)) {
|
||||||
|
error = RED._("validator.errors.invalid-prop")
|
||||||
|
}
|
||||||
|
} else if (propertyType === 'num') {
|
||||||
|
if (!/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(propertyValue)) {
|
||||||
|
error = RED._("validator.errors.invalid-num")
|
||||||
|
}
|
||||||
|
} else if (propertyType === 'jsonata') {
|
||||||
|
try {
|
||||||
|
jsonata(propertyValue)
|
||||||
|
} catch(err) {
|
||||||
|
error = RED._("validator.errors.invalid-expr", {
|
||||||
|
error: err.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
if (opt && opt.label) {
|
||||||
|
return opt.label+': '+error
|
||||||
|
}
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function getMessageProperty(msg,expr) {
|
function getMessageProperty(msg,expr) {
|
||||||
var result = null;
|
var result = null;
|
||||||
var msgPropParts;
|
var msgPropParts;
|
||||||
@ -1451,6 +1476,7 @@ RED.utils = (function() {
|
|||||||
getDarkerColor: getDarkerColor,
|
getDarkerColor: getDarkerColor,
|
||||||
parseModuleList: parseModuleList,
|
parseModuleList: parseModuleList,
|
||||||
checkModuleAllowed: checkModuleAllowed,
|
checkModuleAllowed: checkModuleAllowed,
|
||||||
getBrowserInfo: getBrowserInfo
|
getBrowserInfo: getBrowserInfo,
|
||||||
|
validateTypedProperty: validateTypedProperty
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -4155,10 +4155,15 @@ RED.view = (function() {
|
|||||||
scaleFactor = 30/largestEdge;
|
scaleFactor = 30/largestEdge;
|
||||||
}
|
}
|
||||||
var width = img.width * scaleFactor;
|
var width = img.width * scaleFactor;
|
||||||
|
if (width > 20) {
|
||||||
|
scalefactor *= 20/width;
|
||||||
|
width = 20;
|
||||||
|
}
|
||||||
var height = img.height * scaleFactor;
|
var height = img.height * scaleFactor;
|
||||||
icon.attr("width",width);
|
icon.attr("width",width);
|
||||||
icon.attr("height",height);
|
icon.attr("height",height);
|
||||||
icon.attr("x",15-width/2);
|
icon.attr("x",15-width/2);
|
||||||
|
icon.attr("y",(30-height)/2);
|
||||||
}
|
}
|
||||||
icon.attr("xlink:href",iconUrl);
|
icon.attr("xlink:href",iconUrl);
|
||||||
icon.style("display",null);
|
icon.style("display",null);
|
||||||
@ -4187,7 +4192,7 @@ RED.view = (function() {
|
|||||||
nodeEl.__statusGroup__.style.display = "none";
|
nodeEl.__statusGroup__.style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
nodeEl.__statusGroup__.style.display = "inline";
|
nodeEl.__statusGroup__.style.display = "inline";
|
||||||
let backgroundWidth = 12
|
let backgroundWidth = 15
|
||||||
var fill = status_colours[d.status.fill]; // Only allow our colours for now
|
var fill = status_colours[d.status.fill]; // Only allow our colours for now
|
||||||
if (d.status.shape == null && fill == null) {
|
if (d.status.shape == null && fill == null) {
|
||||||
backgroundWidth = 0
|
backgroundWidth = 0
|
||||||
@ -4207,7 +4212,11 @@ RED.view = (function() {
|
|||||||
nodeEl.__statusLabel__.textContent = "";
|
nodeEl.__statusLabel__.textContent = "";
|
||||||
}
|
}
|
||||||
const textSize = nodeEl.__statusLabel__.getBBox()
|
const textSize = nodeEl.__statusLabel__.getBBox()
|
||||||
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth + textSize.width + 6)
|
backgroundWidth += textSize.width
|
||||||
|
if (backgroundWidth > 0 && textSize.width > 0) {
|
||||||
|
backgroundWidth += 6
|
||||||
|
}
|
||||||
|
nodeEl.__statusBackground__.setAttribute('width', backgroundWidth)
|
||||||
}
|
}
|
||||||
delete d.dirtyStatus;
|
delete d.dirtyStatus;
|
||||||
}
|
}
|
||||||
@ -4619,8 +4628,8 @@ RED.view = (function() {
|
|||||||
statusBackground.setAttribute("y",-1);
|
statusBackground.setAttribute("y",-1);
|
||||||
statusBackground.setAttribute("width",200);
|
statusBackground.setAttribute("width",200);
|
||||||
statusBackground.setAttribute("height",13);
|
statusBackground.setAttribute("height",13);
|
||||||
statusBackground.setAttribute("rx",1);
|
statusBackground.setAttribute("rx",2);
|
||||||
statusBackground.setAttribute("ry",1);
|
statusBackground.setAttribute("ry",2);
|
||||||
|
|
||||||
statusEl.appendChild(statusBackground);
|
statusEl.appendChild(statusBackground);
|
||||||
node[0][0].__statusBackground__ = statusBackground;
|
node[0][0].__statusBackground__ = statusBackground;
|
||||||
@ -6243,6 +6252,10 @@ RED.view = (function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (selection.links) {
|
||||||
|
selectedLinks.clear();
|
||||||
|
selection.links.forEach(selectedLinks.add);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateSelection();
|
updateSelection();
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
RED.workspaces = (function() {
|
RED.workspaces = (function() {
|
||||||
|
|
||||||
|
const documentTitle = document.title;
|
||||||
|
|
||||||
var activeWorkspace = 0;
|
var activeWorkspace = 0;
|
||||||
var workspaceIndex = 0;
|
var workspaceIndex = 0;
|
||||||
|
|
||||||
@ -339,12 +341,18 @@ RED.workspaces = (function() {
|
|||||||
$("#red-ui-workspace-chart").show();
|
$("#red-ui-workspace-chart").show();
|
||||||
activeWorkspace = tab.id;
|
activeWorkspace = tab.id;
|
||||||
window.location.hash = 'flow/'+tab.id;
|
window.location.hash = 'flow/'+tab.id;
|
||||||
|
if (tab.label) {
|
||||||
|
document.title = `${documentTitle} : ${tab.label}`
|
||||||
|
} else {
|
||||||
|
document.title = documentTitle
|
||||||
|
}
|
||||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
|
$("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
|
||||||
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
|
$("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
|
||||||
} else {
|
} else {
|
||||||
$("#red-ui-workspace-chart").hide();
|
$("#red-ui-workspace-chart").hide();
|
||||||
activeWorkspace = 0;
|
activeWorkspace = 0;
|
||||||
window.location.hash = '';
|
window.location.hash = '';
|
||||||
|
document.title = documentTitle
|
||||||
}
|
}
|
||||||
event.workspace = activeWorkspace;
|
event.workspace = activeWorkspace;
|
||||||
RED.events.emit("workspace:change",event);
|
RED.events.emit("workspace:change",event);
|
||||||
|
@ -40,46 +40,32 @@ RED.validators = {
|
|||||||
return opt ? RED._("validator.errors.invalid-regexp") : false;
|
return opt ? RED._("validator.errors.invalid-regexp") : false;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
typedInput: function(ptypeName,isConfig,mopt) {
|
typedInput: function(ptypeName, isConfig, mopt) {
|
||||||
|
let options = ptypeName
|
||||||
|
if (typeof ptypeName === 'string' ) {
|
||||||
|
options = {}
|
||||||
|
options.typeField = ptypeName
|
||||||
|
options.isConfig = isConfig
|
||||||
|
options.allowBlank = false
|
||||||
|
}
|
||||||
return function(v, opt) {
|
return function(v, opt) {
|
||||||
var ptype = $("#node-"+(isConfig?"config-":"")+"input-"+ptypeName).val() || this[ptypeName];
|
let ptype = options.type
|
||||||
if (ptype === 'json') {
|
if (!ptype && options.typeField) {
|
||||||
try {
|
ptype = $("#node-"+(options.isConfig?"config-":"")+"input-"+options.typeField).val() || this[options.typeField];
|
||||||
JSON.parse(v);
|
|
||||||
return true;
|
|
||||||
} catch(err) {
|
|
||||||
if (opt && opt.label) {
|
|
||||||
return RED._("validator.errors.invalid-json-prop", {
|
|
||||||
error: err.message,
|
|
||||||
prop: opt.label,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return opt ? RED._("validator.errors.invalid-json", {
|
|
||||||
error: err.message
|
|
||||||
}) : false;
|
|
||||||
}
|
|
||||||
} else if (ptype === 'msg' || ptype === 'flow' || ptype === 'global' ) {
|
|
||||||
if (RED.utils.validatePropertyExpression(v)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (opt && opt.label) {
|
|
||||||
return RED._("validator.errors.invalid-prop-prop", {
|
|
||||||
prop: opt.label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return opt ? RED._("validator.errors.invalid-prop") : false;
|
|
||||||
} else if (ptype === 'num') {
|
|
||||||
if (/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (opt && opt.label) {
|
|
||||||
return RED._("validator.errors.invalid-num-prop", {
|
|
||||||
prop: opt.label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return opt ? RED._("validator.errors.invalid-num") : false;
|
|
||||||
}
|
}
|
||||||
return true;
|
if (options.allowBlank && v === '') {
|
||||||
};
|
return true
|
||||||
|
}
|
||||||
|
if (options.allowUndefined && v === undefined) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const result = RED.utils.validateTypedProperty(v, ptype, opt)
|
||||||
|
if (result === true || opt) {
|
||||||
|
// Valid, or opt provided - return result as-is
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
// No opt - need to return false for backwards compatibilty
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -114,6 +114,7 @@
|
|||||||
pointer-events: stroke;
|
pointer-events: stroke;
|
||||||
}
|
}
|
||||||
.red-ui-flow-group-outline-select {
|
.red-ui-flow-group-outline-select {
|
||||||
|
cursor: move;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: var(--red-ui-node-selected-color);
|
stroke: var(--red-ui-node-selected-color);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -194,10 +194,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.red-ui-clipboard-dialog-import-conflicts-controls {
|
.red-ui-clipboard-dialog-import-conflicts-controls {
|
||||||
position: absolute;
|
|
||||||
top:0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--red-ui-form-text-color);
|
color: var(--red-ui-form-text-color);
|
||||||
.form-row & label {
|
.form-row & label {
|
||||||
@ -218,9 +214,21 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#red-ui-clipboard-dialog-import-conflicts-list .disabled .red-ui-info-outline-item {
|
#red-ui-clipboard-dialog-import-conflicts-list .disabled {
|
||||||
opacity: 0.4;
|
.red-ui-info-outline-item,
|
||||||
|
.red-ui-node-list-item {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#red-ui-clipboard-dialog-import-conflicts-list .red-ui-node-list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > :first-child {
|
||||||
|
flex-grow: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-row label.red-ui-clipboard-dialog-import-conflicts-gutter {
|
.form-row label.red-ui-clipboard-dialog-import-conflicts-gutter {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
|
@ -825,6 +825,7 @@ div.red-ui-projects-dialog-ssh-public-key {
|
|||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
border-radius: 3px 3px 0px 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ ul.red-ui-sidebar-node-config-list {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.red-ui-palette-node {
|
.red-ui-palette-node {
|
||||||
overflow: hidden;
|
// overflow: hidden;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
&.selected {
|
&.selected {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
@ -113,6 +113,15 @@ ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type {
|
|||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.red-ui-palette-node-config-invalid {
|
||||||
|
border-color: var(--red-ui-form-input-border-error-color)
|
||||||
|
}
|
||||||
|
.red-ui-palette-node-annotations {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(100% - 15px);
|
||||||
|
top: -8px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
.red-ui-sidebar-node-config-filter-info {
|
.red-ui-sidebar-node-config-filter-info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -22,26 +22,26 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<title>{{ page.title }}</title>
|
<title>{{ page.title }}</title>
|
||||||
<link rel="icon" type="image/png" href="{{ page.favicon }}">
|
<link rel="icon" type="image/png" href="{{{ page.favicon }}}">
|
||||||
<link rel="mask-icon" href="{{ page.tabicon.icon }}" color="{{ page.tabicon.colour }}">
|
<link rel="mask-icon" href="{{{ page.tabicon.icon }}}" color="{{ page.tabicon.colour }}">
|
||||||
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css?v={{ page.version }}">
|
<link rel="stylesheet" href="vendor/jquery/css/base/jquery-ui.min.css?v={{ cacheBuster }}">
|
||||||
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css?v={{ page.version }}">
|
<link rel="stylesheet" href="vendor/font-awesome/css/font-awesome.min.css?v={{ cacheBuster }}">
|
||||||
<link rel="stylesheet" href="red/style.min.css?v={{ page.version }}">
|
<link rel="stylesheet" href="red/style.min.css?v={{ cacheBuster }}">
|
||||||
{{#page.css}}
|
{{#page.css}}
|
||||||
<link rel="stylesheet" href="{{.}}">
|
<link rel="stylesheet" href="{{.}}">
|
||||||
{{/page.css}}
|
{{/page.css}}
|
||||||
{{#asset.vendorMonaco}}
|
{{#asset.vendorMonaco}}
|
||||||
<link rel="stylesheet" href="vendor/monaco/style.css?v={{ page.version }}">
|
<link rel="stylesheet" href="vendor/monaco/style.css?v={{ cacheBuster }}">
|
||||||
{{/asset.vendorMonaco}}
|
{{/asset.vendorMonaco}}
|
||||||
</head>
|
</head>
|
||||||
<body spellcheck="false">
|
<body spellcheck="false">
|
||||||
<div id="red-ui-editor"></div>
|
<div id="red-ui-editor"></div>
|
||||||
<script src="vendor/vendor.js?v={{ page.version }}"></script>
|
<script src="vendor/vendor.js?v={{ cacheBuster }}"></script>
|
||||||
{{#asset.vendorMonaco}}
|
{{#asset.vendorMonaco}}
|
||||||
<script src="{{ asset.vendorMonaco }}?v={{ page.version }}"></script>
|
<script src="{{{ asset.vendorMonaco }}}?v={{ cacheBuster }}"></script>
|
||||||
{{/asset.vendorMonaco}}
|
{{/asset.vendorMonaco}}
|
||||||
<script src="{{ asset.red }}?v={{ page.version }}"></script>
|
<script src="{{{ asset.red }}}?v={{ cacheBuster }}"></script>
|
||||||
<script src="{{ asset.main }}?v={{ page.version }}"></script>
|
<script src="{{{ asset.main }}}?v={{ cacheBuster }}"></script>
|
||||||
{{# page.scripts }}
|
{{# page.scripts }}
|
||||||
<script src="{{.}}"></script>
|
<script src="{{.}}"></script>
|
||||||
{{/ page.scripts }}
|
{{/ page.scripts }}
|
||||||
|
@ -320,7 +320,7 @@
|
|||||||
}
|
}
|
||||||
// but replace with repeat one if set to repeat
|
// but replace with repeat one if set to repeat
|
||||||
if ((this.repeat && this.repeat != 0) || this.crontab) {
|
if ((this.repeat && this.repeat != 0) || this.crontab) {
|
||||||
suffix = " ↻";
|
suffix = "\t↻";
|
||||||
}
|
}
|
||||||
if (this.name) {
|
if (this.name) {
|
||||||
return this.name+suffix;
|
return this.name+suffix;
|
||||||
|
@ -109,9 +109,8 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
const p = props.shift()
|
const p = props.shift()
|
||||||
const property = p.p;
|
const property = p.p;
|
||||||
const value = p.v ? p.v : '';
|
const value = p.v !== undefined ? p.v : '';
|
||||||
const valueType = p.vt ? p.vt : 'str';
|
const valueType = p.vt !== undefined ? p.vt : 'str';
|
||||||
|
|
||||||
if (property) {
|
if (property) {
|
||||||
if (valueType === "jsonata") {
|
if (valueType === "jsonata") {
|
||||||
if (p.v) {
|
if (p.v) {
|
||||||
|
@ -86,7 +86,7 @@
|
|||||||
},
|
},
|
||||||
label: function() {
|
label: function() {
|
||||||
var suffix = "";
|
var suffix = "";
|
||||||
if (this.console === true || this.console === "true") { suffix = " ⇲"; }
|
if (this.console === true || this.console === "true") { suffix = "\t⇲"; }
|
||||||
if (this.targetType === "jsonata") {
|
if (this.targetType === "jsonata") {
|
||||||
return (this.name || "JSONata") + suffix;
|
return (this.name || "JSONata") + suffix;
|
||||||
}
|
}
|
||||||
@ -195,6 +195,119 @@
|
|||||||
node.dirty = true;
|
node.dirty = true;
|
||||||
});
|
});
|
||||||
RED.view.redraw();
|
RED.view.redraw();
|
||||||
|
},
|
||||||
|
requestDebugNodeList: function(filteredNodes) {
|
||||||
|
var workspaceOrder = RED.nodes.getWorkspaceOrder();
|
||||||
|
var workspaceOrderMap = {};
|
||||||
|
workspaceOrder.forEach(function(ws,i) {
|
||||||
|
workspaceOrderMap[ws] = i;
|
||||||
|
});
|
||||||
|
|
||||||
|
var candidateNodes = [];
|
||||||
|
var candidateSFs = [];
|
||||||
|
var subflows = {};
|
||||||
|
RED.nodes.eachNode(function (n) {
|
||||||
|
var nt = n.type;
|
||||||
|
if (nt === "debug") {
|
||||||
|
if (n.z in workspaceOrderMap) {
|
||||||
|
candidateNodes.push(n);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var sf = RED.nodes.subflow(n.z);
|
||||||
|
if (sf) {
|
||||||
|
subflows[sf.id] = {
|
||||||
|
debug: true,
|
||||||
|
subflows: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(nt.substring(0, 8) === "subflow:") {
|
||||||
|
if (n.z in workspaceOrderMap) {
|
||||||
|
candidateSFs.push(n);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var psf = RED.nodes.subflow(n.z);
|
||||||
|
if (psf) {
|
||||||
|
var sid = nt.substring(8);
|
||||||
|
var item = subflows[psf.id];
|
||||||
|
if (!item) {
|
||||||
|
item = {
|
||||||
|
debug: undefined,
|
||||||
|
subflows: {}
|
||||||
|
};
|
||||||
|
subflows[psf.id] = item;
|
||||||
|
}
|
||||||
|
item.subflows[sid] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
candidateSFs.forEach(function (sf) {
|
||||||
|
var sid = sf.type.substring(8);
|
||||||
|
if (containsDebug(sid, subflows)) {
|
||||||
|
candidateNodes.push(sf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
candidateNodes.sort(function(A,B) {
|
||||||
|
var wsA = workspaceOrderMap[A.z];
|
||||||
|
var wsB = workspaceOrderMap[B.z];
|
||||||
|
if (wsA !== wsB) {
|
||||||
|
return wsA-wsB;
|
||||||
|
}
|
||||||
|
var labelA = RED.utils.getNodeLabel(A,A.id);
|
||||||
|
var labelB = RED.utils.getNodeLabel(B,B.id);
|
||||||
|
return labelA.localeCompare(labelB);
|
||||||
|
});
|
||||||
|
var currentWs = null;
|
||||||
|
var data = [];
|
||||||
|
var currentFlow;
|
||||||
|
var currentSelectedCount = 0;
|
||||||
|
candidateNodes.forEach(function(node) {
|
||||||
|
if (currentWs !== node.z) {
|
||||||
|
if (currentFlow && currentFlow.checkbox) {
|
||||||
|
currentFlow.selected = currentSelectedCount === currentFlow.children.length
|
||||||
|
}
|
||||||
|
currentSelectedCount = 0;
|
||||||
|
currentWs = node.z;
|
||||||
|
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
|
||||||
|
currentFlow = {
|
||||||
|
label: RED.utils.getNodeLabel(parent, currentWs),
|
||||||
|
}
|
||||||
|
if (!parent.disabled) {
|
||||||
|
currentFlow.children = [];
|
||||||
|
currentFlow.checkbox = true;
|
||||||
|
} else {
|
||||||
|
currentFlow.class = "disabled"
|
||||||
|
}
|
||||||
|
data.push(currentFlow);
|
||||||
|
}
|
||||||
|
if (currentFlow.children) {
|
||||||
|
if (!filteredNodes[node.id]) {
|
||||||
|
currentSelectedCount++;
|
||||||
|
}
|
||||||
|
currentFlow.children.push({
|
||||||
|
label: RED.utils.getNodeLabel(node,node.id),
|
||||||
|
node: {
|
||||||
|
id: node.id
|
||||||
|
},
|
||||||
|
checkbox: true,
|
||||||
|
selected: !filteredNodes[node.id]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentFlow && currentFlow.checkbox) {
|
||||||
|
currentFlow.selected = currentSelectedCount === currentFlow.children.length
|
||||||
|
}
|
||||||
|
if (subWindow) {
|
||||||
|
try {
|
||||||
|
subWindow.postMessage({event:"refreshDebugNodeList", nodes:data},"*");
|
||||||
|
} catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RED.debug.refreshDebugNodeList(data)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -396,6 +509,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function containsDebug(sid, map) {
|
||||||
|
var item = map[sid];
|
||||||
|
if (item) {
|
||||||
|
if (item.debug === undefined) {
|
||||||
|
var sfs = Object.keys(item.subflows);
|
||||||
|
var contain = false;
|
||||||
|
for (var i = 0; i < sfs.length; i++) {
|
||||||
|
var sf = sfs[i];
|
||||||
|
if (containsDebug(sf, map)) {
|
||||||
|
contain = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.debug = contain;
|
||||||
|
}
|
||||||
|
return item.debug;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$("#red-ui-sidebar-debug-open").on("click", function(e) {
|
$("#red-ui-sidebar-debug-open").on("click", function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
|
subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600");
|
||||||
@ -427,6 +560,8 @@
|
|||||||
options.messageSourceClick(msg.id,msg._alias,msg.path);
|
options.messageSourceClick(msg.id,msg._alias,msg.path);
|
||||||
} else if (msg.event === "clear") {
|
} else if (msg.event === "clear") {
|
||||||
options.clear();
|
options.clear();
|
||||||
|
} else if (msg.event === "requestDebugNodeList") {
|
||||||
|
options.requestDebugNodeList(msg.filteredNodes)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener('message',this.handleWindowMessage);
|
window.addEventListener('message',this.handleWindowMessage);
|
||||||
|
@ -5,6 +5,7 @@ module.exports = function(RED) {
|
|||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
var debuglength = RED.settings.debugMaxLength || 1000;
|
var debuglength = RED.settings.debugMaxLength || 1000;
|
||||||
|
var statuslength = RED.settings.debugStatusLength || 32;
|
||||||
var useColors = RED.settings.debugUseColors || false;
|
var useColors = RED.settings.debugUseColors || false;
|
||||||
util.inspect.styles.boolean = "red";
|
util.inspect.styles.boolean = "red";
|
||||||
const { hasOwnProperty } = Object.prototype;
|
const { hasOwnProperty } = Object.prototype;
|
||||||
@ -164,7 +165,7 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.length > 32) { st = st.substr(0,32) + "..."; }
|
if (st.length > statuslength) { st = st.substr(0,statuslength) + "..."; }
|
||||||
|
|
||||||
var newStatus = {fill:fill, shape:shape, text:st};
|
var newStatus = {fill:fill, shape:shape, text:st};
|
||||||
if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to
|
if (JSON.stringify(newStatus) !== node.oldState) { // only send if we have to
|
||||||
|
@ -275,7 +275,7 @@
|
|||||||
value: [],
|
value: [],
|
||||||
type: "link in[]",
|
type: "link in[]",
|
||||||
validate: function (v, opt) {
|
validate: function (v, opt) {
|
||||||
if ((this.linkType === "static" && v.length > 0)
|
if (((this.linkType || "static") === "static" && v.length > 0)
|
||||||
|| this.linkType === "dynamic") {
|
|| this.linkType === "dynamic") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -167,19 +167,13 @@ RED.debug = (function() {
|
|||||||
var menu = RED.popover.menu({
|
var menu = RED.popover.menu({
|
||||||
options: options,
|
options: options,
|
||||||
onselect: function(item) {
|
onselect: function(item) {
|
||||||
if (item.value !== filterType) {
|
setFilterType(item.value)
|
||||||
filterType = item.value;
|
|
||||||
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
|
|
||||||
refreshMessageList();
|
|
||||||
RED.settings.set("debug.filter",filterType)
|
|
||||||
}
|
|
||||||
if (filterType === 'filterSelected') {
|
if (filterType === 'filterSelected') {
|
||||||
refreshDebugNodeList();
|
config.requestDebugNodeList(filteredNodes);
|
||||||
filterDialog.slideDown(200);
|
filterDialog.slideDown(200);
|
||||||
filterDialogShown = true;
|
filterDialogShown = true;
|
||||||
debugNodeTreeList.focus();
|
debugNodeTreeList.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menu.show({
|
menu.show({
|
||||||
@ -254,131 +248,7 @@ RED.debug = (function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshDebugNodeList(data) {
|
||||||
function containsDebug(sid, map) {
|
|
||||||
var item = map[sid];
|
|
||||||
if (item) {
|
|
||||||
if (item.debug === undefined) {
|
|
||||||
var sfs = Object.keys(item.subflows);
|
|
||||||
var contain = false;
|
|
||||||
for (var i = 0; i < sfs.length; i++) {
|
|
||||||
var sf = sfs[i];
|
|
||||||
if (containsDebug(sf, map)) {
|
|
||||||
contain = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.debug = contain;
|
|
||||||
}
|
|
||||||
return item.debug;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function refreshDebugNodeList() {
|
|
||||||
var workspaceOrder = RED.nodes.getWorkspaceOrder();
|
|
||||||
var workspaceOrderMap = {};
|
|
||||||
workspaceOrder.forEach(function(ws,i) {
|
|
||||||
workspaceOrderMap[ws] = i;
|
|
||||||
});
|
|
||||||
|
|
||||||
var candidateNodes = [];
|
|
||||||
var candidateSFs = [];
|
|
||||||
var subflows = {};
|
|
||||||
RED.nodes.eachNode(function (n) {
|
|
||||||
var nt = n.type;
|
|
||||||
if (nt === "debug") {
|
|
||||||
if (n.z in workspaceOrderMap) {
|
|
||||||
candidateNodes.push(n);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var sf = RED.nodes.subflow(n.z);
|
|
||||||
if (sf) {
|
|
||||||
subflows[sf.id] = {
|
|
||||||
debug: true,
|
|
||||||
subflows: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(nt.substring(0, 8) === "subflow:") {
|
|
||||||
if (n.z in workspaceOrderMap) {
|
|
||||||
candidateSFs.push(n);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var psf = RED.nodes.subflow(n.z);
|
|
||||||
if (psf) {
|
|
||||||
var sid = nt.substring(8);
|
|
||||||
var item = subflows[psf.id];
|
|
||||||
if (!item) {
|
|
||||||
item = {
|
|
||||||
debug: undefined,
|
|
||||||
subflows: {}
|
|
||||||
};
|
|
||||||
subflows[psf.id] = item;
|
|
||||||
}
|
|
||||||
item.subflows[sid] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
candidateSFs.forEach(function (sf) {
|
|
||||||
var sid = sf.type.substring(8);
|
|
||||||
if (containsDebug(sid, subflows)) {
|
|
||||||
candidateNodes.push(sf);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
candidateNodes.sort(function(A,B) {
|
|
||||||
var wsA = workspaceOrderMap[A.z];
|
|
||||||
var wsB = workspaceOrderMap[B.z];
|
|
||||||
if (wsA !== wsB) {
|
|
||||||
return wsA-wsB;
|
|
||||||
}
|
|
||||||
var labelA = RED.utils.getNodeLabel(A,A.id);
|
|
||||||
var labelB = RED.utils.getNodeLabel(B,B.id);
|
|
||||||
return labelA.localeCompare(labelB);
|
|
||||||
});
|
|
||||||
var currentWs = null;
|
|
||||||
var data = [];
|
|
||||||
var currentFlow;
|
|
||||||
var currentSelectedCount = 0;
|
|
||||||
candidateNodes.forEach(function(node) {
|
|
||||||
if (currentWs !== node.z) {
|
|
||||||
if (currentFlow && currentFlow.checkbox) {
|
|
||||||
currentFlow.selected = currentSelectedCount === currentFlow.children.length
|
|
||||||
}
|
|
||||||
currentSelectedCount = 0;
|
|
||||||
currentWs = node.z;
|
|
||||||
var parent = RED.nodes.workspace(currentWs) || RED.nodes.subflow(currentWs);
|
|
||||||
currentFlow = {
|
|
||||||
label: RED.utils.getNodeLabel(parent, currentWs),
|
|
||||||
}
|
|
||||||
if (!parent.disabled) {
|
|
||||||
currentFlow.children = [];
|
|
||||||
currentFlow.checkbox = true;
|
|
||||||
} else {
|
|
||||||
currentFlow.class = "disabled"
|
|
||||||
}
|
|
||||||
data.push(currentFlow);
|
|
||||||
}
|
|
||||||
if (currentFlow.children) {
|
|
||||||
if (!filteredNodes[node.id]) {
|
|
||||||
currentSelectedCount++;
|
|
||||||
}
|
|
||||||
currentFlow.children.push({
|
|
||||||
label: RED.utils.getNodeLabel(node,node.id),
|
|
||||||
node: node,
|
|
||||||
checkbox: true,
|
|
||||||
selected: !filteredNodes[node.id]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (currentFlow && currentFlow.checkbox) {
|
|
||||||
currentFlow.selected = currentSelectedCount === currentFlow.children.length
|
|
||||||
}
|
|
||||||
|
|
||||||
debugNodeTreeList.treeList("data", data);
|
debugNodeTreeList.treeList("data", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +271,7 @@ RED.debug = (function() {
|
|||||||
},200);
|
},200);
|
||||||
}
|
}
|
||||||
function _refreshMessageList(_activeWorkspace) {
|
function _refreshMessageList(_activeWorkspace) {
|
||||||
if (_activeWorkspace) {
|
if (typeof _activeWorkspace === 'string') {
|
||||||
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
|
activeWorkspace = _activeWorkspace.replace(/\./g,"_");
|
||||||
}
|
}
|
||||||
if (filterType === "filterAll") {
|
if (filterType === "filterAll") {
|
||||||
@ -479,12 +349,12 @@ RED.debug = (function() {
|
|||||||
filteredNodes[n.id] = true;
|
filteredNodes[n.id] = true;
|
||||||
});
|
});
|
||||||
delete filteredNodes[sourceId];
|
delete filteredNodes[sourceId];
|
||||||
$("#red-ui-sidebar-debug-filterSelected").trigger("click");
|
|
||||||
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
|
RED.settings.set('debug.filteredNodes',Object.keys(filteredNodes))
|
||||||
|
setFilterType('filterSelected')
|
||||||
refreshMessageList();
|
refreshMessageList();
|
||||||
}},
|
}},
|
||||||
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
|
{id:"red-ui-debug-msg-menu-item-clear-filter",label:RED._("node-red:debug.messageMenu.clearFilter"),onselect:function(){
|
||||||
$("#red-ui-sidebar-debug-filterAll").trigger("click");
|
clearFilterSettings()
|
||||||
refreshMessageList();
|
refreshMessageList();
|
||||||
}}
|
}}
|
||||||
);
|
);
|
||||||
@ -713,9 +583,17 @@ RED.debug = (function() {
|
|||||||
if (!!clearFilter) {
|
if (!!clearFilter) {
|
||||||
clearFilterSettings();
|
clearFilterSettings();
|
||||||
}
|
}
|
||||||
refreshDebugNodeList();
|
config.requestDebugNodeList(filteredNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFilterType(type) {
|
||||||
|
if (type !== filterType) {
|
||||||
|
filterType = type;
|
||||||
|
$('#red-ui-sidebar-debug-filter span').text(RED._('node-red:debug.sidebar.'+filterType));
|
||||||
|
refreshMessageList();
|
||||||
|
RED.settings.set("debug.filter",filterType)
|
||||||
|
}
|
||||||
|
}
|
||||||
function clearFilterSettings() {
|
function clearFilterSettings() {
|
||||||
filteredNodes = {};
|
filteredNodes = {};
|
||||||
filterType = 'filterAll';
|
filterType = 'filterAll';
|
||||||
@ -728,6 +606,7 @@ RED.debug = (function() {
|
|||||||
init: init,
|
init: init,
|
||||||
refreshMessageList:refreshMessageList,
|
refreshMessageList:refreshMessageList,
|
||||||
handleDebugMessage: handleDebugMessage,
|
handleDebugMessage: handleDebugMessage,
|
||||||
clearMessageList: clearMessageList
|
clearMessageList: clearMessageList,
|
||||||
|
refreshDebugNodeList: refreshDebugNodeList
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -12,6 +12,9 @@ $(function() {
|
|||||||
},
|
},
|
||||||
clear: function() {
|
clear: function() {
|
||||||
window.opener.postMessage({event:"clear"},'*');
|
window.opener.postMessage({event:"clear"},'*');
|
||||||
|
},
|
||||||
|
requestDebugNodeList: function(filteredNodes) {
|
||||||
|
window.opener.postMessage({event: 'requestDebugNodeList', filteredNodes},'*')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +29,8 @@ $(function() {
|
|||||||
RED.debug.refreshMessageList(evt.data.activeWorkspace);
|
RED.debug.refreshMessageList(evt.data.activeWorkspace);
|
||||||
} else if (evt.data.event === "projectChange") {
|
} else if (evt.data.event === "projectChange") {
|
||||||
RED.debug.clearMessageList(true);
|
RED.debug.clearMessageList(true);
|
||||||
|
} else if (evt.data.event === "refreshDebugNodeList") {
|
||||||
|
RED.debug.refreshDebugNodeList(evt.data.nodes)
|
||||||
}
|
}
|
||||||
},false);
|
},false);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -315,7 +315,7 @@ module.exports = function(RED) {
|
|||||||
var spec = module.module;
|
var spec = module.module;
|
||||||
if (spec && (spec !== "")) {
|
if (spec && (spec !== "")) {
|
||||||
moduleLoadPromises.push(RED.import(module.module).then(lib => {
|
moduleLoadPromises.push(RED.import(module.module).then(lib => {
|
||||||
sandbox[vname] = lib.default;
|
sandbox[vname] = lib.default || lib;
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()}))
|
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()}))
|
||||||
throw err;
|
throw err;
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
} else if (type === "istype") {
|
} else if (type === "istype") {
|
||||||
r.v = rule.find(".node-input-rule-type-value").typedInput('type');
|
r.v = rule.find(".node-input-rule-type-value").typedInput('type');
|
||||||
r.vt = rule.find(".node-input-rule-type-value").typedInput('type');
|
r.vt = rule.find(".node-input-rule-type-value").typedInput('type');
|
||||||
r.vt = (r.vt === "number") ? "num" : "str";
|
|
||||||
} else if (type === "jsonata_exp") {
|
} else if (type === "jsonata_exp") {
|
||||||
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
|
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
|
||||||
r.vt = rule.find(".node-input-rule-exp-value").typedInput('type');
|
r.vt = rule.find(".node-input-rule-exp-value").typedInput('type');
|
||||||
@ -168,7 +167,35 @@
|
|||||||
label:RED._("node-red:common.label.payload"),
|
label:RED._("node-red:common.label.payload"),
|
||||||
validate: RED.validators.typedInput("propertyType", false)},
|
validate: RED.validators.typedInput("propertyType", false)},
|
||||||
propertyType: { value:"msg" },
|
propertyType: { value:"msg" },
|
||||||
rules: {value:[{t:"eq", v:"", vt:"str"}]},
|
rules: {
|
||||||
|
value:[{t:"eq", v:"", vt:"str"}],
|
||||||
|
validate: function (rules, opt) {
|
||||||
|
let msg;
|
||||||
|
const errors = []
|
||||||
|
if (!rules || rules.length === 0) { return true }
|
||||||
|
for (var i=0;i<rules.length;i++) {
|
||||||
|
const opt = { label: RED._('node-red:switch.label.rule')+' '+(i+1) }
|
||||||
|
const r = rules[i];
|
||||||
|
if (r.t !== 'istype') {
|
||||||
|
if (r.hasOwnProperty('v')) {
|
||||||
|
if ((msg = RED.utils.validateTypedProperty(r.v,r.vt,opt)) !== true) {
|
||||||
|
errors.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.hasOwnProperty('v2')) {
|
||||||
|
if ((msg = RED.utils.validateTypedProperty(r.v2,r.v2t,opt)) !== true) {
|
||||||
|
errors.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errors.length) {
|
||||||
|
console.log(errors)
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
checkall: {value:"true", required:true},
|
checkall: {value:"true", required:true},
|
||||||
repair: {value:false},
|
repair: {value:false},
|
||||||
outputs: {value:1}
|
outputs: {value:1}
|
||||||
@ -218,7 +245,11 @@
|
|||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1);
|
var lastRule = $("#node-input-rule-container").editableList('getItemAt',i-1);
|
||||||
var exportedRule = exportRule(lastRule.element);
|
var exportedRule = exportRule(lastRule.element);
|
||||||
opt.r.vt = exportedRule.vt;
|
if (exportedRule.t === "istype") {
|
||||||
|
opt.r.vt = (exportedRule.vt === "number") ? "num" : "str";
|
||||||
|
} else {
|
||||||
|
opt.r.vt = exportedRule.vt;
|
||||||
|
}
|
||||||
opt.r.v = "";
|
opt.r.v = "";
|
||||||
// We could copy the value over as well and preselect it (see the 'activeElement' code below)
|
// We could copy the value over as well and preselect it (see the 'activeElement' code below)
|
||||||
// But not sure that feels right. Is copying over the last value 'expected' behaviour?
|
// But not sure that feels right. Is copying over the last value 'expected' behaviour?
|
||||||
|
@ -19,71 +19,42 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
(function() {
|
(function() {
|
||||||
function isInvalidProperty(v,vt) {
|
|
||||||
if (/msg|flow|global/.test(vt)) {
|
|
||||||
if (!RED.utils.validatePropertyExpression(v)) {
|
|
||||||
return RED._("node-red:change.errors.invalid-prop", {
|
|
||||||
property: v
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (vt === "jsonata") {
|
|
||||||
try{ jsonata(v); } catch(e) {
|
|
||||||
return RED._("node-red:change.errors.invalid-expr", {
|
|
||||||
error: e.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (vt === "json") {
|
|
||||||
try{ JSON.parse(v); } catch(e) {
|
|
||||||
return RED._("node-red:change.errors.invalid-json-data", {
|
|
||||||
error: e.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RED.nodes.registerType('change', {
|
RED.nodes.registerType('change', {
|
||||||
color: "#E2D96E",
|
color: "#E2D96E",
|
||||||
category: 'function',
|
category: 'function',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
rules:{value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],validate: function(rules, opt) {
|
rules:{
|
||||||
var msg;
|
value:[{t:"set",p:"payload",pt:"msg",to:"",tot:"str"}],
|
||||||
if (!rules || rules.length === 0) { return true }
|
validate: function(rules, opt) {
|
||||||
for (var i=0;i<rules.length;i++) {
|
let msg;
|
||||||
var r = rules[i];
|
const errors = []
|
||||||
if (r.t === 'set') {
|
if (!rules || rules.length === 0) { return true }
|
||||||
if (msg = isInvalidProperty(r.p,r.pt)) {
|
for (var i=0;i<rules.length;i++) {
|
||||||
return msg;
|
const opt = { label: RED._('node-red:change.label.rule')+' '+(i+1) }
|
||||||
|
const r = rules[i];
|
||||||
|
if (r.t === 'set' || r.t === 'change' || r.t === 'delete' || r.t === 'move') {
|
||||||
|
if ((msg = RED.utils.validateTypedProperty(r.p,r.pt,opt)) !== true) {
|
||||||
|
errors.push(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (msg = isInvalidProperty(r.to,r.tot)) {
|
if (r.t === 'set' || r.t === 'change' || r.t === 'move') {
|
||||||
return msg;
|
if ((msg = RED.utils.validateTypedProperty(r.to,r.tot,opt)) !== true) {
|
||||||
|
errors.push(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (r.t === 'change') {
|
if (r.t === 'change') {
|
||||||
if (msg = isInvalidProperty(r.p,r.pt)) {
|
if ((msg = RED.utils.validateTypedProperty(r.from,r.fromt,opt)) !== true) {
|
||||||
return msg;
|
errors.push(msg)
|
||||||
}
|
}
|
||||||
if(msg = isInvalidProperty(r.from,r.fromt)) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
if(msg = isInvalidProperty(r.to,r.tot)) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
} else if (r.t === 'delete') {
|
|
||||||
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
} else if (r.t === 'move') {
|
|
||||||
if (msg = isInvalidProperty(r.p,r.pt)) {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
if (msg = isInvalidProperty(r.to,r.tot)) {
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (errors.length) {
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
},
|
||||||
}},
|
|
||||||
// legacy
|
// legacy
|
||||||
action: {value:""},
|
action: {value:""},
|
||||||
property: {value:""},
|
property: {value:""},
|
||||||
|
@ -117,7 +117,7 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
return
|
return
|
||||||
} else if (rule.tot === 'date') {
|
} else if (rule.tot === 'date') {
|
||||||
value = Date.now();
|
value = RED.util.evaluateNodeProperty(rule.to, rule.tot, node)
|
||||||
} else if (rule.tot === 'jsonata') {
|
} else if (rule.tot === 'jsonata') {
|
||||||
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
|
RED.util.evaluateJSONataExpression(rule.to,msg, (err, value) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -233,7 +233,9 @@ module.exports = function(RED) {
|
|||||||
// only replace if they match exactly
|
// only replace if they match exactly
|
||||||
RED.util.setMessageProperty(msg,property,value);
|
RED.util.setMessageProperty(msg,property,value);
|
||||||
} else {
|
} else {
|
||||||
current = current.replace(fromRE,value);
|
// if target is boolean then just replace it
|
||||||
|
if (rule.tot === "bool") { current = value; }
|
||||||
|
else { current = current.replace(fromRE,value); }
|
||||||
RED.util.setMessageProperty(msg,property,current);
|
RED.util.setMessageProperty(msg,property,current);
|
||||||
}
|
}
|
||||||
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
} else if ((typeof current === 'number' || current instanceof Number) && fromType === 'num') {
|
||||||
|
@ -57,7 +57,9 @@
|
|||||||
action: {value:"scale"},
|
action: {value:"scale"},
|
||||||
round: {value:false},
|
round: {value:false},
|
||||||
property: {value:"payload",required:true,
|
property: {value:"payload",required:true,
|
||||||
label:RED._("node-red:common.label.property")},
|
label:RED._("node-red:common.label.property"),
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })
|
||||||
|
},
|
||||||
name: {value:""}
|
name: {value:""}
|
||||||
},
|
},
|
||||||
inputs: 1,
|
inputs: 1,
|
||||||
|
@ -153,7 +153,7 @@
|
|||||||
}
|
}
|
||||||
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
||||||
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
|
||||||
$(".node-text-editor").css("height",height+"px");
|
$("#dialog-form .node-text-editor").css("height",height+"px");
|
||||||
this.editor.resize();
|
this.editor.resize();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -284,7 +284,7 @@ module.exports = function(RED) {
|
|||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (!msg.hasOwnProperty("reset")) {
|
||||||
if (maxKeptMsgsCount(node) > 0) {
|
if (maxKeptMsgsCount(node) > 0) {
|
||||||
if (node.intervalID === -1) {
|
if (node.intervalID === -1) {
|
||||||
node.send(msg);
|
node.send(msg);
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
color:"darksalmon",
|
color:"darksalmon",
|
||||||
defaults: {
|
defaults: {
|
||||||
command: {value:""},
|
command: {value:""},
|
||||||
addpay: {value:""},
|
addpay: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
|
||||||
append: {value:""},
|
append: {value:""},
|
||||||
useSpawn: {value:"false"},
|
useSpawn: {value:"false"},
|
||||||
timer: {value:""},
|
timer: {value:""},
|
||||||
|
@ -56,9 +56,11 @@
|
|||||||
inout: {value:"out"},
|
inout: {value:"out"},
|
||||||
septopics: {value:true},
|
septopics: {value:true},
|
||||||
property: {value:"payload", required:true,
|
property: {value:"payload", required:true,
|
||||||
label:RED._("node-red:rbe.label.property")},
|
label:RED._("node-red:rbe.label.property"),
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
|
||||||
topi: {value:"topic", required:true,
|
topi: {value:"topic", required:true,
|
||||||
label:RED._("node-red:rbe.label.topic")}
|
label:RED._("node-red:rbe.label.topic"),
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:1,
|
outputs:1,
|
||||||
|
@ -104,6 +104,7 @@ module.exports = function(RED) {
|
|||||||
* @returns `true` if it is a valid topic
|
* @returns `true` if it is a valid topic
|
||||||
*/
|
*/
|
||||||
function isValidPublishTopic(topic) {
|
function isValidPublishTopic(topic) {
|
||||||
|
if (topic.length === 0) return false;
|
||||||
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
|
return !/[\+#\b\f\n\r\t\v\0]/.test(topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +220,8 @@ module.exports = function(RED) {
|
|||||||
*/
|
*/
|
||||||
function subscriptionHandler(node, datatype ,topic, payload, packet) {
|
function subscriptionHandler(node, datatype ,topic, payload, packet) {
|
||||||
const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
|
const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
|
||||||
const v5 = (node && node.brokerConn)
|
const v5 = (node && node.brokerConn)
|
||||||
? node.brokerConn.v5()
|
? node.brokerConn.v5()
|
||||||
: Object.prototype.hasOwnProperty.call(packet, "properties");
|
: Object.prototype.hasOwnProperty.call(packet, "properties");
|
||||||
if(v5 && packet.properties) {
|
if(v5 && packet.properties) {
|
||||||
setStrProp(packet.properties, msg, "responseTopic");
|
setStrProp(packet.properties, msg, "responseTopic");
|
||||||
@ -451,7 +452,7 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the disconnect action
|
* Perform the disconnect action
|
||||||
* @param {MQTTInNode|MQTTOutNode} node
|
* @param {MQTTInNode|MQTTOutNode} node
|
||||||
* @param {Function} done
|
* @param {Function} done
|
||||||
*/
|
*/
|
||||||
function handleDisconnectAction(node, done) {
|
function handleDisconnectAction(node, done) {
|
||||||
@ -611,7 +612,7 @@ module.exports = function(RED) {
|
|||||||
node.brokerurl = node.url;
|
node.brokerurl = node.url;
|
||||||
} else {
|
} else {
|
||||||
// if the broker is ws:// or wss:// or tcp://
|
// if the broker is ws:// or wss:// or tcp://
|
||||||
if (node.broker.indexOf("://") > -1) {
|
if ((typeof node.broker === 'string') && node.broker.indexOf("://") > -1) {
|
||||||
node.brokerurl = node.broker;
|
node.brokerurl = node.broker;
|
||||||
// Only for ws or wss, check if proxy env var for additional configuration
|
// Only for ws or wss, check if proxy env var for additional configuration
|
||||||
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
|
if (node.brokerurl.indexOf("wss://") > -1 || node.brokerurl.indexOf("ws://") > -1) {
|
||||||
@ -865,7 +866,7 @@ module.exports = function(RED) {
|
|||||||
* Call end and wait for the client to end (or timeout)
|
* Call end and wait for the client to end (or timeout)
|
||||||
* @param {mqtt.MqttClient} client The broker client
|
* @param {mqtt.MqttClient} client The broker client
|
||||||
* @param {number} ms The time to wait for the client to end
|
* @param {number} ms The time to wait for the client to end
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
let waitEnd = (client, ms) => {
|
let waitEnd = (client, ms) => {
|
||||||
return new Promise( (resolve, reject) => {
|
return new Promise( (resolve, reject) => {
|
||||||
@ -905,7 +906,7 @@ module.exports = function(RED) {
|
|||||||
node.subid = 1;
|
node.subid = 1;
|
||||||
|
|
||||||
//typedef for subscription object:
|
//typedef for subscription object:
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Subscription
|
* @typedef {Object} Subscription
|
||||||
* @property {String} topic - topic to subscribe to
|
* @property {String} topic - topic to subscribe to
|
||||||
* @property {Object} [options] - options object
|
* @property {Object} [options] - options object
|
||||||
@ -933,7 +934,7 @@ module.exports = function(RED) {
|
|||||||
const ref = _ref || 0;
|
const ref = _ref || 0;
|
||||||
let options
|
let options
|
||||||
let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2)
|
let qos = 1 // default to QoS 1 (AWS and several other brokers don't support QoS 2)
|
||||||
|
|
||||||
// if options is an object, then clone it
|
// if options is an object, then clone it
|
||||||
if (typeof _options == "object") {
|
if (typeof _options == "object") {
|
||||||
options = RED.util.cloneMessage(_options || {})
|
options = RED.util.cloneMessage(_options || {})
|
||||||
@ -947,7 +948,7 @@ module.exports = function(RED) {
|
|||||||
if (typeof qos === "number" && qos >= 0 && qos <= 2) {
|
if (typeof qos === "number" && qos >= 0 && qos <= 2) {
|
||||||
options.qos = qos;
|
options.qos = qos;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscription.topic = _topic;
|
subscription.topic = _topic;
|
||||||
subscription.qos = qos;
|
subscription.qos = qos;
|
||||||
subscription.options = RED.util.cloneMessage(options);
|
subscription.options = RED.util.cloneMessage(options);
|
||||||
@ -957,16 +958,16 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If topic is a subscription object, then use that, otherwise look up the topic in
|
* If topic is a subscription object, then use that, otherwise look up the topic in
|
||||||
* the subscriptions object. If the topic is not found, then create a new subscription
|
* the subscriptions object. If the topic is not found, then create a new subscription
|
||||||
* object and add it to the subscriptions object.
|
* object and add it to the subscriptions object.
|
||||||
* @param {Subscription|String} topic
|
* @param {Subscription|String} topic
|
||||||
* @param {*} options
|
* @param {*} options
|
||||||
* @param {*} callback
|
* @param {*} callback
|
||||||
* @param {*} ref
|
* @param {*} ref
|
||||||
*/
|
*/
|
||||||
node.subscribe = function (topic, options, callback, ref) {
|
node.subscribe = function (topic, options, callback, ref) {
|
||||||
/** @type {Subscription} */
|
/** @type {Subscription} */
|
||||||
let subscription
|
let subscription
|
||||||
let doCompare = false
|
let doCompare = false
|
||||||
let changesFound = false
|
let changesFound = false
|
||||||
@ -1004,7 +1005,7 @@ module.exports = function(RED) {
|
|||||||
_brokerConn.unsubscribe(sub.topic, sub.ref, true)
|
_brokerConn.unsubscribe(sub.topic, sub.ref, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// if subscription is found (or sent in as a parameter), then check for changes.
|
// if subscription is found (or sent in as a parameter), then check for changes.
|
||||||
// if there are any changes requested, tidy up the old subscription
|
// if there are any changes requested, tidy up the old subscription
|
||||||
if (subscription) {
|
if (subscription) {
|
||||||
@ -1091,7 +1092,7 @@ module.exports = function(RED) {
|
|||||||
delete sub[ref]
|
delete sub[ref]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if instructed to remove the actual MQTT client subscription
|
// if instructed to remove the actual MQTT client subscription
|
||||||
if (unsub) {
|
if (unsub) {
|
||||||
// if there are no more subscriptions for the topic, then remove the topic
|
// if there are no more subscriptions for the topic, then remove the topic
|
||||||
if (Object.keys(sub).length === 0) {
|
if (Object.keys(sub).length === 0) {
|
||||||
|
@ -141,15 +141,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @param {Object} headersObject
|
|
||||||
* @param {string} name
|
|
||||||
* @return {any} value
|
|
||||||
*/
|
|
||||||
const getHeaderValue = (headersObject, name) => {
|
|
||||||
const asLowercase = name.toLowercase();
|
|
||||||
return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)];
|
|
||||||
}
|
|
||||||
this.on("input",function(msg,nodeSend,nodeDone) {
|
this.on("input",function(msg,nodeSend,nodeDone) {
|
||||||
checkNodeAgentPatch();
|
checkNodeAgentPatch();
|
||||||
//reset redirectList on each request
|
//reset redirectList on each request
|
||||||
@ -300,7 +292,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts.headers = {};
|
opts.headers = {};
|
||||||
//add msg.headers
|
//add msg.headers
|
||||||
//NOTE: ui headers will take precidence over msg.headers
|
//NOTE: ui headers will take precidence over msg.headers
|
||||||
if (msg.headers) {
|
if (msg.headers) {
|
||||||
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
|
if (msg.headers.hasOwnProperty('x-node-red-request-node')) {
|
||||||
@ -452,10 +444,6 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
formData.append(opt, val);
|
formData.append(opt, val);
|
||||||
} else if (typeof val === 'object' && val.hasOwnProperty('value')) {
|
} else if (typeof val === 'object' && val.hasOwnProperty('value')) {
|
||||||
formData.append(opt,val.value,val.options || {});
|
formData.append(opt,val.value,val.options || {});
|
||||||
} else if (Array.isArray(val)) {
|
|
||||||
for (var i=0; i<val.length; i++) {
|
|
||||||
formData.append(opt, val[i])
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
formData.append(opt,JSON.stringify(val));
|
formData.append(opt,JSON.stringify(val));
|
||||||
}
|
}
|
||||||
@ -637,7 +625,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
msg.payload = msg.payload.toString('utf8'); // txt
|
msg.payload = msg.payload.toString('utf8'); // txt
|
||||||
|
|
||||||
if (node.ret === "obj") {
|
if (node.ret === "obj") {
|
||||||
if (msg.statusCode == 204){msg.payload= "{}"};
|
if (msg.statusCode == 204){msg.payload= "{}"};
|
||||||
try { msg.payload = JSON.parse(msg.payload); } // obj
|
try { msg.payload = JSON.parse(msg.payload); } // obj
|
||||||
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
|
catch(e) { node.warn(RED._("httpin.errors.json-error")); }
|
||||||
}
|
}
|
||||||
@ -744,7 +732,7 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
|||||||
*
|
*
|
||||||
* If the algorithm directive's value ends with "-sess", then HA1 is
|
* If the algorithm directive's value ends with "-sess", then HA1 is
|
||||||
* HA1=digestCompute(digestCompute(username:realm:password):nonce:cnonce)
|
* HA1=digestCompute(digestCompute(username:realm:password):nonce:cnonce)
|
||||||
*
|
*
|
||||||
* If the algorithm directive's value does not end with "-sess", then HA1 is
|
* If the algorithm directive's value does not end with "-sess", then HA1 is
|
||||||
* HA1=digestCompute(username:realm:password)
|
* HA1=digestCompute(username:realm:password)
|
||||||
*/
|
*/
|
||||||
|
@ -411,23 +411,33 @@ module.exports = function(RED) {
|
|||||||
if (msg._session && msg._session.type == "tcp") {
|
if (msg._session && msg._session.type == "tcp") {
|
||||||
var client = connectionPool[msg._session.id];
|
var client = connectionPool[msg._session.id];
|
||||||
if (client) {
|
if (client) {
|
||||||
if (Buffer.isBuffer(msg.payload)) {
|
if (msg?.reset === true) {
|
||||||
client.write(msg.payload);
|
client.destroy();
|
||||||
} else if (typeof msg.payload === "string" && node.base64) {
|
}
|
||||||
client.write(Buffer.from(msg.payload,'base64'));
|
else {
|
||||||
} else {
|
if (Buffer.isBuffer(msg.payload)) {
|
||||||
client.write(Buffer.from(""+msg.payload));
|
client.write(msg.payload);
|
||||||
|
} else if (typeof msg.payload === "string" && node.base64) {
|
||||||
|
client.write(Buffer.from(msg.payload,'base64'));
|
||||||
|
} else {
|
||||||
|
client.write(Buffer.from(""+msg.payload));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (var i in connectionPool) {
|
for (var i in connectionPool) {
|
||||||
if (Buffer.isBuffer(msg.payload)) {
|
if (msg?.reset === true) {
|
||||||
connectionPool[i].write(msg.payload);
|
connectionPool[i].destroy();
|
||||||
} else if (typeof msg.payload === "string" && node.base64) {
|
}
|
||||||
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
|
else {
|
||||||
} else {
|
if (Buffer.isBuffer(msg.payload)) {
|
||||||
connectionPool[i].write(Buffer.from(""+msg.payload));
|
connectionPool[i].write(msg.payload);
|
||||||
|
} else if (typeof msg.payload === "string" && node.base64) {
|
||||||
|
connectionPool[i].write(Buffer.from(msg.payload,'base64'));
|
||||||
|
} else {
|
||||||
|
connectionPool[i].write(Buffer.from(""+msg.payload));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,13 +557,33 @@ module.exports = function(RED) {
|
|||||||
|
|
||||||
this.on("input", function(msg, nodeSend, nodeDone) {
|
this.on("input", function(msg, nodeSend, nodeDone) {
|
||||||
var i = 0;
|
var i = 0;
|
||||||
if ((!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
|
if (msg.payload !== undefined && (!Buffer.isBuffer(msg.payload)) && (typeof msg.payload !== "string")) {
|
||||||
msg.payload = msg.payload.toString();
|
msg.payload = msg.payload.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var host = node.server || msg.host;
|
var host = node.server || msg.host;
|
||||||
var port = node.port || msg.port;
|
var port = node.port || msg.port;
|
||||||
|
|
||||||
|
if (node.out === "sit" && msg?.reset) {
|
||||||
|
if (msg.reset === true) { // kill all connections
|
||||||
|
for (var cl in clients) {
|
||||||
|
if (clients[cl].hasOwnProperty("client")) {
|
||||||
|
clients[cl].client.destroy();
|
||||||
|
delete clients[cl];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof(msg.reset) === "string" && msg.reset.includes(":")) { // just kill connection host:port
|
||||||
|
if (clients.hasOwnProperty(msg.reset) && clients[msg.reset].hasOwnProperty("client")) {
|
||||||
|
clients[msg.reset].client.destroy();
|
||||||
|
delete clients[msg.reset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cc = Object.keys(clients).length;
|
||||||
|
node.status({fill:"green",shape:cc===0?"ring":"dot",text:RED._("tcpin.status.connections",{count:cc})});
|
||||||
|
if ((host === undefined || port === undefined) && !msg.hasOwnProperty("payload")) { return; }
|
||||||
|
}
|
||||||
|
|
||||||
// Store client information independently
|
// Store client information independently
|
||||||
// the clients object will have:
|
// the clients object will have:
|
||||||
// clients[id].client, clients[id].msg, clients[id].timeout
|
// clients[id].client, clients[id].msg, clients[id].timeout
|
||||||
@ -621,13 +651,16 @@ module.exports = function(RED) {
|
|||||||
clients[connection_id].connecting = true;
|
clients[connection_id].connecting = true;
|
||||||
clients[connection_id].client.connect(connOpts, function() {
|
clients[connection_id].client.connect(connOpts, function() {
|
||||||
//node.log(RED._("tcpin.errors.client-connected"));
|
//node.log(RED._("tcpin.errors.client-connected"));
|
||||||
node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
// node.status({fill:"green",shape:"dot",text:"common.status.connected"});
|
||||||
|
node.status({fill:"green",shape:"dot",text:RED._("tcpin.status.connections",{count:Object.keys(clients).length})});
|
||||||
if (clients[connection_id] && clients[connection_id].client) {
|
if (clients[connection_id] && clients[connection_id].client) {
|
||||||
clients[connection_id].connected = true;
|
clients[connection_id].connected = true;
|
||||||
clients[connection_id].connecting = false;
|
clients[connection_id].connecting = false;
|
||||||
let event;
|
let event;
|
||||||
while (event = dequeue(clients[connection_id].msgQueue)) {
|
while (event = dequeue(clients[connection_id].msgQueue)) {
|
||||||
clients[connection_id].client.write(event.msg.payload);
|
if (event.msg.payload !== undefined) {
|
||||||
|
clients[connection_id].client.write(event.msg.payload);
|
||||||
|
}
|
||||||
event.nodeDone();
|
event.nodeDone();
|
||||||
}
|
}
|
||||||
if (node.out === "time" && node.splitc < 0) {
|
if (node.out === "time" && node.splitc < 0) {
|
||||||
@ -823,7 +856,9 @@ module.exports = function(RED) {
|
|||||||
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
|
else if (!clients[connection_id].connecting && clients[connection_id].connected) {
|
||||||
if (clients[connection_id] && clients[connection_id].client) {
|
if (clients[connection_id] && clients[connection_id].client) {
|
||||||
let event = dequeue(clients[connection_id].msgQueue)
|
let event = dequeue(clients[connection_id].msgQueue)
|
||||||
clients[connection_id].client.write(event.msg.payload);
|
if (event.msg.payload !== undefined ) {
|
||||||
|
clients[connection_id].client.write(event.msg.payload);
|
||||||
|
}
|
||||||
event.nodeDone();
|
event.nodeDone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,20 @@
|
|||||||
</select>
|
</select>
|
||||||
<input style="width:40px;" type="text" id="node-input-sep" pattern=".">
|
<input style="width:40px;" type="text" id="node-input-sep" pattern=".">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label><i class="fa fa-code"></i> <span data-i18n="csv.label.spec"></span></label>
|
||||||
|
<div style="display: inline-grid;width: 70%;">
|
||||||
|
<select style="width:100%" id="csv-option-spec">
|
||||||
|
<option value="rfc" data-i18n="csv.spec.rfc"></option>
|
||||||
|
<option value="" data-i18n="csv.spec.legacy"></option>
|
||||||
|
</select>
|
||||||
|
<div>
|
||||||
|
<div class="form-tips csv-lecacy-warning" data-i18n="node-red:csv.spec.legacy_warning"
|
||||||
|
style="width: calc(100% - 18px); margin-top: 4px; max-width: unset;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||||
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
<input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
|
||||||
@ -60,10 +73,10 @@
|
|||||||
<div class="form-row" style="padding-left:20px;">
|
<div class="form-row" style="padding-left:20px;">
|
||||||
<label></label>
|
<label></label>
|
||||||
<label style="width:auto; margin-right:10px;" for="node-input-ret"><span data-i18n="csv.label.newline"></span></label>
|
<label style="width:auto; margin-right:10px;" for="node-input-ret"><span data-i18n="csv.label.newline"></span></label>
|
||||||
<select style="width:150px;" id="node-input-ret">
|
<select style="width:calc(70% - 108px);" id="node-input-ret">
|
||||||
|
<option value='\r\n' data-i18n="csv.newline.windows"></option>
|
||||||
<option value='\n' data-i18n="csv.newline.linux"></option>
|
<option value='\n' data-i18n="csv.newline.linux"></option>
|
||||||
<option value='\r' data-i18n="csv.newline.mac"></option>
|
<option value='\r' data-i18n="csv.newline.mac"></option>
|
||||||
<option value='\r\n' data-i18n="csv.newline.windows"></option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
@ -75,6 +88,7 @@
|
|||||||
color:"#DEBD5C",
|
color:"#DEBD5C",
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
|
spec: {value:"rfc"},
|
||||||
sep: {
|
sep: {
|
||||||
value:',', required:true,
|
value:',', required:true,
|
||||||
label:RED._("node-red:csv.label.separator"),
|
label:RED._("node-red:csv.label.separator"),
|
||||||
@ -83,7 +97,7 @@
|
|||||||
hdrin: {value:""},
|
hdrin: {value:""},
|
||||||
hdrout: {value:"none"},
|
hdrout: {value:"none"},
|
||||||
multi: {value:"one",required:true},
|
multi: {value:"one",required:true},
|
||||||
ret: {value:'\\n'},
|
ret: {value:'\\r\\n'}, // default to CRLF (RFC4180 Sec 2.1: "Each record is located on a separate line, delimited by a line break (CRLF)")
|
||||||
temp: {value:""},
|
temp: {value:""},
|
||||||
skip: {value:"0"},
|
skip: {value:"0"},
|
||||||
strings: {value:true},
|
strings: {value:true},
|
||||||
@ -123,6 +137,27 @@
|
|||||||
$("#node-input-sep").hide();
|
$("#node-input-sep").hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#csv-option-spec").on("change", function() {
|
||||||
|
if ($("#csv-option-spec").val() == "rfc") {
|
||||||
|
$(".form-tips.csv-lecacy-warning").hide();
|
||||||
|
} else {
|
||||||
|
$(".form-tips.csv-lecacy-warning").show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// new nodes will have `spec` set to "rfc" (default), but existing nodes will either not have
|
||||||
|
// a spec value or it will be empty - we need to maintain the legacy behaviour for existing
|
||||||
|
// flows but default to rfc for new nodes
|
||||||
|
let spec = !this.spec ? "" : "rfc"
|
||||||
|
$("#csv-option-spec").val(spec).trigger("change")
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
const specFormVal = $("#csv-option-spec").val() || '' // empty === legacy
|
||||||
|
const spectNodeVal = this.spec || '' // empty === legacy, null/undefined means in-place node upgrade (keep as is)
|
||||||
|
if (specFormVal !== spectNodeVal) {
|
||||||
|
// only update the flow value if changed (avoid marking the node dirty unnecessarily)
|
||||||
|
this.spec = specFormVal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -15,319 +15,674 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
|
const csv = require('./lib/csv')
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
function CSVNode(n) {
|
function CSVNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n)
|
||||||
this.template = (n.temp || "");
|
const node = this
|
||||||
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
|
const RFC4180Mode = n.spec === 'rfc'
|
||||||
this.quo = '"';
|
const legacyMode = !RFC4180Mode
|
||||||
this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
|
|
||||||
this.winflag = (this.ret === "\r\n");
|
|
||||||
this.lineend = "\n";
|
|
||||||
this.multi = n.multi || "one";
|
|
||||||
this.hdrin = n.hdrin || false;
|
|
||||||
this.hdrout = n.hdrout || "none";
|
|
||||||
this.goodtmpl = true;
|
|
||||||
this.skip = parseInt(n.skip || 0);
|
|
||||||
this.store = [];
|
|
||||||
this.parsestrings = n.strings;
|
|
||||||
this.include_empty_strings = n.include_empty_strings || false;
|
|
||||||
this.include_null_values = n.include_null_values || false;
|
|
||||||
if (this.parsestrings === undefined) { this.parsestrings = true; }
|
|
||||||
if (this.hdrout === false) { this.hdrout = "none"; }
|
|
||||||
if (this.hdrout === true) { this.hdrout = "all"; }
|
|
||||||
var tmpwarn = true;
|
|
||||||
var node = this;
|
|
||||||
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
|
|
||||||
|
|
||||||
// pass in an array of column names to be trimmed, de-quoted and retrimmed
|
node.status({}) // clear status
|
||||||
var clean = function(col,sep) {
|
|
||||||
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
|
|
||||||
col = col.trim().split(re) || [""];
|
|
||||||
col = col.map(x => x.replace(/"/g,'').trim());
|
|
||||||
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
|
|
||||||
else { node.goodtmpl = true; }
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
var template = clean(node.template,',');
|
|
||||||
var notemplate = template.length === 1 && template[0] === '';
|
|
||||||
node.hdrSent = false;
|
|
||||||
|
|
||||||
this.on("input", function(msg, send, done) {
|
if (legacyMode) {
|
||||||
if (msg.hasOwnProperty("reset")) {
|
this.template = (n.temp || "");
|
||||||
node.hdrSent = false;
|
this.sep = (n.sep || ',').replace(/\\t/g,"\t").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
|
||||||
|
this.quo = '"';
|
||||||
|
this.ret = (n.ret || "\n").replace(/\\n/g,"\n").replace(/\\r/g,"\r");
|
||||||
|
this.winflag = (this.ret === "\r\n");
|
||||||
|
this.lineend = "\n";
|
||||||
|
this.multi = n.multi || "one";
|
||||||
|
this.hdrin = n.hdrin || false;
|
||||||
|
this.hdrout = n.hdrout || "none";
|
||||||
|
this.goodtmpl = true;
|
||||||
|
this.skip = parseInt(n.skip || 0);
|
||||||
|
this.store = [];
|
||||||
|
this.parsestrings = n.strings;
|
||||||
|
this.include_empty_strings = n.include_empty_strings || false;
|
||||||
|
this.include_null_values = n.include_null_values || false;
|
||||||
|
if (this.parsestrings === undefined) { this.parsestrings = true; }
|
||||||
|
if (this.hdrout === false) { this.hdrout = "none"; }
|
||||||
|
if (this.hdrout === true) { this.hdrout = "all"; }
|
||||||
|
var tmpwarn = true;
|
||||||
|
// var node = this;
|
||||||
|
var re = new RegExp(node.sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') + '(?=(?:(?:[^"]*"){2})*[^"]*$)','g');
|
||||||
|
|
||||||
|
// pass in an array of column names to be trimmed, de-quoted and retrimmed
|
||||||
|
var clean = function(col,sep) {
|
||||||
|
if (sep) { re = new RegExp(sep.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,'\\$&') +'(?=(?:(?:[^"]*"){2})*[^"]*$)','g'); }
|
||||||
|
col = col.trim().split(re) || [""];
|
||||||
|
col = col.map(x => x.replace(/"/g,'').trim());
|
||||||
|
if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
|
||||||
|
else { node.goodtmpl = true; }
|
||||||
|
return col;
|
||||||
}
|
}
|
||||||
if (msg.hasOwnProperty("payload")) {
|
var template = clean(node.template,',');
|
||||||
if (typeof msg.payload == "object") { // convert object to CSV string
|
var notemplate = template.length === 1 && template[0] === '';
|
||||||
try {
|
node.hdrSent = false;
|
||||||
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
|
|
||||||
template = clean(node.template);
|
this.on("input", function(msg, send, done) {
|
||||||
}
|
if (msg.hasOwnProperty("reset")) {
|
||||||
var ou = "";
|
node.hdrSent = false;
|
||||||
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
|
}
|
||||||
if (node.hdrout !== "none" && node.hdrSent === false) {
|
if (msg.hasOwnProperty("payload")) {
|
||||||
if ((template.length === 1) && (template[0] === '')) {
|
if (typeof msg.payload == "object") { // convert object to CSV string
|
||||||
if (msg.hasOwnProperty("columns")) {
|
try {
|
||||||
template = clean(msg.columns || "",",");
|
if (!(notemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
|
||||||
}
|
template = clean(node.template);
|
||||||
else {
|
|
||||||
template = Object.keys(msg.payload[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ou += template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep) + node.ret;
|
const ou = [];
|
||||||
if (node.hdrout === "once") { node.hdrSent = true; }
|
if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
|
||||||
}
|
if (node.hdrout !== "none" && node.hdrSent === false) {
|
||||||
for (var s = 0; s < msg.payload.length; s++) {
|
|
||||||
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
|
|
||||||
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
|
|
||||||
for (var t = 0; t < msg.payload[s].length; t++) {
|
|
||||||
if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; }
|
|
||||||
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
|
||||||
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
|
|
||||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
|
||||||
}
|
|
||||||
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
|
||||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
|
||||||
}
|
|
||||||
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
|
|
||||||
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ou += msg.payload[s].join(node.sep) + node.ret;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
|
|
||||||
template = clean(msg.columns || "",",");
|
|
||||||
}
|
|
||||||
if ((template.length === 1) && (template[0] === '')) {
|
if ((template.length === 1) && (template[0] === '')) {
|
||||||
/* istanbul ignore else */
|
if (msg.hasOwnProperty("columns")) {
|
||||||
if (tmpwarn === true) { // just warn about missing template once
|
template = clean(msg.columns || "",",");
|
||||||
node.warn(RED._("csv.errors.obj_csv"));
|
|
||||||
tmpwarn = false;
|
|
||||||
}
|
}
|
||||||
for (var p in msg.payload[0]) {
|
else {
|
||||||
|
template = Object.keys(msg.payload[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ou.push(template.map(v => v.indexOf(node.sep)!==-1 ? '"'+v+'"' : v).join(node.sep));
|
||||||
|
if (node.hdrout === "once") { node.hdrSent = true; }
|
||||||
|
}
|
||||||
|
for (var s = 0; s < msg.payload.length; s++) {
|
||||||
|
if ((Array.isArray(msg.payload[s])) || (typeof msg.payload[s] !== "object")) {
|
||||||
|
if (typeof msg.payload[s] !== "object") { msg.payload = [ msg.payload ]; }
|
||||||
|
for (var t = 0; t < msg.payload[s].length; t++) {
|
||||||
|
if (msg.payload[s][t] === undefined) { msg.payload[s][t] = ""; }
|
||||||
|
if (msg.payload[s][t].toString().indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||||
|
msg.payload[s][t] = msg.payload[s][t].toString().replace(/"/g, '""');
|
||||||
|
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||||
|
}
|
||||||
|
else if (msg.payload[s][t].toString().indexOf(node.sep) !== -1) { // add quotes if any "commas"
|
||||||
|
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||||
|
}
|
||||||
|
else if (msg.payload[s][t].toString().indexOf("\n") !== -1) { // add quotes if any "\n"
|
||||||
|
msg.payload[s][t] = node.quo + msg.payload[s][t].toString() + node.quo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ou.push(msg.payload[s].join(node.sep));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((template.length === 1) && (template[0] === '') && (msg.hasOwnProperty("columns"))) {
|
||||||
|
template = clean(msg.columns || "",",");
|
||||||
|
}
|
||||||
|
if ((template.length === 1) && (template[0] === '')) {
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (msg.payload[s].hasOwnProperty(p)) {
|
if (tmpwarn === true) { // just warn about missing template once
|
||||||
|
node.warn(RED._("csv.errors.obj_csv"));
|
||||||
|
tmpwarn = false;
|
||||||
|
}
|
||||||
|
const row = [];
|
||||||
|
for (var p in msg.payload[0]) {
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (typeof msg.payload[s][p] !== "object") {
|
if (msg.payload[s].hasOwnProperty(p)) {
|
||||||
// Fix to honour include null values flag
|
/* istanbul ignore else */
|
||||||
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
|
if (typeof msg.payload[s][p] !== "object") {
|
||||||
var q = "";
|
// Fix to honour include null values flag
|
||||||
if (msg.payload[s][p] !== undefined) {
|
//if (typeof msg.payload[s][p] !== "object" || (node.include_null_values === true && msg.payload[s][p] === null)) {
|
||||||
q += msg.payload[s][p];
|
var q = "";
|
||||||
|
if (msg.payload[s][p] !== undefined) {
|
||||||
|
q += msg.payload[s][p];
|
||||||
|
}
|
||||||
|
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||||
|
q = q.replace(/"/g, '""');
|
||||||
|
row.push(node.quo + q + node.quo);
|
||||||
|
}
|
||||||
|
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
||||||
|
row.push(node.quo + q + node.quo);
|
||||||
|
}
|
||||||
|
else { row.push(q); } // otherwise just add
|
||||||
}
|
}
|
||||||
if (q.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
|
||||||
q = q.replace(/"/g, '""');
|
|
||||||
ou += node.quo + q + node.quo + node.sep;
|
|
||||||
}
|
|
||||||
else if (q.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
|
||||||
ou += node.quo + q + node.quo + node.sep;
|
|
||||||
}
|
|
||||||
else { ou += q + node.sep; } // otherwise just add
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ou.push(row.join(node.sep)); // add separator
|
||||||
}
|
}
|
||||||
ou = ou.slice(0,-1) + node.ret;
|
else {
|
||||||
|
const row = [];
|
||||||
|
for (var t=0; t < template.length; t++) {
|
||||||
|
if (template[t] === '') {
|
||||||
|
row.push('');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var tt = template[t];
|
||||||
|
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
|
||||||
|
else { tt = '"'+tt+'"'; }
|
||||||
|
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
|
||||||
|
/* istanbul ignore else */
|
||||||
|
if (p === undefined) { p = ""; }
|
||||||
|
// fix to honour include null values flag
|
||||||
|
//if (p === null && node.include_null_values !== true) { p = "";}
|
||||||
|
p = RED.util.ensureString(p);
|
||||||
|
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
||||||
|
p = p.replace(/"/g, '""');
|
||||||
|
row.push(node.quo + p + node.quo);
|
||||||
|
}
|
||||||
|
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
||||||
|
row.push(node.quo + p + node.quo);
|
||||||
|
}
|
||||||
|
else { row.push(p); } // otherwise just add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ou.push(row.join(node.sep)); // add separator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// join lines, don't forget to add the last new line
|
||||||
|
msg.payload = ou.join(node.ret) + node.ret;
|
||||||
|
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
|
||||||
|
if (msg.payload !== '') {
|
||||||
|
send(msg);
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
catch(e) { done(e); }
|
||||||
|
}
|
||||||
|
else if (typeof msg.payload == "string") { // convert CSV string to object
|
||||||
|
try {
|
||||||
|
var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
|
||||||
|
var j = 0; // pointer into array of template items
|
||||||
|
var k = [""]; // array of data for each of the template items
|
||||||
|
var o = {}; // output object to build up
|
||||||
|
var a = []; // output array is needed for multiline option
|
||||||
|
var first = true; // is this the first line
|
||||||
|
var last = false;
|
||||||
|
var line = msg.payload;
|
||||||
|
var linecount = 0;
|
||||||
|
var tmp = "";
|
||||||
|
var has_parts = msg.hasOwnProperty("parts");
|
||||||
|
var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i;
|
||||||
|
if (msg.hasOwnProperty("parts")) {
|
||||||
|
linecount = msg.parts.index;
|
||||||
|
if (msg.parts.index > node.skip) { first = false; }
|
||||||
|
if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now we are just going to assume that any \r or \n means an end of line...
|
||||||
|
// got to be a weird csv that has singleton \r \n in it for another reason...
|
||||||
|
|
||||||
|
// Now process the whole file/line
|
||||||
|
var nocr = (line.match(/[\r\n]/g)||[]).length;
|
||||||
|
if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; }
|
||||||
|
for (var i = 0; i < line.length; i++) {
|
||||||
|
if (first && (linecount < node.skip)) {
|
||||||
|
if (line[i] === "\n") { linecount += 1; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((node.hdrin === true) && first) { // if the template is in the first line
|
||||||
|
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
|
||||||
|
if (line.length - i === 1) { tmp += line[i]; }
|
||||||
|
template = clean(tmp,node.sep);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else { tmp += line[i]; }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (var t=0; t < template.length; t++) {
|
if (line[i] === node.quo) { // if it's a quote toggle inside or outside
|
||||||
if (template[t] === '') {
|
f = !f;
|
||||||
ou += node.sep;
|
if (line[i-1] === node.quo) {
|
||||||
|
if (f === false) { k[j] += '\"'; }
|
||||||
|
} // if it's a quotequote then it's actually a quote
|
||||||
|
//if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; }
|
||||||
|
}
|
||||||
|
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
|
||||||
|
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
||||||
|
if ( template[j] && (template[j] !== "") ) {
|
||||||
|
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
|
||||||
|
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
|
||||||
|
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
||||||
|
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
||||||
|
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
||||||
|
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
||||||
}
|
}
|
||||||
else {
|
j += 1;
|
||||||
var tt = template[t];
|
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
|
||||||
if (template[t].indexOf('"') >=0 ) { tt = "'"+tt+"'"; }
|
k[j] = line.length - 1 === i ? null : "";
|
||||||
else { tt = '"'+tt+'"'; }
|
}
|
||||||
var p = RED.util.getMessageProperty(msg,'payload["'+s+'"]['+tt+']');
|
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
|
||||||
/* istanbul ignore else */
|
//console.log(j,k,o,k[j]);
|
||||||
if (p === undefined) { p = ""; }
|
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
||||||
// fix to honour include null values flag
|
if ( template[j] && (template[j] !== "") ) {
|
||||||
//if (p === null && node.include_null_values !== true) { p = "";}
|
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
|
||||||
p = RED.util.ensureString(p);
|
if (line[i-1] === node.sep) k[j] = null;
|
||||||
if (p.indexOf(node.quo) !== -1) { // add double quotes if any quotes
|
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
||||||
p = p.replace(/"/g, '""');
|
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
|
||||||
ou += node.quo + p + node.quo + node.sep;
|
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
||||||
}
|
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
||||||
else if (p.indexOf(node.sep) !== -1 || p.indexOf("\n") !== -1) { // add quotes if any "commas" or "\n"
|
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
||||||
ou += node.quo + p + node.quo + node.sep;
|
|
||||||
}
|
|
||||||
else { ou += p + node.sep; } // otherwise just add
|
|
||||||
}
|
}
|
||||||
|
if (JSON.stringify(o) !== "{}") { // don't send empty objects
|
||||||
|
a.push(o); // add to the array
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
k = [""];
|
||||||
|
o = {};
|
||||||
|
f = true; // reset in/out flag ready for next line.
|
||||||
|
}
|
||||||
|
else { // just add to the part of the message
|
||||||
|
k[j] += line[i];
|
||||||
}
|
}
|
||||||
ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// Finished so finalize and send anything left
|
||||||
msg.payload = ou;
|
if (f === false) { node.warn(RED._("csv.errors.bad_csv")); }
|
||||||
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).join(',');
|
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
||||||
if (msg.payload !== '') { send(msg); }
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
catch(e) { done(e); }
|
|
||||||
}
|
|
||||||
else if (typeof msg.payload == "string") { // convert CSV string to object
|
|
||||||
try {
|
|
||||||
var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
|
|
||||||
var j = 0; // pointer into array of template items
|
|
||||||
var k = [""]; // array of data for each of the template items
|
|
||||||
var o = {}; // output object to build up
|
|
||||||
var a = []; // output array is needed for multiline option
|
|
||||||
var first = true; // is this the first line
|
|
||||||
var last = false;
|
|
||||||
var line = msg.payload;
|
|
||||||
var linecount = 0;
|
|
||||||
var tmp = "";
|
|
||||||
var has_parts = msg.hasOwnProperty("parts");
|
|
||||||
var reg = /^[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+$/i;
|
|
||||||
if (msg.hasOwnProperty("parts")) {
|
|
||||||
linecount = msg.parts.index;
|
|
||||||
if (msg.parts.index > node.skip) { first = false; }
|
|
||||||
if (msg.parts.hasOwnProperty("count") && (msg.parts.index+1 >= msg.parts.count)) { last = true; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now we are just going to assume that any \r or \n means an end of line...
|
if ( template[j] && (template[j] !== "") ) {
|
||||||
// got to be a weird csv that has singleton \r \n in it for another reason...
|
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
||||||
|
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
|
||||||
// Now process the whole file/line
|
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
||||||
var nocr = (line.match(/[\r\n]/g)||[]).length;
|
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
||||||
if (has_parts && node.multi === "mult" && nocr > 1) { tmp = ""; first = true; }
|
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
||||||
for (var i = 0; i < line.length; i++) {
|
|
||||||
if (first && (linecount < node.skip)) {
|
|
||||||
if (line[i] === "\n") { linecount += 1; }
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if ((node.hdrin === true) && first) { // if the template is in the first line
|
|
||||||
if ((line[i] === "\n")||(line[i] === "\r")||(line.length - i === 1)) { // look for first line break
|
if (JSON.stringify(o) !== "{}") { // don't send empty objects
|
||||||
if (line.length - i === 1) { tmp += line[i]; }
|
a.push(o); // add to the array
|
||||||
template = clean(tmp,node.sep);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
else { tmp += line[i]; }
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (line[i] === node.quo) { // if it's a quote toggle inside or outside
|
|
||||||
f = !f;
|
|
||||||
if (line[i-1] === node.quo) {
|
|
||||||
if (f === false) { k[j] += '\"'; }
|
|
||||||
} // if it's a quotequote then it's actually a quote
|
|
||||||
//if ((line[i-1] !== node.sep) && (line[i+1] !== node.sep)) { k[j] += line[i]; }
|
|
||||||
}
|
|
||||||
else if ((line[i] === node.sep) && f) { // if it is the end of the line then finish
|
|
||||||
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
|
||||||
if ( template[j] && (template[j] !== "") ) {
|
|
||||||
// if no value between separators ('1,,"3"...') or if the line beings with separator (',1,"2"...') treat value as null
|
|
||||||
if (line[i-1] === node.sep || line[i-1].includes('\n','\r')) k[j] = null;
|
|
||||||
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
|
||||||
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
|
||||||
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
|
||||||
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
|
||||||
}
|
|
||||||
j += 1;
|
|
||||||
// if separator is last char in processing string line (without end of line), add null value at the end - example: '1,2,3\n3,"3",'
|
|
||||||
k[j] = line.length - 1 === i ? null : "";
|
|
||||||
}
|
|
||||||
else if (((line[i] === "\n") || (line[i] === "\r")) && f) { // handle multiple lines
|
|
||||||
//console.log(j,k,o,k[j]);
|
|
||||||
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
|
||||||
if ( template[j] && (template[j] !== "") ) {
|
|
||||||
// if separator before end of line, set null value ie. '1,2,"3"\n1,2,\n1,2,3'
|
|
||||||
if (line[i-1] === node.sep) k[j] = null;
|
|
||||||
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
|
||||||
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
|
|
||||||
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
|
||||||
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
|
||||||
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
|
||||||
}
|
|
||||||
if (JSON.stringify(o) !== "{}") { // don't send empty objects
|
|
||||||
a.push(o); // add to the array
|
|
||||||
}
|
|
||||||
j = 0;
|
|
||||||
k = [""];
|
|
||||||
o = {};
|
|
||||||
f = true; // reset in/out flag ready for next line.
|
|
||||||
}
|
|
||||||
else { // just add to the part of the message
|
|
||||||
k[j] += line[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finished so finalize and send anything left
|
|
||||||
if (f === false) { node.warn(RED._("csv.errors.bad_csv")); }
|
|
||||||
if (!node.goodtmpl) { template[j] = "col"+(j+1); }
|
|
||||||
|
|
||||||
if ( template[j] && (template[j] !== "") ) {
|
if (node.multi !== "one") {
|
||||||
if ( (k[j] !== null && node.parsestrings === true) && reg.test(k[j].trim()) ) { k[j] = parseFloat(k[j].trim()); }
|
msg.payload = a;
|
||||||
else { if (k[j] !== null) k[j].replace(/\r$/,''); }
|
if (has_parts && nocr <= 1) {
|
||||||
if (node.include_null_values && k[j] === null) o[template[j]] = k[j];
|
if (JSON.stringify(o) !== "{}") {
|
||||||
if (node.include_empty_strings && k[j] === "") o[template[j]] = k[j];
|
node.store.push(o);
|
||||||
if (k[j] !== null && k[j] !== "") o[template[j]] = k[j];
|
}
|
||||||
}
|
if (msg.parts.index + 1 === msg.parts.count) {
|
||||||
|
msg.payload = node.store;
|
||||||
if (JSON.stringify(o) !== "{}") { // don't send empty objects
|
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
||||||
a.push(o); // add to the array
|
delete msg.parts;
|
||||||
}
|
send(msg);
|
||||||
|
node.store = [];
|
||||||
if (node.multi !== "one") {
|
}
|
||||||
msg.payload = a;
|
|
||||||
if (has_parts && nocr <= 1) {
|
|
||||||
if (JSON.stringify(o) !== "{}") {
|
|
||||||
node.store.push(o);
|
|
||||||
}
|
}
|
||||||
if (msg.parts.index + 1 === msg.parts.count) {
|
else {
|
||||||
msg.payload = node.store;
|
|
||||||
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
||||||
delete msg.parts;
|
send(msg); // finally send the array
|
||||||
send(msg);
|
|
||||||
node.store = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
msg.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
var len = a.length;
|
||||||
send(msg); // finally send the array
|
for (var i = 0; i < len; i++) {
|
||||||
}
|
var newMessage = RED.util.cloneMessage(msg);
|
||||||
}
|
newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
||||||
else {
|
newMessage.payload = a[i];
|
||||||
var len = a.length;
|
if (!has_parts) {
|
||||||
for (var i = 0; i < len; i++) {
|
newMessage.parts = {
|
||||||
var newMessage = RED.util.cloneMessage(msg);
|
id: msg._msgid,
|
||||||
newMessage.columns = template.map(v => v.indexOf(',')!==-1 ? '"'+v+'"' : v).filter(v => v).join(',');
|
index: i,
|
||||||
newMessage.payload = a[i];
|
count: len
|
||||||
if (!has_parts) {
|
};
|
||||||
newMessage.parts = {
|
}
|
||||||
id: msg._msgid,
|
else {
|
||||||
index: i,
|
newMessage.parts.index -= node.skip;
|
||||||
count: len
|
newMessage.parts.count -= node.skip;
|
||||||
};
|
if (node.hdrin) { // if we removed the header line then shift the counts by 1
|
||||||
|
newMessage.parts.index -= 1;
|
||||||
|
newMessage.parts.count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (last) { newMessage.complete = true; }
|
||||||
|
send(newMessage);
|
||||||
}
|
}
|
||||||
else {
|
if (has_parts && last && len === 0) {
|
||||||
newMessage.parts.index -= node.skip;
|
send({complete:true});
|
||||||
newMessage.parts.count -= node.skip;
|
}
|
||||||
if (node.hdrin) { // if we removed the header line then shift the counts by 1
|
}
|
||||||
newMessage.parts.index -= 1;
|
node.linecount = 0;
|
||||||
newMessage.parts.count -= 1;
|
done();
|
||||||
|
}
|
||||||
|
catch(e) { done(e); }
|
||||||
|
}
|
||||||
|
else { node.warn(RED._("csv.errors.csv_js")); done(); }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!msg.hasOwnProperty("reset")) {
|
||||||
|
node.send(msg); // If no payload and not reset - just pass it on.
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(RFC4180Mode) {
|
||||||
|
node.template = (n.temp || "")
|
||||||
|
node.sep = (n.sep || ',').replace(/\\t/g, "\t").replace(/\\n/g, "\n").replace(/\\r/g, "\r")
|
||||||
|
node.quo = '"'
|
||||||
|
// default to CRLF (RFC4180 Sec 2.1: "Each record is located on a separate line, delimited by a line break (CRLF)")
|
||||||
|
node.ret = (n.ret || "\r\n").replace(/\\n/g, "\n").replace(/\\r/g, "\r")
|
||||||
|
node.multi = n.multi || "one"
|
||||||
|
node.hdrin = n.hdrin || false
|
||||||
|
node.hdrout = n.hdrout || "none"
|
||||||
|
node.goodtmpl = true
|
||||||
|
node.skip = parseInt(n.skip || 0)
|
||||||
|
node.store = []
|
||||||
|
node.parsestrings = n.strings
|
||||||
|
node.include_empty_strings = n.include_empty_strings || false
|
||||||
|
node.include_null_values = n.include_null_values || false
|
||||||
|
if (node.parsestrings === undefined) { node.parsestrings = true }
|
||||||
|
if (node.hdrout === false) { node.hdrout = "none" }
|
||||||
|
if (node.hdrout === true) { node.hdrout = "all" }
|
||||||
|
const dontSendHeaders = node.hdrout === "none"
|
||||||
|
const sendHeadersOnce = node.hdrout === "once"
|
||||||
|
const sendHeadersAlways = node.hdrout === "all"
|
||||||
|
const sendHeaders = !dontSendHeaders && (sendHeadersOnce || sendHeadersAlways)
|
||||||
|
const quoteables = [node.sep, node.quo, "\n", "\r"]
|
||||||
|
const templateQuoteables = [',', '"', "\n", "\r"]
|
||||||
|
let badTemplateWarnOnce = true
|
||||||
|
|
||||||
|
const columnStringToTemplateArray = function (col, sep) {
|
||||||
|
// NOTE: enforce strict column template parsing in RFC4180 mode
|
||||||
|
const parsed = csv.parse(col, { separator: sep, quote: node.quo, outputStyle: 'array', strict: true })
|
||||||
|
if (parsed.headers.length > 0) { node.goodtmpl = true } else { node.goodtmpl = false }
|
||||||
|
return parsed.headers.length ? parsed.headers : null
|
||||||
|
}
|
||||||
|
const templateArrayToColumnString = function (template, keepEmptyColumns) {
|
||||||
|
// NOTE: enforce strict column template parsing in RFC4180 mode
|
||||||
|
const parsed = csv.parse('', {headers: template, headersOnly:true, separator: ',', quote: node.quo, outputStyle: 'array', strict: true })
|
||||||
|
return keepEmptyColumns
|
||||||
|
? parsed.headers.map(e => addQuotes(e || '', { separator: ',', quoteables: templateQuoteables}))
|
||||||
|
: parsed.header // exclues empty columns
|
||||||
|
// TODO: resolve inconsistency between CSV->JSON and JSON->CSV
|
||||||
|
// CSV->JSON: empty columns are excluded
|
||||||
|
// JSON->CSV: empty columns are kept in some cases
|
||||||
|
}
|
||||||
|
function addQuotes(cell, options) {
|
||||||
|
options = options || {}
|
||||||
|
return csv.quoteCell(cell, {
|
||||||
|
quote: options.quote || node.quo || '"',
|
||||||
|
separator: options.separator || node.sep || ',',
|
||||||
|
quoteables: options.quoteables || quoteables
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const hasTemplate = (t) => t?.length > 0 && !(t.length === 1 && t[0] === '')
|
||||||
|
let template
|
||||||
|
try {
|
||||||
|
template = columnStringToTemplateArray(node.template, ',') || ['']
|
||||||
|
} catch (e) {
|
||||||
|
node.warn(RED._("csv.errors.bad_template")) // is warning really necessary now we have status?
|
||||||
|
node.status({ fill: "red", shape: "dot", text: RED._("csv.errors.bad_template") })
|
||||||
|
return // dont hook up the node
|
||||||
|
}
|
||||||
|
const noTemplate = hasTemplate(template) === false
|
||||||
|
node.hdrSent = false
|
||||||
|
|
||||||
|
node.on("input", function (msg, send, done) {
|
||||||
|
node.status({}) // clear status
|
||||||
|
if (msg.hasOwnProperty("reset")) {
|
||||||
|
node.hdrSent = false
|
||||||
|
}
|
||||||
|
if (msg.hasOwnProperty("payload")) {
|
||||||
|
let inputData = msg.payload
|
||||||
|
if (typeof inputData == "object") { // convert object to CSV string
|
||||||
|
try {
|
||||||
|
// first determine the payload kind. Array or objects? Array of primitives? Array of arrays? Just an object?
|
||||||
|
// then, if necessary, convert to an array of objects/arrays
|
||||||
|
let isObject = !Array.isArray(inputData) && typeof inputData === 'object'
|
||||||
|
let isArrayOfObjects = Array.isArray(inputData) && inputData.length > 0 && typeof inputData[0] === 'object'
|
||||||
|
let isArrayOfArrays = Array.isArray(inputData) && inputData.length > 0 && Array.isArray(inputData[0])
|
||||||
|
let isArrayOfPrimitives = Array.isArray(inputData) && inputData.length > 0 && typeof inputData[0] !== 'object'
|
||||||
|
|
||||||
|
if (isObject) {
|
||||||
|
inputData = [inputData]
|
||||||
|
isArrayOfObjects = true
|
||||||
|
isObject = false
|
||||||
|
} else if (isArrayOfPrimitives) {
|
||||||
|
inputData = [inputData]
|
||||||
|
isArrayOfArrays = true
|
||||||
|
isArrayOfPrimitives = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringBuilder = []
|
||||||
|
if (!(noTemplate && (msg.hasOwnProperty("parts") && msg.parts.hasOwnProperty("index") && msg.parts.index > 0))) {
|
||||||
|
template = columnStringToTemplateArray(node.template) || ['']
|
||||||
|
}
|
||||||
|
|
||||||
|
// build header line
|
||||||
|
if (sendHeaders && node.hdrSent === false) {
|
||||||
|
if (hasTemplate(template) === false) {
|
||||||
|
if (msg.hasOwnProperty("columns")) {
|
||||||
|
template = columnStringToTemplateArray(msg.columns || "", ",") || ['']
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
template = Object.keys(inputData[0]) || ['']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (last) { newMessage.complete = true; }
|
stringBuilder.push(templateArrayToColumnString(template, true))
|
||||||
send(newMessage);
|
if (sendHeadersOnce) { node.hdrSent = true }
|
||||||
}
|
}
|
||||||
if (has_parts && last && len === 0) {
|
|
||||||
send({complete:true});
|
// build csv lines
|
||||||
|
for (let s = 0; s < inputData.length; s++) {
|
||||||
|
let row = inputData[s]
|
||||||
|
if (isArrayOfArrays) {
|
||||||
|
/*** row is an array of arrays ***/
|
||||||
|
const _hasTemplate = hasTemplate(template)
|
||||||
|
const len = _hasTemplate ? template.length : row.length
|
||||||
|
const result = []
|
||||||
|
for (let t = 0; t < len; t++) {
|
||||||
|
let cell = row[t]
|
||||||
|
if (cell === undefined) { cell = "" }
|
||||||
|
if(_hasTemplate) {
|
||||||
|
const header = template[t]
|
||||||
|
if (header) {
|
||||||
|
result[t] = addQuotes(RED.util.ensureString(cell))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result[t] = addQuotes(RED.util.ensureString(cell))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.push(result.join(node.sep))
|
||||||
|
} else {
|
||||||
|
/*** row is an object ***/
|
||||||
|
if (hasTemplate(template) === false && (msg.hasOwnProperty("columns"))) {
|
||||||
|
template = columnStringToTemplateArray(msg.columns || "", ",")
|
||||||
|
}
|
||||||
|
if (hasTemplate(template) === false) {
|
||||||
|
/*** row is an object but we still don't have a template ***/
|
||||||
|
if (badTemplateWarnOnce === true) {
|
||||||
|
node.warn(RED._("csv.errors.obj_csv"))
|
||||||
|
badTemplateWarnOnce = false
|
||||||
|
}
|
||||||
|
const rowData = []
|
||||||
|
for (let header in inputData[0]) {
|
||||||
|
if (row.hasOwnProperty(header)) {
|
||||||
|
const cell = row[header]
|
||||||
|
if (typeof cell !== "object") {
|
||||||
|
let cellValue = ""
|
||||||
|
if (cell !== undefined) {
|
||||||
|
cellValue += cell
|
||||||
|
}
|
||||||
|
rowData.push(addQuotes(cellValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.push(rowData.join(node.sep))
|
||||||
|
} else {
|
||||||
|
/*** row is an object and we have a template ***/
|
||||||
|
const rowData = []
|
||||||
|
for (let t = 0; t < template.length; t++) {
|
||||||
|
if (!template[t]) {
|
||||||
|
rowData.push('')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let cellValue = inputData[s][template[t]]
|
||||||
|
if (cellValue === undefined) { cellValue = "" }
|
||||||
|
cellValue = RED.util.ensureString(cellValue)
|
||||||
|
rowData.push(addQuotes(cellValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringBuilder.push(rowData.join(node.sep)); // add separator
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// join lines, don't forget to add the last new line
|
||||||
|
msg.payload = stringBuilder.join(node.ret) + node.ret
|
||||||
|
msg.columns = templateArrayToColumnString(template)
|
||||||
|
if (msg.payload !== '') { send(msg) }
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
done(e)
|
||||||
}
|
}
|
||||||
node.linecount = 0;
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
catch(e) { done(e); }
|
else if (typeof inputData == "string") { // convert CSV string to object
|
||||||
|
try {
|
||||||
|
let firstLine = true; // is this the first line
|
||||||
|
let last = false
|
||||||
|
let linecount = 0
|
||||||
|
const has_parts = msg.hasOwnProperty("parts")
|
||||||
|
|
||||||
|
// determine if this is a multi part message and if so what part we are processing
|
||||||
|
if (msg.hasOwnProperty("parts")) {
|
||||||
|
linecount = msg.parts.index
|
||||||
|
if (msg.parts.index > node.skip) { firstLine = false }
|
||||||
|
if (msg.parts.hasOwnProperty("count") && (msg.parts.index + 1 >= msg.parts.count)) { last = true }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If skip is set, compute the cursor position to start parsing from
|
||||||
|
let _cursor = 0
|
||||||
|
if (node.skip > 0 && linecount < node.skip) {
|
||||||
|
for (; _cursor < inputData.length; _cursor++) {
|
||||||
|
if (firstLine && (linecount < node.skip)) {
|
||||||
|
if (inputData[_cursor] === "\r" || inputData[_cursor] === "\n") {
|
||||||
|
linecount += 1
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (_cursor >= inputData.length) {
|
||||||
|
return // skip this line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// count the number of line breaks in the string
|
||||||
|
const noofCR = ((_cursor ? inputData.slice(_cursor) : inputData).match(/[\r\n]/g) || []).length
|
||||||
|
|
||||||
|
// if we have `parts` and we are outputting multiple objects and we have more than one line
|
||||||
|
// then we need to set firstLine to true so that we process the header line
|
||||||
|
if (has_parts && node.multi === "mult" && noofCR > 1) {
|
||||||
|
firstLine = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are processing the first line and the node has been set to extract the header line
|
||||||
|
// update the template with the header line
|
||||||
|
if (firstLine && node.hdrin === true) {
|
||||||
|
/** @type {import('./lib/csv/index.js').CSVParseOptions} */
|
||||||
|
const csvOptionsForHeaderRow = {
|
||||||
|
cursor: _cursor,
|
||||||
|
separator: node.sep,
|
||||||
|
quote: node.quo,
|
||||||
|
dataHasHeaderRow: true,
|
||||||
|
headersOnly: true,
|
||||||
|
outputStyle: 'array',
|
||||||
|
strict: true // enforce strict parsing of the header row
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const csvHeader = csv.parse(inputData, csvOptionsForHeaderRow)
|
||||||
|
template = csvHeader.headers
|
||||||
|
_cursor = csvHeader.cursor
|
||||||
|
} catch (e) {
|
||||||
|
// node.warn(RED._("csv.errors.bad_template")) // add warning?
|
||||||
|
node.status({ fill: "red", shape: "dot", text: RED._("csv.errors.bad_template") })
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we process the data lines
|
||||||
|
/** @type {import('./lib/csv/index.js').CSVParseOptions} */
|
||||||
|
const csvOptions = {
|
||||||
|
cursor: _cursor,
|
||||||
|
separator: node.sep,
|
||||||
|
quote: node.quo,
|
||||||
|
dataHasHeaderRow: false,
|
||||||
|
headers: hasTemplate(template) ? template : null,
|
||||||
|
outputStyle: 'object',
|
||||||
|
includeNullValues: node.include_null_values,
|
||||||
|
includeEmptyStrings: node.include_empty_strings,
|
||||||
|
parseNumeric: node.parsestrings,
|
||||||
|
strict: false // relax the strictness of the parser for data rows
|
||||||
|
}
|
||||||
|
const csvParseResult = csv.parse(inputData, csvOptions)
|
||||||
|
const data = csvParseResult.data
|
||||||
|
|
||||||
|
// output results
|
||||||
|
if (node.multi !== "one") {
|
||||||
|
if (has_parts && noofCR <= 1) {
|
||||||
|
if (data.length > 0) {
|
||||||
|
node.store.push(...data)
|
||||||
|
}
|
||||||
|
if (msg.parts.index + 1 === msg.parts.count) {
|
||||||
|
msg.payload = node.store
|
||||||
|
msg.columns = csvParseResult.header
|
||||||
|
// msg._mode = 'RFC4180 mode'
|
||||||
|
delete msg.parts
|
||||||
|
send(msg)
|
||||||
|
node.store = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg.columns = csvParseResult.header
|
||||||
|
// msg._mode = 'RFC4180 mode'
|
||||||
|
msg.payload = data
|
||||||
|
send(msg); // finally send the array
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const len = data.length
|
||||||
|
for (let row = 0; row < len; row++) {
|
||||||
|
const newMessage = RED.util.cloneMessage(msg)
|
||||||
|
newMessage.columns = csvParseResult.header
|
||||||
|
newMessage.payload = data[row]
|
||||||
|
if (!has_parts) {
|
||||||
|
newMessage.parts = {
|
||||||
|
id: msg._msgid,
|
||||||
|
index: row,
|
||||||
|
count: len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newMessage.parts.index -= node.skip
|
||||||
|
newMessage.parts.count -= node.skip
|
||||||
|
if (node.hdrin) { // if we removed the header line then shift the counts by 1
|
||||||
|
newMessage.parts.index -= 1
|
||||||
|
newMessage.parts.count -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (last) { newMessage.complete = true }
|
||||||
|
// newMessage._mode = 'RFC4180 mode'
|
||||||
|
send(newMessage)
|
||||||
|
}
|
||||||
|
if (has_parts && last && len === 0) {
|
||||||
|
// send({complete:true, _mode: 'RFC4180 mode'})
|
||||||
|
send({ complete: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.linecount = 0
|
||||||
|
done()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
done(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// RFC-vs-legacy mode difference: In RFC mode, we throw catchable errors and provide a status message
|
||||||
|
const err = new Error(RED._("csv.errors.csv_js"))
|
||||||
|
node.status({ fill: "red", shape: "dot", text: err.message })
|
||||||
|
done(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else { node.warn(RED._("csv.errors.csv_js")); done(); }
|
else {
|
||||||
}
|
if (!msg.hasOwnProperty("reset")) {
|
||||||
else {
|
node.send(msg); // If no payload and not reset - just pass it on.
|
||||||
if (!msg.hasOwnProperty("reset")) {
|
}
|
||||||
node.send(msg); // If no payload and not reset - just pass it on.
|
done()
|
||||||
}
|
}
|
||||||
done();
|
})
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("csv",CSVNode);
|
|
||||||
|
RED.nodes.registerType("csv",CSVNode)
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@
|
|||||||
color:"#DEBD5C",
|
color:"#DEBD5C",
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
property: {value:"payload"},
|
property: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
|
||||||
outproperty: {value:"payload"},
|
outproperty: {value:"payload", validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }) },
|
||||||
tag: {value:""},
|
tag: {value:""},
|
||||||
ret: {value:"html"},
|
ret: {value:"html"},
|
||||||
as: {value:"single"}
|
as: {value:"single"}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
property: {value:"payload",required:true,
|
property: {value:"payload",required:true,
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true}),
|
||||||
label:RED._("node-red:json.label.property")},
|
label:RED._("node-red:json.label.property")},
|
||||||
action: {value:""},
|
action: {value:""},
|
||||||
pretty: {value:false}
|
pretty: {value:false}
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
property: {value:"payload",required:true,
|
property: {value:"payload",required:true,
|
||||||
label:RED._("node-red:common.label.property")},
|
label:RED._("node-red:common.label.property"),
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true })},
|
||||||
attr: {value:""},
|
attr: {value:""},
|
||||||
chr: {value:""}
|
chr: {value:""}
|
||||||
},
|
},
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
color:"#DEBD5C",
|
color:"#DEBD5C",
|
||||||
defaults: {
|
defaults: {
|
||||||
property: {value:"payload",required:true,
|
property: {value:"payload",required:true,
|
||||||
|
validate: RED.validators.typedInput({ type: 'msg', allowUndefined: true }),
|
||||||
label:RED._("node-red:common.label.property")},
|
label:RED._("node-red:common.label.property")},
|
||||||
name: {value:""}
|
name: {value:""}
|
||||||
},
|
},
|
||||||
|
324
packages/node_modules/@node-red/nodes/core/parsers/lib/csv/index.js
vendored
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CSVParseOptions
|
||||||
|
* @property {number} [cursor=0] - an index into the CSV to start parsing from
|
||||||
|
* @property {string} [separator=','] - the separator character
|
||||||
|
* @property {string} [quote='"'] - the quote character
|
||||||
|
* @property {boolean} [headersOnly=false] - only parse the headers and return them
|
||||||
|
* @property {string[]} [headers=[]] - an array of headers to use instead of the first row of the CSV data
|
||||||
|
* @property {boolean} [dataHasHeaderRow=true] - whether the CSV data to parse has a header row
|
||||||
|
* @property {boolean} [outputHeader=true] - whether the output data should include a header row (only applies to array output)
|
||||||
|
* @property {boolean} [parseNumeric=false] - parse numeric values into numbers
|
||||||
|
* @property {boolean} [includeNullValues=false] - include null values in the output
|
||||||
|
* @property {boolean} [includeEmptyStrings=true] - include empty strings in the output
|
||||||
|
* @property {string} [outputStyle='object'] - output an array of arrays or an array of objects
|
||||||
|
* @property {boolean} [strict=false] - throw an error if the CSV is malformed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a CSV string into an array of arrays or an array of objects.
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
* * Deviations from the RFC4180 spec (for the sake of user fiendliness, system implementations and flexibility), this parser will:
|
||||||
|
* * accept any separator character, not just `,`
|
||||||
|
* * accept any quote character, not just `"`
|
||||||
|
* * parse `\r`, `\n` or `\r\n` as line endings (RRFC4180 2.1 states lines are separated by CRLF)
|
||||||
|
* * Only single character `quote` is supported
|
||||||
|
* * `quote` is `"` by default
|
||||||
|
* * Any cell that contains a `quote` or `separator` will be quoted
|
||||||
|
* * Any `quote` characters inside a cell will be escaped as per RFC 4180 2.6
|
||||||
|
* * Only single character `separator` is supported
|
||||||
|
* * Only `array` and `object` output styles are supported
|
||||||
|
* * `array` output style is an array of arrays [[],[],[]]
|
||||||
|
* * `object` output style is an array of objects [{},{},{}]
|
||||||
|
* * Only `headers` or `dataHasHeaderRow` are supported, not both
|
||||||
|
* @param {string} csvIn - the CSV string to parse
|
||||||
|
* @param {CSVParseOptions} parseOptions - options
|
||||||
|
* @throws {Error}
|
||||||
|
*/
|
||||||
|
function parse(csvIn, parseOptions) {
|
||||||
|
/* Normalise options */
|
||||||
|
parseOptions = parseOptions || {};
|
||||||
|
const separator = parseOptions.separator ?? ',';
|
||||||
|
const quote = parseOptions.quote ?? '"';
|
||||||
|
const headersOnly = parseOptions.headersOnly ?? false;
|
||||||
|
const headers = Array.isArray(parseOptions.headers) ? parseOptions.headers : []
|
||||||
|
const dataHasHeaderRow = parseOptions.dataHasHeaderRow ?? true;
|
||||||
|
const outputHeader = parseOptions.outputHeader ?? true;
|
||||||
|
const parseNumeric = parseOptions.parseNumeric ?? false;
|
||||||
|
const includeNullValues = parseOptions.includeNullValues ?? false;
|
||||||
|
const includeEmptyStrings = parseOptions.includeEmptyStrings ?? true;
|
||||||
|
const outputStyle = ['array', 'object'].includes(parseOptions.outputStyle) ? parseOptions.outputStyle : 'object'; // 'array [[],[],[]]' or 'object [{},{},{}]
|
||||||
|
const strict = parseOptions.strict ?? false
|
||||||
|
|
||||||
|
/* Local variables */
|
||||||
|
const cursorMax = csvIn.length;
|
||||||
|
const ouputArrays = outputStyle === 'array';
|
||||||
|
const headersSupplied = headers.length > 0
|
||||||
|
// The original regex was an "is-a-number" positive logic test. /^ *[-]?(?!E)(?!0\d)\d*\.?\d*(E-?\+?)?\d+ *$/i;
|
||||||
|
// Below, is less strict and inverted logic but coupled with +cast it is 13%+ faster than original regex+parsefloat
|
||||||
|
// and has the benefit of understanding hexadecimals, binary and octal numbers.
|
||||||
|
const skipNumberConversion = /^ *(\+|-0\d|0\d)/
|
||||||
|
const cellBuilder = []
|
||||||
|
let rowBuilder = []
|
||||||
|
let cursor = typeof parseOptions.cursor === 'number' ? parseOptions.cursor : 0;
|
||||||
|
let newCell = true, inQuote = false, closed = false, output = [];
|
||||||
|
|
||||||
|
/* inline helper functions */
|
||||||
|
const finaliseCell = () => {
|
||||||
|
let cell = cellBuilder.join('')
|
||||||
|
cellBuilder.length = 0
|
||||||
|
// push the cell:
|
||||||
|
// NOTE: if cell is empty but newCell==true, then this cell had zero chars - push `null`
|
||||||
|
// otherwise push empty string
|
||||||
|
return rowBuilder.push(cell || (newCell ? null : ''))
|
||||||
|
}
|
||||||
|
const finaliseRow = () => {
|
||||||
|
if (cellBuilder.length) {
|
||||||
|
finaliseCell()
|
||||||
|
}
|
||||||
|
if (rowBuilder.length) {
|
||||||
|
output.push(rowBuilder)
|
||||||
|
rowBuilder = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main parsing loop */
|
||||||
|
while (cursor < cursorMax) {
|
||||||
|
const char = csvIn[cursor]
|
||||||
|
if (inQuote) {
|
||||||
|
if (char === quote && csvIn[cursor + 1] === quote) {
|
||||||
|
cellBuilder.push(quote)
|
||||||
|
cursor += 2;
|
||||||
|
newCell = false;
|
||||||
|
closed = false;
|
||||||
|
} else if (char === quote) {
|
||||||
|
inQuote = false;
|
||||||
|
cursor += 1;
|
||||||
|
newCell = false;
|
||||||
|
closed = true;
|
||||||
|
} else {
|
||||||
|
cellBuilder.push(char)
|
||||||
|
newCell = false;
|
||||||
|
closed = false;
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (char === separator) {
|
||||||
|
finaliseCell()
|
||||||
|
cursor += 1;
|
||||||
|
newCell = true;
|
||||||
|
closed = false;
|
||||||
|
} else if (char === quote) {
|
||||||
|
if (newCell) {
|
||||||
|
inQuote = true;
|
||||||
|
cursor += 1;
|
||||||
|
newCell = false;
|
||||||
|
closed = false;
|
||||||
|
}
|
||||||
|
else if (strict) {
|
||||||
|
throw new UnquotedQuoteError(cursor)
|
||||||
|
} else {
|
||||||
|
// not strict, keep 1 quote if the next char is not a cell/record separator
|
||||||
|
cursor++
|
||||||
|
if (csvIn[cursor] && csvIn[cursor] !== '\n' && csvIn[cursor] !== '\r' && csvIn[cursor] !== separator) {
|
||||||
|
cellBuilder.push(char)
|
||||||
|
if (csvIn[cursor] === quote) {
|
||||||
|
cursor++ // skip the next quote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (char === '\n' || char === '\r') {
|
||||||
|
finaliseRow()
|
||||||
|
if (csvIn[cursor + 1] === '\n') {
|
||||||
|
cursor += 2;
|
||||||
|
} else {
|
||||||
|
cursor++
|
||||||
|
}
|
||||||
|
newCell = true;
|
||||||
|
closed = false;
|
||||||
|
if (headersOnly) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (closed) {
|
||||||
|
if (strict) {
|
||||||
|
throw new DataAfterCloseError(cursor)
|
||||||
|
} else {
|
||||||
|
cursor--; // move back to grab the previously discarded char
|
||||||
|
closed = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cellBuilder.push(char)
|
||||||
|
newCell = false;
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strict && inQuote) {
|
||||||
|
throw new ParseError(`Missing quote, unclosed cell`, cursor)
|
||||||
|
}
|
||||||
|
// finalise the last cell/row
|
||||||
|
finaliseRow()
|
||||||
|
let firstRowIsHeader = false
|
||||||
|
// if no headers supplied, generate them
|
||||||
|
if (output.length >= 1) {
|
||||||
|
if (headersSupplied) {
|
||||||
|
// headers already supplied
|
||||||
|
} else if (dataHasHeaderRow) {
|
||||||
|
// take the first row as the headers
|
||||||
|
headers.push(...output[0])
|
||||||
|
firstRowIsHeader = true
|
||||||
|
} else {
|
||||||
|
// generate headers col1, col2, col3, etc
|
||||||
|
for (let i = 0; i < output[0].length; i++) {
|
||||||
|
headers.push("col" + (i + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalResult = {
|
||||||
|
/** @type {String[]} headers as an array of string */
|
||||||
|
headers: headers,
|
||||||
|
/** @type {String} headers as a comma-separated string */
|
||||||
|
header: null,
|
||||||
|
/** @type {Any[]} Result Data (may include header row: check `firstRowIsHeader` flag) */
|
||||||
|
data: [],
|
||||||
|
/** @type {Boolean|undefined} flag to indicate if the first row is a header row (only applies when `outputStyle` is 'array') */
|
||||||
|
firstRowIsHeader: undefined,
|
||||||
|
/** @type {'array'|'object'} flag to indicate the output style */
|
||||||
|
outputStyle: outputStyle,
|
||||||
|
/** @type {Number} The current cursor position */
|
||||||
|
cursor: cursor,
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedHeaders = []
|
||||||
|
for (let i = 0; i < headers.length; i++) {
|
||||||
|
if (!headers[i]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
quotedHeaders.push(quoteCell(headers[i], { quote, separator: ',' }))
|
||||||
|
}
|
||||||
|
finalResult.header = quotedHeaders.join(',') // always quote headers and join with comma
|
||||||
|
|
||||||
|
// output is an array of arrays [[],[],[]]
|
||||||
|
if (ouputArrays || headersOnly) {
|
||||||
|
if (!firstRowIsHeader && !headersOnly && outputHeader && headers.length > 0) {
|
||||||
|
if (output.length > 0) {
|
||||||
|
output.unshift(headers)
|
||||||
|
} else {
|
||||||
|
output = [headers]
|
||||||
|
}
|
||||||
|
firstRowIsHeader = true
|
||||||
|
}
|
||||||
|
if (headersOnly) {
|
||||||
|
delete finalResult.firstRowIsHeader
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
finalResult.firstRowIsHeader = firstRowIsHeader
|
||||||
|
finalResult.data = (firstRowIsHeader && !outputHeader) ? output.slice(1) : output
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// output is an array of objects [{},{},{}]
|
||||||
|
const outputObjects = []
|
||||||
|
let i = firstRowIsHeader ? 1 : 0
|
||||||
|
for (; i < output.length; i++) {
|
||||||
|
const rowObject = {}
|
||||||
|
let isEmpty = true
|
||||||
|
for (let j = 0; j < headers.length; j++) {
|
||||||
|
if (!headers[j]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let v = output[i][j] === undefined ? null : output[i][j]
|
||||||
|
if (v === null && !includeNullValues) {
|
||||||
|
continue
|
||||||
|
} else if (v === "" && !includeEmptyStrings) {
|
||||||
|
continue
|
||||||
|
} else if (parseNumeric === true && v && !skipNumberConversion.test(v)) {
|
||||||
|
const vTemp = +v
|
||||||
|
const isNumber = !isNaN(vTemp)
|
||||||
|
if(isNumber) {
|
||||||
|
v = vTemp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rowObject[headers[j]] = v
|
||||||
|
isEmpty = false
|
||||||
|
}
|
||||||
|
// determine if this row is empty
|
||||||
|
if (!isEmpty) {
|
||||||
|
outputObjects.push(rowObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalResult.data = outputObjects
|
||||||
|
delete finalResult.firstRowIsHeader
|
||||||
|
return finalResult
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quotes a cell in a CSV string if necessary. Addiionally, any double quotes inside the cell will be escaped as per RFC 4180 2.6 (https://datatracker.ietf.org/doc/html/rfc4180#section-2).
|
||||||
|
* @param {string} cell - the string to quote
|
||||||
|
* @param {*} options - options
|
||||||
|
* @param {string} [options.quote='"'] - the quote character
|
||||||
|
* @param {string} [options.separator=','] - the separator character
|
||||||
|
* @param {string[]} [options.quoteables] - an array of characters that, when encountered, will trigger the application of outer quotes
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function quoteCell(cell, { quote = '"', separator = ",", quoteables } = {
|
||||||
|
quote: '"',
|
||||||
|
separator: ",",
|
||||||
|
quoteables: [quote, separator, '\r', '\n']
|
||||||
|
}) {
|
||||||
|
quoteables = quoteables || [quote, separator, '\r', '\n'];
|
||||||
|
|
||||||
|
let doubleUp = false;
|
||||||
|
if (cell.indexOf(quote) !== -1) { // add double quotes if any quotes
|
||||||
|
doubleUp = true;
|
||||||
|
}
|
||||||
|
const quoteChar = quoteables.some(q => cell.includes(q)) ? quote : '';
|
||||||
|
return quoteChar + (doubleUp ? cell.replace(/"/g, '""') : cell) + quoteChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Custom Error Classes
|
||||||
|
class ParseError extends Error {
|
||||||
|
/**
|
||||||
|
* @param {string} message - the error message
|
||||||
|
* @param {number} cursor - the cursor index where the error occurred
|
||||||
|
*/
|
||||||
|
constructor(message, cursor) {
|
||||||
|
super(message)
|
||||||
|
this.name = 'ParseError'
|
||||||
|
this.cursor = cursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnquotedQuoteError extends ParseError {
|
||||||
|
/**
|
||||||
|
* @param {number} cursor - the cursor index where the error occurred
|
||||||
|
*/
|
||||||
|
constructor(cursor) {
|
||||||
|
super('Quote found in the middle of an unquoted field', cursor)
|
||||||
|
this.name = 'UnquotedQuoteError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataAfterCloseError extends ParseError {
|
||||||
|
/**
|
||||||
|
* @param {number} cursor - the cursor index where the error occurred
|
||||||
|
*/
|
||||||
|
constructor(cursor) {
|
||||||
|
super('Data found after closing quote', cursor)
|
||||||
|
this.name = 'DataAfterCloseError'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
exports.parse = parse
|
||||||
|
exports.quoteCell = quoteCell
|
||||||
|
exports.ParseError = ParseError
|
||||||
|
exports.UnquotedQuoteError = UnquotedQuoteError
|
||||||
|
exports.DataAfterCloseError = DataAfterCloseError
|
@ -60,7 +60,7 @@
|
|||||||
arraySplt: {value:1},
|
arraySplt: {value:1},
|
||||||
arraySpltType: {value:"len"},
|
arraySpltType: {value:"len"},
|
||||||
stream: {value:false},
|
stream: {value:false},
|
||||||
addname: {value:""},
|
addname: {value:"", validate: RED.validators.typedInput({ type: 'msg', allowBlank: true })},
|
||||||
property: {value:"payload",required:true}
|
property: {value:"payload",required:true}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
@ -216,7 +216,22 @@
|
|||||||
validate:RED.validators.typedInput("propertyType", false)
|
validate:RED.validators.typedInput("propertyType", false)
|
||||||
},
|
},
|
||||||
propertyType: { value:"msg"},
|
propertyType: { value:"msg"},
|
||||||
key: {value:"topic"},
|
key: {value:"topic", validate: (function () {
|
||||||
|
const typeValidator = RED.validators.typedInput({ type: 'msg' })
|
||||||
|
return function(v, opt) {
|
||||||
|
const joinMode = $("#node-input-mode").val() || this.mode
|
||||||
|
if (joinMode !== 'custom') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const buildType = $("#node-input-build").val() || this.build
|
||||||
|
if (buildType !== 'object') {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return typeValidator(v, opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
},
|
||||||
joiner: { value:"\\n"},
|
joiner: { value:"\\n"},
|
||||||
joinerType: { value:"str"},
|
joinerType: { value:"str"},
|
||||||
accumulate: { value:"false" },
|
accumulate: { value:"false" },
|
||||||
|
@ -198,7 +198,7 @@
|
|||||||
category: 'storage',
|
category: 'storage',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
filename: {value:""},
|
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' })},
|
||||||
filenameType: {value:"str"},
|
filenameType: {value:"str"},
|
||||||
appendNewline: {value:true},
|
appendNewline: {value:true},
|
||||||
createDir: {value:false},
|
createDir: {value:false},
|
||||||
@ -297,7 +297,7 @@
|
|||||||
category: 'storage',
|
category: 'storage',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
filename: {value:""},
|
filename: {value:"", validate: RED.validators.typedInput({ typeField: 'filenameType' }) },
|
||||||
filenameType: {value:"str"},
|
filenameType: {value:"str"},
|
||||||
format: {value:"utf8"},
|
format: {value:"utf8"},
|
||||||
chunk: {value:false},
|
chunk: {value:false},
|
||||||
|
Before Width: | Height: | Size: 603 B |
1
packages/node_modules/@node-red/nodes/icons/arduino.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff"><path d="m9.7884 22.379c-5.2427-0.41732-9.6475 5.7885-7.4975 10.585 2.0949 5.2041 9.9782 6.6154 13.727 2.4477 3.633-3.5613 5.0332-9.0411 9.4821-11.853 4.5205-3.0872 11.797-0.172 12.68 5.3144 0.86 5.2537-4.8017 10.364-9.9231 8.8205-3.7873-0.85449-6.5051-4.0905-8.0487-7.4975-1.9019-3.2526-4.3882-6.7257-8.2693-7.6077-0.6891-0.15656-1.4003-0.21831-2.1059-0.21721z" stroke-width="3.3"/><path d="m6.7012 29.821h6.6154" stroke-width="1.4"/><path d="m26.988 29.821h5.5128m-2.8115-2.7564v5.5128" stroke-width="1.8"/></g></svg>
|
After Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 2.3 KiB |
1
packages/node_modules/@node-red/nodes/icons/bluetooth.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m8.3474 17.75 22.298 22.444-10.747 13.013v-46.497l10.747 12.428-22.298 21.859" fill="none" stroke="#fff" stroke-width="4"/></svg>
|
After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 1.6 KiB |
1
packages/node_modules/@node-red/nodes/icons/leveldb.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m2.7078 12.986c0 7.7994-0.36386 21.569 0 32.545s35.118 9.8751 34.848 0c-0.26959-9.8751 0-24.82 0-32.545 0-7.7243-34.848-7.7995-34.848 0z" fill="none" stroke="#fff"/><g fill="#fff"><path d="m3.8741 13.406v8.955c0.021834 3.5781 19.543 5.0789 25.575 3.2543 0 0 0.02229-2.6683 0.02998-2.6673l5.5325 0.7238c0.64508 0.0844 1.1345-0.74597 1.134-1.3284v-8.573l-0.99896 0.93349-15.217-2.2765c4.5883 2.1798 9.808 4.1312 9.808 4.1312-9.3667 3.1562-25.846-0.31965-25.864-3.1525z"/><path d="m3.886 26.607v8.1052c3.2188 6.1087 29.901 5.8574 32.272 0v-8.1052c-3.3598 4.6685-29.204 5.1534-32.272 0z"/><path d="m4.0032 39.082v7.1522c2.556 7.4622 28.918 7.6072 32.272 0v-7.1522c-3.2345 4.9471-29.087 5.359-32.272 0z"/></g></svg>
|
After Width: | Height: | Size: 806 B |
Before Width: | Height: | Size: 414 B |
1
packages/node_modules/@node-red/nodes/icons/mongodb.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m23.515 13.831c-4.7594-5.8789-2.6084-5.7751-7.3474 0-8.0368 10.477-8.3322 24.431 2.5476 32.935 0.13181 2.0418 0.46056 4.9803 0.46056 4.9803h1.315s0.32875-2.9219 0.46017-4.9803c2.8458-2.2339 16.799-14.619 2.5641-32.935z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 335 B |
Before Width: | Height: | Size: 671 B |
1
packages/node_modules/@node-red/nodes/icons/mouse.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fff" stroke-width="3"><path d="m6 30c6 5 24 4 29-0.07"/><path d="m21 33 0.1-19c0.02-4 4-3 4-6s-4-2-4-5"/><path d="m6 22c0-11 29-10 29 0v21c0 18-29 19-29 0s4e-7 -11 0-21z"/></g></svg>
|
After Width: | Height: | Size: 293 B |
BIN
packages/node_modules/@node-red/nodes/icons/rbe.png
vendored
Before Width: | Height: | Size: 252 B |
1
packages/node_modules/@node-red/nodes/icons/rbe.svg
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="m29 12s0.1 30 0.05 31-3 5-7 5-19 0.04-19 0.04c6-4 9-5 17-5 0 0 4-0.1 4-2 0-2 8e-3 -29 8e-3 -29z" fill="#fff"/><path d="m12 47s-0.1-30-0.05-31 3-5 7-5 19-0.04 19-0.04c-6 4-9 5-17 5 0 0-4 0.1-4 2 0 2-8e-3 29-8e-3 29z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 331 B |