mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
272 Commits
1.1.0
...
fix-numeri
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e86f6a841a | ||
|
|
23f0cd3a26 | ||
|
|
74db3e17d0 | ||
|
|
7bde7f0cfd | ||
|
|
7068c175f2 | ||
|
|
9b5ed8407f | ||
|
|
37935bf388 | ||
|
|
70554e24b1 | ||
|
|
a0f736bb88 | ||
|
|
79473c243d | ||
|
|
441eb3bb29 | ||
|
|
ca44af0625 | ||
|
|
b0acb58442 | ||
|
|
06ceb056f3 | ||
|
|
abe77ab96f | ||
|
|
ea720bb4a5 | ||
|
|
6ee2e2b570 | ||
|
|
3885107e6e | ||
|
|
30a68fefec | ||
|
|
fa84c4e461 | ||
|
|
c433f736a5 | ||
|
|
55e6c6e01a | ||
|
|
496b5a092f | ||
|
|
02510efda1 | ||
|
|
be828af3e2 | ||
|
|
df1eb631e1 | ||
|
|
9f3e9786a8 | ||
|
|
c9bc530df0 | ||
|
|
0b569a4120 | ||
|
|
950fd7d2cf | ||
|
|
50dd0354d1 | ||
|
|
78f1cb8a66 | ||
|
|
4bfe9a9ae9 | ||
|
|
c5d38d8962 | ||
|
|
2f86bb1ca5 | ||
|
|
3999690062 | ||
|
|
d57edaa4c1 | ||
|
|
088419b38e | ||
|
|
8ebcee32c2 | ||
|
|
2b801a756a | ||
|
|
98b639540b | ||
|
|
e0b797fc7e | ||
|
|
795416a84d | ||
|
|
545dda166f | ||
|
|
f19ec5d9b6 | ||
|
|
6ea978d83d | ||
|
|
42f3b70a22 | ||
|
|
1cd10f074b | ||
|
|
bed1d31bc8 | ||
|
|
99478897c5 | ||
|
|
d79cd463a0 | ||
|
|
ccf4e73701 | ||
|
|
01b67c692b | ||
|
|
4023ab3f28 | ||
|
|
70f3b7450f | ||
|
|
ca4960e097 | ||
|
|
ebe604e1af | ||
|
|
32b04cd32f | ||
|
|
e149174696 | ||
|
|
f878ffc01b | ||
|
|
15b49f4db8 | ||
|
|
b1cc7b3296 | ||
|
|
65d90a6dff | ||
|
|
a58f4c2ec2 | ||
|
|
75d7ac2d8a | ||
|
|
468cfeffb6 | ||
|
|
6720c1aa46 | ||
|
|
280203e64e | ||
|
|
281d8b7cec | ||
|
|
2c6cda1f27 | ||
|
|
fa532da8c7 | ||
|
|
cbf84647de | ||
|
|
c38a490a6f | ||
|
|
9d7a450821 | ||
|
|
0ecd9673b8 | ||
|
|
97aa1230ef | ||
|
|
ff0be73b1f | ||
|
|
8049e44dec | ||
|
|
dc26022fb4 | ||
|
|
e8e44f9a32 | ||
|
|
12d56b8b03 | ||
|
|
e62fd7ed15 | ||
|
|
978eb95acd | ||
|
|
e34f4acb22 | ||
|
|
15a600c763 | ||
|
|
82ad5839fa | ||
|
|
9af883231d | ||
|
|
9bfe8ac007 | ||
|
|
f46367d77b | ||
|
|
eb2e1c0c45 | ||
|
|
baffc2d6ca | ||
|
|
96ab508c91 | ||
|
|
57e42659e3 | ||
|
|
f059e97697 | ||
|
|
516e6430eb | ||
|
|
f194a8ecf4 | ||
|
|
13f046f310 | ||
|
|
1edf5acb87 | ||
|
|
af636870d4 | ||
|
|
379b8ada61 | ||
|
|
5e63471983 | ||
|
|
086f0f8450 | ||
|
|
97a4b3dc2a | ||
|
|
4eb8d681c1 | ||
|
|
2066584164 | ||
|
|
a954c198fb | ||
|
|
bb1f8cd5e8 | ||
|
|
101e96dcb3 | ||
|
|
59adf82895 | ||
|
|
3b68f56b15 | ||
|
|
2962c4372c | ||
|
|
517e376582 | ||
|
|
7a90fe5aec | ||
|
|
ea45dde63a | ||
|
|
22a301b55e | ||
|
|
605177dcf0 | ||
|
|
460e1f5563 | ||
|
|
6f25337b99 | ||
|
|
08148a07b2 | ||
|
|
27c0e45940 | ||
|
|
bdd736315a | ||
|
|
d57ec0cd53 | ||
|
|
952c9d8bdb | ||
|
|
cf84ec78fa | ||
|
|
b595e84c30 | ||
|
|
6e5f115bd5 | ||
|
|
a1ab00c93e | ||
|
|
6e5c4e832e | ||
|
|
48ea487974 | ||
|
|
54dc98a90b | ||
|
|
c5bdd3d056 | ||
|
|
64d6e1f8e1 | ||
|
|
69d60ffb24 | ||
|
|
e6ffa3d143 | ||
|
|
bb4330e486 | ||
|
|
1a4d720978 | ||
|
|
91c2f479bb | ||
|
|
b61701fa17 | ||
|
|
cb9f910642 | ||
|
|
4b8d07f301 | ||
|
|
2db3a4f1ef | ||
|
|
ead1f65887 | ||
|
|
aae9866866 | ||
|
|
085ff84bc9 | ||
|
|
e12975cf0b | ||
|
|
a33cf6b532 | ||
|
|
5be25d9538 | ||
|
|
2b29eeb795 | ||
|
|
785561a0cc | ||
|
|
b46dc88346 | ||
|
|
96d81ef72b | ||
|
|
81dc3de26a | ||
|
|
4d0c572c2e | ||
|
|
fb2da0ee9e | ||
|
|
b8b0247717 | ||
|
|
103e212aee | ||
|
|
2f33575907 | ||
|
|
576c528573 | ||
|
|
3c444d3fb3 | ||
|
|
7cb499cde9 | ||
|
|
5a174ba014 | ||
|
|
041feb4e86 | ||
|
|
19726cf428 | ||
|
|
aaf134b1c5 | ||
|
|
9d5f5ee94b | ||
|
|
a48f0827ae | ||
|
|
5686158245 | ||
|
|
3824cdde68 | ||
|
|
e619b9bf7b | ||
|
|
b7243c2226 | ||
|
|
70b6674f44 | ||
|
|
ef67b8481e | ||
|
|
baffe4861c | ||
|
|
5cf489a270 | ||
|
|
44b1819926 | ||
|
|
d84c2b780b | ||
|
|
dc8991a1da | ||
|
|
7bd0ca2212 | ||
|
|
4dd619b8c6 | ||
|
|
3a86ab186c | ||
|
|
2f2a6367c2 | ||
|
|
be880c25f9 | ||
|
|
17812f0d77 | ||
|
|
0c5eae2349 | ||
|
|
3ad1803057 | ||
|
|
02c20e97b7 | ||
|
|
716dc781e4 | ||
|
|
d9900d8e4c | ||
|
|
3b9065b057 | ||
|
|
e73b748b95 | ||
|
|
b309161f00 | ||
|
|
b21667834e | ||
|
|
183fa59c83 | ||
|
|
0c5586ddfb | ||
|
|
33855bcb8b | ||
|
|
dc81b7a699 | ||
|
|
b0b2c32654 | ||
|
|
6f1ed76b4c | ||
|
|
f81cee0be2 | ||
|
|
bcd85b11a1 | ||
|
|
ec368ae3fd | ||
|
|
d28c264422 | ||
|
|
fba505bc90 | ||
|
|
c50ed2c328 | ||
|
|
7e11ff2b20 | ||
|
|
3fb83c46e2 | ||
|
|
763f2bd5c5 | ||
|
|
85edee288f | ||
|
|
1aa494a97a | ||
|
|
a8e7627184 | ||
|
|
d590bbdd2c | ||
|
|
80d65b5acb | ||
|
|
1d250f7491 | ||
|
|
dd741ec6d8 | ||
|
|
e741af6d55 | ||
|
|
e691b1b7c3 | ||
|
|
29142128f2 | ||
|
|
758f44e25f | ||
|
|
16c26d8098 | ||
|
|
a004c61afc | ||
|
|
a9d1a64c32 | ||
|
|
889224715b | ||
|
|
442b9d23f1 | ||
|
|
e4dd895709 | ||
|
|
82677c304e | ||
|
|
73d8dfe381 | ||
|
|
1177aa8aca | ||
|
|
0eda0a4935 | ||
|
|
a19dab0dc9 | ||
|
|
d8eb80b72e | ||
|
|
4f3a6821d1 | ||
|
|
77bd7541ca | ||
|
|
ca46bc5366 | ||
|
|
2e19bc07df | ||
|
|
3f4de43b67 | ||
|
|
0d0bf62fc4 | ||
|
|
3c8654fa25 | ||
|
|
756a6ec5aa | ||
|
|
98c7364924 | ||
|
|
62c01b59b2 | ||
|
|
43db1824be | ||
|
|
7f671c9f3f | ||
|
|
410009dd61 | ||
|
|
580cc00967 | ||
|
|
612c565cfd | ||
|
|
979c5351a8 | ||
|
|
97b7479081 | ||
|
|
1df2f5e96a | ||
|
|
0601833387 | ||
|
|
8b36279e52 | ||
|
|
32163d5f21 | ||
|
|
1c337f6817 | ||
|
|
6df26f2400 | ||
|
|
a9431a5aee | ||
|
|
5a3c832a98 | ||
|
|
c4b5bb22db | ||
|
|
2b5a976f35 | ||
|
|
5d7a625883 | ||
|
|
ae1ca85924 | ||
|
|
c9acfdb1d7 | ||
|
|
11ac8fbf13 | ||
|
|
dc541444ba | ||
|
|
8ea25bcd1c | ||
|
|
f5e46a663a | ||
|
|
64ec415a54 | ||
|
|
57154b2853 | ||
|
|
0243a902b2 | ||
|
|
6c04402a98 | ||
|
|
ef7c9b5c2a | ||
|
|
73448a6039 | ||
|
|
176a0ff99b | ||
|
|
b96d562700 |
29
.github/scripts/update-node-red-docker.js
vendored
Normal file
29
.github/scripts/update-node-red-docker.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const newVersion = require("../../package.json").version;
|
||||
|
||||
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
|
||||
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
|
||||
console.log(`Not updating for a non-stable release - ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const currentVersion = require("../../../node-red-docker/package.json").version;
|
||||
|
||||
console.log(`Update from ${currentVersion} to ${newVersion}`)
|
||||
|
||||
updateFile(__dirname+"/../../../node-red-docker/package.json", currentVersion, newVersion);
|
||||
updateFile(__dirname+"/../../../node-red-docker/docker-custom/package.json", currentVersion, newVersion);
|
||||
updateFile(__dirname+"/../../../node-red-docker/README.md", currentVersion, newVersion);
|
||||
|
||||
console.log(`::set-env name=newVersion::${newVersion}`);
|
||||
|
||||
function updateFile(path,from,to) {
|
||||
let contents = fs.readFileSync(path,"utf8");
|
||||
contents = contents.replace(new RegExp(from.replace(/\./g,"\\."),"g"), to);
|
||||
fs.writeFileSync(path, contents);
|
||||
}
|
||||
18
.github/scripts/update-node-red-website.js
vendored
Normal file
18
.github/scripts/update-node-red-website.js
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const newVersion = require("../../package.json").version;
|
||||
|
||||
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
|
||||
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
|
||||
console.log(`Not updating for a non-stable release - ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const path = __dirname+"/../../../node-red.github.io/index.html";
|
||||
let contents = fs.readFileSync(path, "utf8");
|
||||
contents = contents.replace(/<span class="node-red-latest-version">v\d+\.\d+\.\d+<\/span>/, `<span class="node-red-latest-version">v${newVersion}<\/span>` );
|
||||
fs.writeFileSync(path, contents);
|
||||
59
.github/workflows/build.yml
vendored
Normal file
59
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: PublishDockerImage
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
name: 'Update node-red-docker image'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out node-red repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'node-red'
|
||||
- name: Check out node-red-docker repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'node-red/node-red-docker'
|
||||
path: 'node-red-docker'
|
||||
- name: Check out node-red.github.io repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'node-red/node-red.github.io'
|
||||
path: 'node-red.github.io'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12'
|
||||
- run: node ./node-red/.github/scripts/update-node-red-docker.js
|
||||
- name: Create Docker Pull Request
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
with:
|
||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
path: 'node-red-docker'
|
||||
commit-message: 'Bump to ${{ env.newVersion }}'
|
||||
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
|
||||
body: |
|
||||
Updates the Node-RED Docker repo for the ${{ env.newVersion }} release.
|
||||
|
||||
Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`.
|
||||
|
||||
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
|
||||
- name: Create Website Pull Request
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
with:
|
||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
path: 'node-red.github.io'
|
||||
commit-message: 'Bump to ${{ env.newVersion }}'
|
||||
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
|
||||
body: |
|
||||
Updates the Node-RED Website repo for the ${{ env.newVersion }} release.
|
||||
|
||||
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -22,4 +22,5 @@ packages/node_modules/@node-red/editor-client/public
|
||||
!test/**/node_modules
|
||||
docs
|
||||
!packages/node_modules/**/docs
|
||||
.vscode
|
||||
.vscode
|
||||
.nyc_output
|
||||
|
||||
14
.travis.yml
14
.travis.yml
@@ -5,11 +5,17 @@ language: node_js
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "14"
|
||||
script:
|
||||
- ./node_modules/.bin/grunt && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
|
||||
# - scripts/install-ui-test-dependencies.sh && grunt test-ui
|
||||
before_script:
|
||||
- npm install -g coveralls
|
||||
- node_js: "12"
|
||||
script:
|
||||
- ./node_modules/.bin/grunt no-coverage
|
||||
- node_js: "10"
|
||||
script:
|
||||
- ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
|
||||
- scripts/install-ui-test-dependencies.sh && grunt test-ui
|
||||
before_script:
|
||||
- npm install -g istanbul coveralls
|
||||
- ./node_modules/.bin/grunt no-coverage
|
||||
- node_js: "8"
|
||||
script:
|
||||
- ./node_modules/.bin/grunt no-coverage
|
||||
|
||||
260
CHANGELOG.md
260
CHANGELOG.md
@@ -1,3 +1,263 @@
|
||||
### 1.2.8: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Ensure subflow help is picked up for palette tooltip Fixes #2834
|
||||
- Improve Ru locale (#2826) @alexk111
|
||||
- Fix scrollbars (#2825) @alexk111
|
||||
|
||||
Runtime
|
||||
|
||||
- Restrict project file access to inside the project directory
|
||||
- Validate user-provided language parameter before passing to i18n
|
||||
- Fix grunt release mkdir issue on Node.js 14 (#2827) @alexk111
|
||||
- Prevent crash when coreNodesDir is empty (#2831) @hardillb
|
||||
|
||||
Nodes
|
||||
|
||||
- Batch node: Fixing minor typo in node's documentation (#2848) @matthiasradde
|
||||
- Split node: Handle out of order messages as long as one of the messages has msg.parts.count set to the proper value (#2748) @s4ke
|
||||
|
||||
### 1.2.7: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Ensure subflow-scoped config nodes do not get moved on import Fixes #2789
|
||||
- Allow TypedInput to be disabled (#2752) @bartbutenaers
|
||||
- Allow userMenu to be explicitly enabled (#2805) @tfmf
|
||||
- Improvements to DE translation (#2192) @ketzu
|
||||
|
||||
|
||||
Runtime
|
||||
|
||||
- Handle `undefined` error passed to node.error (#2781) @johnwang71
|
||||
- Disable nyc coverage reporting on older node versions
|
||||
- Improve Editor API unit test coverage (#2777) @aaronmyatt
|
||||
|
||||
|
||||
Nodes
|
||||
|
||||
- Trigger: ensure timestamp option sends .now() at point of sending
|
||||
|
||||
|
||||
### 1.2.6: Maintenance Release
|
||||
|
||||
|
||||
Editor
|
||||
|
||||
- Update Japanese translations for 1.2.5 (#2764) @kazuhitoyokoi
|
||||
- Library: properly handle symlinked folders (#2768) @natcl
|
||||
|
||||
Runtime
|
||||
|
||||
- Support Windows paths when installing tarball by path name Fixes #2769
|
||||
- Fix unsecure command usage in GH Action
|
||||
|
||||
Nodes
|
||||
|
||||
- Update MQTT to latest to fix Node 8 URL breakage
|
||||
|
||||
|
||||
|
||||
|
||||
### 1.2.5: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix import of config nodes with unknown z property
|
||||
|
||||
Runtime
|
||||
|
||||
- Set ACTIONS_ALLOW_UNSECURE_COMMANDS in GH Action
|
||||
|
||||
### 1.2.4: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Support bigint types in Debug sidebar
|
||||
- Clear retained status of deleted nodes
|
||||
- Prevent needless retention of node status messages
|
||||
- Update projects dialogs to use TypedInput-cred input
|
||||
- Restore cursor position in TypedInput cred-mode
|
||||
- Ensure config nodes with invalid z are imported somewhere
|
||||
- Ensure user keyboard shortcuts override defaults Fixes #2753
|
||||
|
||||
Runtime
|
||||
|
||||
- Disable projects when flowFile passed into grunt dev
|
||||
- Add Russian Locale (#2761) (#2531) (@alexk111)
|
||||
- Add Japanese translation for http-in node (#2758) (@kazuhitoyokoi)
|
||||
|
||||
Nodes
|
||||
|
||||
- CSV: Fix CSV node repeating array output
|
||||
|
||||
### 1.2.3: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Disable 'use strict' checking in Function node Fixes #2743
|
||||
- Add gray/grey alternate options for status
|
||||
- Handle import errors on initial load and report to user
|
||||
- Only apply recovery tab on initial load Fixes #2731
|
||||
- Reinstate coveralls reporting to travis build
|
||||
- Update Japanese message catalogue for 1.2.3 release #2747 (@HiroyasuNishiyama)
|
||||
|
||||
Runtime
|
||||
|
||||
- Modify default settings comment (#2739)
|
||||
- Add mutex lock to saveSettings storage call Fixes #2736 (#2737)
|
||||
- Migrate to nyc instead of istanbul for code coverage
|
||||
- Move mosca to ui-test-dependencies
|
||||
- Remove " from npm install prefix option
|
||||
|
||||
### 1.2.2: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Prevent node z property getting set to 0 or ""
|
||||
- Only apply z-recovery logic to flow nodes
|
||||
- Fix api call to reload flows Fixes #2726
|
||||
- Remove bad z property from import config nodes
|
||||
|
||||
### 1.2.1: Maintenance Release
|
||||
|
||||
Runtime
|
||||
|
||||
- Fix race condition in .config file migration Fixes #2724
|
||||
|
||||
|
||||
### 1.2.0: Milestone Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix selection of link node not existing within active workspace #2722 (@HiroyasuNishiyama)
|
||||
- Fix import of merged flow
|
||||
- Fix width of upload button in Safari #2718 (@HiroyasuNishiyama)
|
||||
- Update Chinese translations #2719 (@JiyeYu)
|
||||
- Update Japanese translations needed for 1.2 #2710 (@kazuhitoyokoi)
|
||||
- Fix unexpected line break of sidebar tab name popover #2716 (@HiroyasuNishiyama)
|
||||
- i18n module refresh tooltip #2717 (@HiroyasuNishiyama)
|
||||
- Add better error message if context file gets corrupted
|
||||
- Update info text of function node #2714 (@HiroyasuNishiyama)
|
||||
- Use markdown editor if editText called with md mode
|
||||
- Prevent group actions when in non-default mouse mode
|
||||
|
||||
### 1.2.0-beta.1: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Detect importing duplicate nodes and help user resolve #2698
|
||||
- Allow sidebar tabs to be reordered #2655
|
||||
- Add tgz upload button to palette manager #2682
|
||||
- Add 'automatic' git workflow for projects #2035
|
||||
- Allow project version string to be edited
|
||||
- Sanitize unknown node type when displaying
|
||||
- Handle nodes with invalid z property Closes #2170
|
||||
- Outline: Ensure sf instance nodes update in outliner when import-replace sf
|
||||
- Outline: Ensure recovered nodes tab is added to outliner properly
|
||||
- Groups: Only recalculate group label offsets when needed
|
||||
- Groups: Reuse first group name/style when merging elements Fixes #2680
|
||||
- Groups: Fix copy/paste of node into active group Fixes #2686
|
||||
- ACE: Update ACE to 1.4.12-src-min-noconflict Fixes #1988
|
||||
- ACE: Add comment highlighting to JSONata and fix regex handling Closes #2701
|
||||
- ACE: Ensure errors in ACE NRJavaScript mode are on valid lines
|
||||
- Prevent Enter on search box from reloading page Fixes #2678
|
||||
- Allow toggleButton icons to be optional
|
||||
- Allow treeList to have a header component
|
||||
- Disable selection of FA icons when dbl clicking node
|
||||
|
||||
Runtime
|
||||
|
||||
- Add RED.hooks API for pluggable routing #2665
|
||||
- Add flows:* events and deprecate nodes-* events
|
||||
- Split .config.json into separate files #2794
|
||||
- Add support for file upload in /nodes api #2682
|
||||
- Add 'done' metric log for message tracing #2685 (@k-toumura)
|
||||
- Add mutex locking around /flow apis #2679
|
||||
- Default flowFilePretty to true if projects enabled
|
||||
- Replace Math.random with crypto.getBytes for session tokens
|
||||
- Fix `this` context when calling multiple event listeners Fixes #2692. #2693 (@mgroenhoff)
|
||||
- Add --userDir=/tmp/foo support to grunt dev
|
||||
- Skip loading node html if disableEditor set #2684
|
||||
- Update util.writeFile to write to tmp file before rename #2683
|
||||
- Fix getModuleFiles function to include path property #2705 (@t-kawamorita)
|
||||
- Update nodemon to latest so grunt dev task behaves
|
||||
- Improve jsdoc of util.getObjectProperty to clarify thrown error See #2703
|
||||
|
||||
Nodes
|
||||
|
||||
- Trigger: allow msg.delay to be used to set delay/loop interval #2707
|
||||
- Function: allow to send & log in its initialize code #2644 (@cinhcet)
|
||||
- MQTT: Update to MQTT 4.2.1 Closes #2694
|
||||
- Debug: Handle undefined value in Debug view of Array and Object Fixes #2696
|
||||
- Switch: Clarify empty rules in switch node documentation #2649 (@natcl) #2669 (@kazuhitoyokoi)
|
||||
- Updated core nodes to use Done callback #2653 (@k-toumura)
|
||||
- yaml, xml, json, html, http, template, range, link, status, catch, complete, inject
|
||||
|
||||
### 1.1.3: Maintenance Release
|
||||
|
||||
Editor
|
||||
- Fix vertical align of fa node icons Fixes #2670
|
||||
- Allow lasso selection to be restricted to active group
|
||||
- Make ctrl-click on nested group more intuitive
|
||||
- Fix copy/paste of nested groups
|
||||
- Add Set(iterable) polyfill for IE11
|
||||
- Support select-all inside active group
|
||||
- Improve performance of moving groups
|
||||
- Add additional check for git auth failure response Fixes #2656
|
||||
- german translation, wording (#2660) (#2666)
|
||||
- Remove filtering of duplicate fa icons
|
||||
- Show node help when switching node edit dialogs Fixes #2652
|
||||
- Ensure group theme picks up theme defaults properly Fixes #2651
|
||||
|
||||
Nodes
|
||||
- Clarify Switch node isEmpty help
|
||||
- HTTP In: handle application/cbor as binary
|
||||
|
||||
Runtime
|
||||
- Move runtime settings back to adminApi from editorApi Fixes #2662
|
||||
- Update Chinese message for debug node
|
||||
|
||||
### 1.1.2: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix all the touch screen issues Fixes #2647
|
||||
- Add RED.view.redrawStatus to avoid full redraw on update
|
||||
- Ensure node/group xrefs are consistent on import
|
||||
- Disable keyboard handler when dialogs are open
|
||||
- Ensure unknown nodes removed from outliner when node registers Fixes #2646
|
||||
|
||||
Runtime
|
||||
|
||||
- Allow Comms websocket auth to be done via token header Fixes #2642
|
||||
|
||||
### 1.1.1: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Set apiRootUrl for debug pop-out to load locales properly Fixes #2629, #2630
|
||||
- Update build-custom-theme to handle keyframes properly Fixes #2636
|
||||
- Remove hardcoded css and allow group to default from theme Fixes #2633
|
||||
- Add RED.view.DEBUG_SYNC_REDRAW to disable requestAnimationFrame References #2631
|
||||
- Fix up subflow port wiring
|
||||
- Ensure groups are removed when deleting subflows
|
||||
- Get group order right in history events to ensure proper handling
|
||||
- Prevent wiring to node with no corresponding port Fixes #2641
|
||||
- Avoid copying duplicate nodes to internal clipboard
|
||||
- Fix connecting wires to subflow status or io ports on touchscreen Fixes #2637
|
||||
|
||||
Runtime
|
||||
|
||||
- Authenticate websocket comms using user-provided token if present Fixes #2642
|
||||
|
||||
Nodes
|
||||
|
||||
- Delay: add words about independence of messages being delayed.
|
||||
- Debug: fix debug status to not loop, make migration more seamless, detect status type objects #2638
|
||||
- Debug: Update Japanese message for debug node #2645 (@kazuhitoyokoi)
|
||||
|
||||
### 1.1.0: Milestone Release
|
||||
|
||||
Editor
|
||||
|
||||
72
Gruntfile.js
72
Gruntfile.js
@@ -20,10 +20,16 @@ var sass = require("node-sass");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
var nodemonArgs = ["-v"];
|
||||
var nodemonArgs = ["-V"];
|
||||
var flowFile = grunt.option('flowFile');
|
||||
if (flowFile) {
|
||||
nodemonArgs.push(flowFile);
|
||||
process.env.NODE_RED_ENABLE_PROJECTS=false;
|
||||
}
|
||||
var userDir = grunt.option('userDir');
|
||||
if (userDir) {
|
||||
nodemonArgs.push("-u");
|
||||
nodemonArgs.push(userDir);
|
||||
}
|
||||
|
||||
var browserstack = grunt.option('browserstack');
|
||||
@@ -47,8 +53,8 @@ module.exports = function(grunt) {
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/unit/**/*_spec.js"]},
|
||||
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
|
||||
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
webdriver: {
|
||||
@@ -56,19 +62,19 @@ module.exports = function(grunt) {
|
||||
configFile: 'test/editor/wdio.conf.js'
|
||||
}
|
||||
},
|
||||
mocha_istanbul: {
|
||||
nyc: {
|
||||
options: {
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reportFormats: ['lcov','html'],
|
||||
print: 'both',
|
||||
istanbulOptions: ['--no-default-excludes', '-i','**/packages/node_modules/**']
|
||||
cwd: '.',
|
||||
include: ['packages/node_modules/**'],
|
||||
excludeNodeModules: false,
|
||||
exclude: ['packages/node_modules/@node-red/editor-client/**'],
|
||||
reporter: ['lcov', 'html','text-summary'],
|
||||
reportDir: 'coverage',
|
||||
all: true
|
||||
},
|
||||
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
|
||||
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
all: { cmd: false, args: ['grunt', 'simplemocha:all'] },
|
||||
core: { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] },
|
||||
nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] }
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
@@ -452,6 +458,7 @@ module.exports = function(grunt) {
|
||||
'packages/node_modules/@node-red/runtime/lib/index.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/api/*.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/events.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/hooks.js',
|
||||
'packages/node_modules/@node-red/util/**/*.js',
|
||||
'packages/node_modules/@node-red/editor-api/lib/index.js',
|
||||
'packages/node_modules/@node-red/editor-api/lib/auth/index.js'
|
||||
@@ -502,12 +509,10 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-nodemon');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-chmod');
|
||||
grunt.loadNpmTasks('grunt-jsonlint');
|
||||
grunt.loadNpmTasks('grunt-mocha-istanbul');
|
||||
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.loadNpmTasks('grunt-webdriver');
|
||||
}
|
||||
@@ -515,6 +520,26 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
|
||||
grunt.loadNpmTasks('grunt-npm-command');
|
||||
grunt.loadNpmTasks('grunt-mkdir');
|
||||
grunt.loadNpmTasks('grunt-simple-nyc');
|
||||
|
||||
grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () {
|
||||
const nodemon = require('nodemon');
|
||||
this.async();
|
||||
const options = this.options();
|
||||
options.script = this.data.script;
|
||||
let callback;
|
||||
if (options.callback) {
|
||||
callback = options.callback;
|
||||
delete options.callback;
|
||||
} else {
|
||||
callback = function(nodemonApp) {
|
||||
nodemonApp.on('log', function (event) {
|
||||
console.log(event.colour);
|
||||
});
|
||||
};
|
||||
}
|
||||
callback(nodemon(options));
|
||||
});
|
||||
|
||||
grunt.registerMultiTask('attachCopyright', function() {
|
||||
var files = this.data.src;
|
||||
@@ -596,11 +621,16 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('default',
|
||||
'Builds editor content then runs code style checks and unit tests on all components',
|
||||
['build','verifyPackageDependencies','jshint:editor','mocha_istanbul:all']);
|
||||
['build','verifyPackageDependencies','jshint:editor','nyc:all']);
|
||||
|
||||
grunt.registerTask('no-coverage',
|
||||
'Builds editor content then runs code style checks and unit tests on all components without code coverage',
|
||||
['build','verifyPackageDependencies','jshint:editor','simplemocha:all']);
|
||||
|
||||
|
||||
grunt.registerTask('test-core',
|
||||
'Runs code style check and unit tests on core runtime code',
|
||||
['build','mocha_istanbul:core']);
|
||||
['build','nyc:core']);
|
||||
|
||||
grunt.registerTask('test-editor',
|
||||
'Runs code style check on editor code',
|
||||
@@ -618,12 +648,12 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('test-nodes',
|
||||
'Runs unit tests on core nodes',
|
||||
['build','mocha_istanbul:nodes']);
|
||||
['build','nyc:nodes']);
|
||||
|
||||
grunt.registerTask('build',
|
||||
'Builds editor content',
|
||||
['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']);
|
||||
|
||||
|
||||
grunt.registerTask('build-dev',
|
||||
'Developer mode: build dev version',
|
||||
['clean:build','concat:build','concat:vendor','copy:build','sass:build','setDevEnv']);
|
||||
@@ -643,7 +673,7 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('coverage',
|
||||
'Run Istanbul code test coverage task',
|
||||
['build','mocha_istanbul:all']);
|
||||
['build','nyc:all']);
|
||||
|
||||
grunt.registerTask('docs',
|
||||
'Generates API documentation',
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright JS Foundation and other contributors, http://js.foundation
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
||||
@@ -67,4 +67,4 @@ It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-t
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright JS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.0.0 | :white_check_mark: |
|
||||
| 0.20.x | :white_check_mark: |
|
||||
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly.
|
||||
|
||||
56
package.json
56
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.8",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -26,7 +26,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "6.12.2",
|
||||
"ajv": "6.12.6",
|
||||
"async-mutex": "0.2.6",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.0",
|
||||
@@ -37,7 +38,7 @@
|
||||
"cookie-parser": "1.4.5",
|
||||
"cors": "2.8.5",
|
||||
"cron": "1.7.2",
|
||||
"denque": "1.4.1",
|
||||
"denque": "1.5.0",
|
||||
"express": "4.17.1",
|
||||
"express-session": "1.17.1",
|
||||
"fs-extra": "8.1.0",
|
||||
@@ -45,24 +46,24 @@
|
||||
"hash-sum": "2.0.0",
|
||||
"https-proxy-agent": "5.0.0",
|
||||
"i18next": "15.1.2",
|
||||
"iconv-lite": "0.5.1",
|
||||
"iconv-lite": "0.6.2",
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.14.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.3",
|
||||
"jsonata": "1.8.4",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"media-typer": "1.1.0",
|
||||
"memorystore": "1.6.2",
|
||||
"mime": "2.4.6",
|
||||
"moment-timezone": "^0.5.31",
|
||||
"mqtt": "2.18.8",
|
||||
"memorystore": "1.6.4",
|
||||
"mime": "2.4.7",
|
||||
"moment-timezone": "0.5.32",
|
||||
"mqtt": "4.2.6",
|
||||
"multer": "1.4.2",
|
||||
"mustache": "4.0.1",
|
||||
"mustache": "4.1.0",
|
||||
"node-red-admin": "^0.2.6",
|
||||
"node-red-node-rbe": "^0.2.9",
|
||||
"node-red-node-sentiment": "^0.1.6",
|
||||
"node-red-node-tail": "^0.1.0",
|
||||
"nopt": "4.0.3",
|
||||
"nopt": "5.0.0",
|
||||
"oauth2orize": "1.11.0",
|
||||
"on-headers": "1.0.2",
|
||||
"passport": "0.4.1",
|
||||
@@ -71,7 +72,8 @@
|
||||
"raw-body": "2.4.1",
|
||||
"request": "2.88.0",
|
||||
"semver": "6.3.0",
|
||||
"uglify-js": "3.9.4",
|
||||
"tar": "6.0.5",
|
||||
"uglify-js": "3.12.4",
|
||||
"when": "3.7.8",
|
||||
"ws": "6.2.1",
|
||||
"xml2js": "0.4.23"
|
||||
@@ -80,40 +82,38 @@
|
||||
"bcrypt": "3.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"marked": "0.8.2",
|
||||
"dompurify": "2.0.11",
|
||||
"grunt": "~1.0.4",
|
||||
"dompurify": "2.2.6",
|
||||
"grunt": "1.3.0",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-cli": "~1.3.2",
|
||||
"grunt-concurrent": "~2.3.1",
|
||||
"grunt-concurrent": "3.0.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-compress": "~1.5.0",
|
||||
"grunt-contrib-compress": "1.6.0",
|
||||
"grunt-contrib-concat": "~1.0.1",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-jshint": "~2.1.0",
|
||||
"grunt-contrib-uglify": "~4.0.1",
|
||||
"grunt-contrib-watch": "~1.1.0",
|
||||
"grunt-jsdoc": "^2.2.1",
|
||||
"grunt-jsdoc-to-markdown": "^4.0.0",
|
||||
"grunt-jsonlint": "~2.0.0",
|
||||
"grunt-mkdir": "~1.0.0",
|
||||
"grunt-mocha-istanbul": "5.0.2",
|
||||
"grunt-nodemon": "~0.4.2",
|
||||
"grunt-jsdoc": "2.4.1",
|
||||
"grunt-jsdoc-to-markdown": "5.0.0",
|
||||
"grunt-jsonlint": "2.1.3",
|
||||
"grunt-mkdir": "~1.1.0",
|
||||
"grunt-npm-command": "~0.1.2",
|
||||
"grunt-sass": "~3.1.0",
|
||||
"grunt-simple-mocha": "~0.4.1",
|
||||
"grunt-simple-nyc": "^3.0.1",
|
||||
"http-proxy": "1.18.1",
|
||||
"istanbul": "0.4.5",
|
||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||
"marked": "1.2.7",
|
||||
"minami": "1.2.3",
|
||||
"mocha": "^5.2.0",
|
||||
"mosca": "^2.8.3",
|
||||
"node-red-node-test-helper": "^0.2.5",
|
||||
"node-red-node-test-helper": "^0.2.6",
|
||||
"node-sass": "^4.14.1",
|
||||
"should": "^8.4.0",
|
||||
"nodemon": "2.0.6",
|
||||
"should": "13.2.3",
|
||||
"sinon": "1.17.7",
|
||||
"stoppable": "^1.1.0",
|
||||
"supertest": "3.4.2"
|
||||
"supertest": "5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
||||
@@ -21,15 +21,17 @@ var flows = require("./flows");
|
||||
var flow = require("./flow");
|
||||
var context = require("./context");
|
||||
var auth = require("../auth");
|
||||
var info = require("./settings");
|
||||
|
||||
var apiUtil = require("../util");
|
||||
|
||||
module.exports = {
|
||||
init: function(runtimeAPI) {
|
||||
init: function(settings,runtimeAPI) {
|
||||
flows.init(runtimeAPI);
|
||||
flow.init(runtimeAPI);
|
||||
nodes.init(runtimeAPI);
|
||||
context.init(runtimeAPI);
|
||||
info.init(settings,runtimeAPI);
|
||||
|
||||
var needsPermission = auth.needsPermission;
|
||||
|
||||
@@ -47,7 +49,14 @@ module.exports = {
|
||||
|
||||
// Nodes
|
||||
adminApp.get("/nodes",needsPermission("nodes.read"),nodes.getAll,apiUtil.errorHandler);
|
||||
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
|
||||
|
||||
if (!settings.editorTheme || !settings.editorTheme.palette || settings.editorTheme.palette.upload !== false) {
|
||||
const multer = require('multer');
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
adminApp.post("/nodes",needsPermission("nodes.write"),upload.single("tarball"),nodes.post,apiUtil.errorHandler);
|
||||
} else {
|
||||
adminApp.post("/nodes",needsPermission("nodes.write"),nodes.post,apiUtil.errorHandler);
|
||||
}
|
||||
adminApp.get(/^\/nodes\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalogs,apiUtil.errorHandler);
|
||||
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+\/[^\/]+)\/messages/,needsPermission("nodes.read"),nodes.getModuleCatalog,apiUtil.errorHandler);
|
||||
adminApp.get(/^\/nodes\/((@[^\/]+\/)?[^\/]+)$/,needsPermission("nodes.read"),nodes.getModule,apiUtil.errorHandler);
|
||||
@@ -67,6 +76,8 @@ module.exports = {
|
||||
// adminApp.delete("/context/:scope(node|flow)/:id",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
|
||||
adminApp.delete("/context/:scope(node|flow)/:id/*",needsPermission("context.write"),context.delete,apiUtil.errorHandler);
|
||||
|
||||
adminApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
|
||||
|
||||
return adminApp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,18 @@ module.exports = {
|
||||
module: req.body.module,
|
||||
version: req.body.version,
|
||||
url: req.body.url,
|
||||
tarball: undefined,
|
||||
req: apiUtils.getRequestLogObject(req)
|
||||
}
|
||||
if (!runtimeAPI.settings.editorTheme || !runtimeAPI.settings.editorTheme.palette || runtimeAPI.settings.editorTheme.palette.upload !== false) {
|
||||
if (req.file) {
|
||||
opts.tarball = {
|
||||
name: req.file.originalname,
|
||||
size: req.file.size,
|
||||
buffer: req.file.buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
runtimeAPI.nodes.addModule(opts).then(function(info) {
|
||||
res.json(info);
|
||||
}).catch(function(err) {
|
||||
|
||||
72
packages/node_modules/@node-red/editor-api/lib/admin/settings.js
vendored
Normal file
72
packages/node_modules/@node-red/editor-api/lib/admin/settings.js
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var apiUtils = require("../util");
|
||||
var runtimeAPI;
|
||||
var settings;
|
||||
var theme = require("../editor/theme");
|
||||
var clone = require("clone");
|
||||
|
||||
var i18n = require("@node-red/util").i18n
|
||||
|
||||
function extend(target, source) {
|
||||
var keys = Object.keys(source);
|
||||
var i = keys.length;
|
||||
while(i--) {
|
||||
var value = source[keys[i]]
|
||||
var type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) {
|
||||
target[keys[i]] = value;
|
||||
} else if (value === null) {
|
||||
if (target.hasOwnProperty(keys[i])) {
|
||||
delete target[keys[i]];
|
||||
}
|
||||
} else {
|
||||
// Object
|
||||
if (target.hasOwnProperty(keys[i])) {
|
||||
target[keys[i]] = extend(target[keys[i]],value);
|
||||
} else {
|
||||
target[keys[i]] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(_settings,_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
settings = _settings;
|
||||
},
|
||||
runtimeSettings: function(req,res) {
|
||||
var opts = {
|
||||
user: req.user
|
||||
}
|
||||
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
|
||||
if (!settings.disableEditor) {
|
||||
result.editorTheme = result.editorTheme||{};
|
||||
var themeSettings = theme.settings();
|
||||
if (themeSettings) {
|
||||
// result.editorTheme may already exist with the palette
|
||||
// disabled. Need to merge that into the receive settings
|
||||
result.editorTheme = extend(clone(themeSettings),result.editorTheme);
|
||||
}
|
||||
result.editorTheme.languages = i18n.availableLanguages("editor");
|
||||
}
|
||||
res.json(result);
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
@@ -123,38 +123,57 @@ AnonymousStrategy.prototype.authenticate = function(req) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function authenticateUserToken(req) {
|
||||
return new Promise( (resolve,reject) => {
|
||||
var token = null;
|
||||
var tokenHeader = Users.tokenHeader();
|
||||
if (Users.tokenHeader() === null) {
|
||||
// No custom user token provided. Fail the request
|
||||
reject();
|
||||
return;
|
||||
} else if (Users.tokenHeader() === 'authorization') {
|
||||
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
|
||||
token = req.headers.authorization.split(' ')[1];
|
||||
}
|
||||
} else {
|
||||
token = req.headers[Users.tokenHeader()];
|
||||
}
|
||||
if (token) {
|
||||
Users.tokens(token).then(function(user) {
|
||||
if (user) {
|
||||
resolve(user);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function TokensStrategy() {
|
||||
passport.Strategy.call(this);
|
||||
this.name = 'tokens';
|
||||
}
|
||||
util.inherits(TokensStrategy, passport.Strategy);
|
||||
TokensStrategy.prototype.authenticate = function(req) {
|
||||
var self = this;
|
||||
var token = null;
|
||||
if (Users.tokenHeader() === 'authorization') {
|
||||
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
|
||||
token = req.headers.authorization.split(' ')[1];
|
||||
}
|
||||
} else {
|
||||
token = req.headers[Users.tokenHeader()];
|
||||
}
|
||||
if (token) {
|
||||
Users.tokens(token).then(function(admin) {
|
||||
if (admin) {
|
||||
self.success(admin,{scope:admin.permissions});
|
||||
} else {
|
||||
self.fail(401);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.fail(401);
|
||||
}
|
||||
authenticateUserToken(req).then(user => {
|
||||
this.success(user,{scope:user.permissions});
|
||||
}).catch(err => {
|
||||
this.fail(401);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
bearerStrategy: bearerStrategy,
|
||||
clientPasswordStrategy: clientPasswordStrategy,
|
||||
passwordTokenExchange: passwordTokenExchange,
|
||||
anonymousStrategy: new AnonymousStrategy(),
|
||||
tokensStrategy: new TokensStrategy()
|
||||
tokensStrategy: new TokensStrategy(),
|
||||
authenticateUserToken: authenticateUserToken
|
||||
}
|
||||
|
||||
@@ -14,15 +14,7 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
function generateToken(length) {
|
||||
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
|
||||
var token = [];
|
||||
for (var i=0;i<length;i++) {
|
||||
token.push(c[Math.floor(Math.random()*c.length)]);
|
||||
}
|
||||
return token.join("");
|
||||
}
|
||||
|
||||
const crypto = require("crypto");
|
||||
|
||||
var storage;
|
||||
var sessionExpiryTime
|
||||
@@ -115,7 +107,7 @@ module.exports = {
|
||||
},
|
||||
create: function(user,client,scope) {
|
||||
return loadSessions().then(function() {
|
||||
var accessToken = generateToken(128);
|
||||
var accessToken = crypto.randomBytes(128).toString('base64');
|
||||
|
||||
var accessTokenExpiresAt = Date.now() + (sessionExpiryTime*1000);
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ var api = {
|
||||
authenticate: authenticate,
|
||||
default: getDefaultUser,
|
||||
tokens: getDefaultUser,
|
||||
tokenHeader: "authorization"
|
||||
tokenHeader: null
|
||||
}
|
||||
|
||||
function init(config) {
|
||||
@@ -111,6 +111,8 @@ function init(config) {
|
||||
api.tokens = config.tokens;
|
||||
if (config.tokenHeader && typeof config.tokenHeader === "string") {
|
||||
api.tokenHeader = config.tokenHeader.toLowerCase();
|
||||
} else {
|
||||
api.tokenHeader = "authorization";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
var ws = require("ws");
|
||||
var url = require("url");
|
||||
const crypto = require("crypto");
|
||||
|
||||
var log = require("@node-red/util").log; // TODO: separate module
|
||||
var Tokens;
|
||||
var Users;
|
||||
var Permissions;
|
||||
var Strategies;
|
||||
|
||||
var server;
|
||||
var settings;
|
||||
@@ -31,8 +33,6 @@ var activeConnections = [];
|
||||
|
||||
var anonymousUser;
|
||||
|
||||
var retained = {};
|
||||
|
||||
var heartbeatTimer;
|
||||
var lastSentTime;
|
||||
|
||||
@@ -44,6 +44,7 @@ function init(_server,_settings,_runtimeAPI) {
|
||||
Tokens.onSessionExpiry(handleSessionExpiry);
|
||||
Users = require("../auth/users");
|
||||
Permissions = require("../auth/permissions");
|
||||
Strategies = require("../auth/strategies");
|
||||
|
||||
}
|
||||
function handleSessionExpiry(session) {
|
||||
@@ -54,26 +55,19 @@ function handleSessionExpiry(session) {
|
||||
}
|
||||
})
|
||||
}
|
||||
function generateSession(length) {
|
||||
var c = "ABCDEFGHIJKLMNOPQRSTUZWXYZabcdefghijklmnopqrstuvwxyz1234567890";
|
||||
var token = [];
|
||||
for (var i=0;i<length;i++) {
|
||||
token.push(c[Math.floor(Math.random()*c.length)]);
|
||||
}
|
||||
return token.join("");
|
||||
}
|
||||
|
||||
function CommsConnection(ws) {
|
||||
this.session = generateSession(32);
|
||||
function CommsConnection(ws, user) {
|
||||
this.session = crypto.randomBytes(32).toString('base64');
|
||||
this.ws = ws;
|
||||
this.stack = [];
|
||||
this.user = null;
|
||||
this.user = user;
|
||||
this.lastSentTime = 0;
|
||||
var self = this;
|
||||
|
||||
log.audit({event: "comms.open"});
|
||||
log.trace("comms.open "+self.session);
|
||||
var pendingAuth = (settings.adminAuth != null);
|
||||
var preAuthed = !!user;
|
||||
var pendingAuth = !this.user && (settings.adminAuth != null);
|
||||
|
||||
if (!pendingAuth) {
|
||||
addActiveConnection(self);
|
||||
@@ -130,8 +124,16 @@ function CommsConnection(ws) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
completeConnection(null,null,false);
|
||||
Users.tokens(msg.auth).then(function(user) {
|
||||
if (user) {
|
||||
self.user = user;
|
||||
log.audit({event: "comms.auth",user:self.user});
|
||||
completeConnection(user.permissions,msg.auth,true);
|
||||
} else {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
completeConnection(null,null,false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -191,8 +193,8 @@ function start() {
|
||||
var commsPath = settings.httpAdminRoot || "/";
|
||||
commsPath = (commsPath.slice(0,1) != "/" ? "/":"") + commsPath + (commsPath.slice(-1) == "/" ? "":"/") + "comms";
|
||||
wsServer = new ws.Server({ noServer: true });
|
||||
wsServer.on('connection',function(ws) {
|
||||
var commsConnection = new CommsConnection(ws);
|
||||
wsServer.on('connection',function(ws, request, user) {
|
||||
var commsConnection = new CommsConnection(ws, user);
|
||||
});
|
||||
wsServer.on('error', function(err) {
|
||||
log.warn(log._("comms.error-server",{message:err.toString()}));
|
||||
@@ -201,8 +203,26 @@ function start() {
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const pathname = url.parse(request.url).pathname;
|
||||
if (pathname === commsPath) {
|
||||
if (Users.tokenHeader() !== null && request.headers[Users.tokenHeader()]) {
|
||||
// The user has provided custom token handling. For the websocket,
|
||||
// the token could be provided in two ways:
|
||||
// - as an http header (only possible with a reverse proxy setup)
|
||||
// - passed over the connected websock in an auth packet
|
||||
// If the header is present, verify the token. If not, use the auth
|
||||
// packet over the connected socket
|
||||
//
|
||||
Strategies.authenticateUserToken(request).then(user => {
|
||||
wsServer.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wsServer.emit('connection', ws, request, user);
|
||||
});
|
||||
}).catch(err => {
|
||||
log.audit({event: "comms.auth.fail"});
|
||||
socket.destroy();
|
||||
})
|
||||
return
|
||||
}
|
||||
wsServer.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wsServer.emit('connection', ws, request);
|
||||
wsServer.emit('connection', ws, request, null);
|
||||
});
|
||||
}
|
||||
// Don't destroy the socket as other listeners may want to handle the
|
||||
|
||||
@@ -103,7 +103,7 @@ module.exports = {
|
||||
editorApp.get('/credentials/:type/:id', needsPermission("credentials.read"),credentials.get,apiUtil.errorHandler);
|
||||
|
||||
// Settings
|
||||
editorApp.get("/settings",needsPermission("settings.read"),info.runtimeSettings,apiUtil.errorHandler);
|
||||
// Main /settings route is an admin route - see lib/admin/settings.js
|
||||
// User Settings
|
||||
editorApp.get("/settings/user",needsPermission("settings.read"),info.userSettings,apiUtil.errorHandler);
|
||||
// User Settings
|
||||
|
||||
@@ -39,9 +39,12 @@ module.exports = {
|
||||
},
|
||||
get: function(req,res) {
|
||||
var namespace = req.params[0];
|
||||
var lngs = req.query.lng;
|
||||
namespace = namespace.replace(/\.json$/,"");
|
||||
var lang = req.query.lng || i18n.defaultLang; //apiUtil.determineLangFromHeaders(req.acceptsLanguages() || []);
|
||||
if (/[^a-z\-\*]/i.test(lang)) {
|
||||
res.json({});
|
||||
return;
|
||||
}
|
||||
var prevLang = i18n.i.language;
|
||||
// Trigger a load from disk of the language if it is not the default
|
||||
i18n.i.changeLanguage(lang, function(){
|
||||
|
||||
@@ -137,6 +137,7 @@ module.exports = {
|
||||
req.body.hasOwnProperty('description') ||
|
||||
req.body.hasOwnProperty('dependencies')||
|
||||
req.body.hasOwnProperty('summary') ||
|
||||
req.body.hasOwnProperty('version') ||
|
||||
req.body.hasOwnProperty('files') ||
|
||||
req.body.hasOwnProperty('git')) {
|
||||
runtimeAPI.projects.updateProject(opts).then(function() {
|
||||
|
||||
@@ -16,56 +16,12 @@
|
||||
var apiUtils = require("../util");
|
||||
var runtimeAPI;
|
||||
var sshkeys = require("./sshkeys");
|
||||
var theme = require("./theme");
|
||||
var clone = require("clone");
|
||||
|
||||
var i18n = require("@node-red/util").i18n
|
||||
|
||||
function extend(target, source) {
|
||||
var keys = Object.keys(source);
|
||||
var i = keys.length;
|
||||
while(i--) {
|
||||
var value = source[keys[i]]
|
||||
var type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean' || Array.isArray(value)) {
|
||||
target[keys[i]] = value;
|
||||
} else if (value === null) {
|
||||
if (target.hasOwnProperty(keys[i])) {
|
||||
delete target[keys[i]];
|
||||
}
|
||||
} else {
|
||||
// Object
|
||||
if (target.hasOwnProperty(keys[i])) {
|
||||
target[keys[i]] = extend(target[keys[i]],value);
|
||||
} else {
|
||||
target[keys[i]] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
sshkeys.init(runtimeAPI);
|
||||
},
|
||||
runtimeSettings: function(req,res) {
|
||||
var opts = {
|
||||
user: req.user
|
||||
}
|
||||
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
|
||||
result.editorTheme = result.editorTheme||{};
|
||||
var themeSettings = theme.settings();
|
||||
if (themeSettings) {
|
||||
// result.editorTheme may already exist with the palette
|
||||
// disabled. Need to merge that into the receive settings
|
||||
result.editorTheme = extend(clone(themeSettings),result.editorTheme);
|
||||
}
|
||||
result.editorTheme.languages = i18n.availableLanguages("editor");
|
||||
res.json(result);
|
||||
});
|
||||
},
|
||||
userSettings: function(req, res) {
|
||||
var opts = {
|
||||
user: req.user
|
||||
|
||||
@@ -99,7 +99,7 @@ function init(settings,_server,storage,runtimeAPI) {
|
||||
adminApp.use(corsHandler);
|
||||
}
|
||||
|
||||
var adminApiApp = require("./admin").init(runtimeAPI);
|
||||
var adminApiApp = require("./admin").init(settings, runtimeAPI);
|
||||
adminApp.use(adminApiApp);
|
||||
} else {
|
||||
adminApp = null;
|
||||
|
||||
@@ -43,6 +43,10 @@ module.exports = {
|
||||
rejectHandler: function(req,res,err) {
|
||||
//TODO: why this when errorHandler also?!
|
||||
log.audit({event: "api.error",error:err.code||"unexpected_error",message:err.message||err.toString()},req);
|
||||
if (!err.code) {
|
||||
// by definition, an unexpected_error to log
|
||||
log.error(err);
|
||||
}
|
||||
var response = {
|
||||
code: err.code||"unexpected_error",
|
||||
message: err.message||err.toString()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.8",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,17 +16,18 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "1.1.0",
|
||||
"@node-red/editor-client": "1.1.0",
|
||||
"@node-red/util": "1.2.8",
|
||||
"@node-red/editor-client": "1.2.8",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.19.0",
|
||||
"clone": "2.1.2",
|
||||
"cors": "2.8.5",
|
||||
"express-session": "1.17.1",
|
||||
"express": "4.17.1",
|
||||
"memorystore": "1.6.2",
|
||||
"mime": "2.4.6",
|
||||
"mustache": "4.0.1",
|
||||
"memorystore": "1.6.4",
|
||||
"mime": "2.4.7",
|
||||
"multer": "1.4.2",
|
||||
"mustache": "4.1.0",
|
||||
"oauth2orize": "1.11.0",
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"label" : {
|
||||
"view" : {
|
||||
"view" : "Ansicht",
|
||||
"grid" : "Gitter",
|
||||
"grid" : "Raster",
|
||||
"showGrid" : "Raster anzeigen",
|
||||
"snapGrid" : "Am Raster ausrichten",
|
||||
"gridSize" : "Rastergröße",
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"color": "Color",
|
||||
"position": "Position",
|
||||
"enable": "Enable",
|
||||
"disable": "Disable"
|
||||
"disable": "Disable",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"type": {
|
||||
"string": "string",
|
||||
@@ -41,7 +42,8 @@
|
||||
"loadNodeCatalogs": "Loading Node catalogs",
|
||||
"loadNodes": "Loading Nodes __count__",
|
||||
"loadFlows": "Loading Flows",
|
||||
"importFlows": "Adding Flows to workspace"
|
||||
"importFlows": "Adding Flows to workspace",
|
||||
"importError": "<p>Error adding flows</p><p>__message__</p>"
|
||||
},
|
||||
"workspace": {
|
||||
"defaultName": "Flow __number__",
|
||||
@@ -197,6 +199,8 @@
|
||||
"flow_plural": "__count__ flows",
|
||||
"subflow": "__count__ subflow",
|
||||
"subflow_plural": "__count__ subflows",
|
||||
"replacedNodes": "__count__ node replaced",
|
||||
"replacedNodes_plural": "__count__ nodes replaced",
|
||||
"pasteNodes": "Paste flow json or",
|
||||
"selectFile": "select a file to import",
|
||||
"importNodes": "Import nodes",
|
||||
@@ -204,6 +208,8 @@
|
||||
"download": "Download",
|
||||
"importUnrecognised": "Imported unrecognised type:",
|
||||
"importUnrecognised_plural": "Imported unrecognised types:",
|
||||
"importDuplicate": "Imported duplicate node:",
|
||||
"importDuplicate_plural": "Imported duplicate nodes:",
|
||||
"nodesExported": "Nodes exported to clipboard",
|
||||
"nodesImported": "Imported:",
|
||||
"nodeCopied": "__count__ node copied",
|
||||
@@ -212,6 +218,9 @@
|
||||
"groupCopied_plural": "__count__ groups copied",
|
||||
"groupStyleCopied": "Group style copied",
|
||||
"invalidFlow": "Invalid flow: __message__",
|
||||
"recoveredNodes": "Recovered Nodes",
|
||||
"recoveredNodesInfo": "The nodes on this flow were missing a valid flow id when they were imported. They have been added to this flow so you can either restore or delete them.",
|
||||
"recoveredNodesNotification": "<p>Imported nodes without a valid flow id</p><p>They have been added to a new flow called '__flowName__'.</p>",
|
||||
"export": {
|
||||
"selected":"selected nodes",
|
||||
"current":"current flow",
|
||||
@@ -226,13 +235,19 @@
|
||||
},
|
||||
"import": {
|
||||
"import": "Import to",
|
||||
"importSelected": "Import selected",
|
||||
"importCopy": "Import copy",
|
||||
"viewNodes": "View nodes...",
|
||||
"newFlow": "new flow",
|
||||
"replace": "replace",
|
||||
"errors": {
|
||||
"notArray": "Input not a JSON Array",
|
||||
"itemNotObject": "Input not a valid flow - item __index__ not a node object",
|
||||
"missingId": "Input not a valid flow - item __index__ missing 'id' 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.",
|
||||
"conflictNotification2": "Select which nodes to import and whether to replace the existing nodes, or to import a copy of them."
|
||||
},
|
||||
"copyMessagePath": "Path copied",
|
||||
"copyMessageValue": "Value copied",
|
||||
@@ -532,6 +547,8 @@
|
||||
"sortAZ": "a-z",
|
||||
"sortRecent": "recent",
|
||||
"more": "+ __count__ more",
|
||||
"upload": "Upload module tgz file",
|
||||
"refresh": "Refresh module list",
|
||||
"errors": {
|
||||
"catalogLoadFailed": "<p>Failed to load node catalogue.</p><p>Check the browser console for more information</p>",
|
||||
"installFailed": "<p>Failed to install: __module__</p><p>__message__</p><p>Check the log for more information</p>",
|
||||
@@ -708,6 +725,12 @@
|
||||
"committerTip": "Leave blank to use system default",
|
||||
"userName": "Username",
|
||||
"email": "Email",
|
||||
"workflow": "Workflow",
|
||||
"workfowTip": "Choose your preferred git workflow",
|
||||
"workflowManual": "Manual",
|
||||
"workflowManualTip": "All changes must be manually committed under the 'history' sidebar",
|
||||
"workflowAuto": "Automatic",
|
||||
"workflowAutoTip": "Changes are committed automatically with every deploy",
|
||||
"sshKeys": "SSH Keys",
|
||||
"sshKeysTip": "Allows you to create secure connections to remote git repositories.",
|
||||
"add": "add key",
|
||||
@@ -1066,6 +1089,7 @@
|
||||
"en-US": "English",
|
||||
"ja": "Japanese",
|
||||
"ko": "Korean",
|
||||
"ru": "Russian",
|
||||
"zh-CN": "Chinese(Simplified)",
|
||||
"zh-TW": "Chinese(Traditional)"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"color": "色",
|
||||
"position": "配置",
|
||||
"enable": "有効",
|
||||
"disable": "無効"
|
||||
"disable": "無効",
|
||||
"upload": "アップロード"
|
||||
},
|
||||
"type": {
|
||||
"string": "文字列",
|
||||
@@ -41,7 +42,8 @@
|
||||
"loadNodeCatalogs": "ノードカタログを読み込み中",
|
||||
"loadNodes": "ノードを読み込み中 __count__",
|
||||
"loadFlows": "フローを読み込み中",
|
||||
"importFlows": "ワークスペースにフローを追加中"
|
||||
"importFlows": "ワークスペースにフローを追加中",
|
||||
"importError": "<p>フロー追加エラー</p><p>__message__</p>"
|
||||
},
|
||||
"workspace": {
|
||||
"defaultName": "フロー __number__",
|
||||
@@ -197,13 +199,17 @@
|
||||
"flow_plural": "__count__ 個のフロー",
|
||||
"subflow": "__count__ 個のサブフロー",
|
||||
"subflow_plural": "__count__ 個のサブフロー",
|
||||
"pasteNodes": "JSON形式のフローデータを貼り付けてください",
|
||||
"selectFile": "読み込むファイルを選択してください",
|
||||
"importNodes": "フローをクリップボートから読み込み",
|
||||
"replacedNodes": "置換された __count__ 個のノード",
|
||||
"replacedNodes_plural": "置換された __count__ 個のノード",
|
||||
"pasteNodes": "JSON形式のフローデータを貼り付け",
|
||||
"selectFile": "読み込むファイルを選択",
|
||||
"importNodes": "フローをクリップボードから読み込み",
|
||||
"exportNodes": "フローをクリップボードへ書き出し",
|
||||
"download": "ダウンロード",
|
||||
"importUnrecognised": "認識できない型が読み込まれました:",
|
||||
"importUnrecognised_plural": "認識できない型が読み込まれました:",
|
||||
"importDuplicate": "重複したノードを読み込みました:",
|
||||
"importDuplicate_plural": "重複したノードを読み込みました:",
|
||||
"nodesExported": "クリップボードへフローを書き出しました",
|
||||
"nodesImported": "読み込みました:",
|
||||
"nodeCopied": "__count__ 個のノードをコピーしました",
|
||||
@@ -212,6 +218,9 @@
|
||||
"groupCopied_plural": "__count__ 個のグループをコピーしました",
|
||||
"groupStyleCopied": "グループの形式をコピーしました",
|
||||
"invalidFlow": "不正なフロー: __message__",
|
||||
"recoveredNodes": "復旧したノード",
|
||||
"recoveredNodesInfo": "このフロー内のノードは読み込み時に、有効なフローIDがありませんでした。これらフローIDは、フローに追加されているため、復元または削除できます。",
|
||||
"recoveredNodesNotification": "<p>有効なフローIDを持たないノードが読み込まれました</p><p>これらノードは '__flowName__' という新しいフローへ追加されました。</p>",
|
||||
"export": {
|
||||
"selected": "選択したフロー",
|
||||
"current": "現在のタブ",
|
||||
@@ -226,13 +235,19 @@
|
||||
},
|
||||
"import": {
|
||||
"import": "読み込み先",
|
||||
"importSelected": "選択したノードを読み込み",
|
||||
"importCopy": "コピーを読み込み",
|
||||
"viewNodes": "ノードを参照...",
|
||||
"newFlow": "新規のタブ",
|
||||
"replace": "置換",
|
||||
"errors": {
|
||||
"notArray": "JSON形式の配列ではありません",
|
||||
"itemNotObject": "不正なフロー - __index__ 番目の要素はノードオブジェクトではありません",
|
||||
"missingId": "不正なフロー - __index__ 番目の要素に'id'プロパティがありません",
|
||||
"missingType": "不正なフロー - __index__ 番目の要素に'type'プロパティがありません"
|
||||
}
|
||||
},
|
||||
"conflictNotification1": "読み込もうとしているノードのいくつかは、既にワークスペース内に存在しています。",
|
||||
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。"
|
||||
},
|
||||
"copyMessagePath": "パスをコピーしました",
|
||||
"copyMessageValue": "値をコピーしました",
|
||||
@@ -532,6 +547,8 @@
|
||||
"sortAZ": "辞書順",
|
||||
"sortRecent": "日付順",
|
||||
"more": "+ さらに __count__ 個",
|
||||
"upload": "モジュールのtgzファイルをアップロード",
|
||||
"refresh": "モジュールリスト更新",
|
||||
"errors": {
|
||||
"catalogLoadFailed": "<p>ノードのカタログの読み込みに失敗しました。</p><p>詳細はブラウザのコンソールを確認してください。</p>",
|
||||
"installFailed": "<p>追加処理が失敗しました: __module__</p><p>__message__</p><p>詳細はログを確認してください。</p>",
|
||||
@@ -708,6 +725,12 @@
|
||||
"committerTip": "システムのデフォルトを使用する場合、空白のままにしてください",
|
||||
"userName": "ユーザ名",
|
||||
"email": "メールアドレス",
|
||||
"workflow": "ワークフロー",
|
||||
"workfowTip": "望ましいgitワークフローを選択してください",
|
||||
"workflowManual": "手動",
|
||||
"workflowManualTip": "全ての変更は「履歴」サイドバー内で手動でコミットする必要があります",
|
||||
"workflowAuto": "自動",
|
||||
"workflowAutoTip": "変更はデプロイの度に自動的にコミットされます",
|
||||
"sshKeys": "SSH キー",
|
||||
"sshKeysTip": "gitリポジトリへのセキュアな接続を作成できます。",
|
||||
"add": "キーを追加",
|
||||
@@ -1066,6 +1089,7 @@
|
||||
"en-US": "英語",
|
||||
"ja": "日本語",
|
||||
"ko": "韓国語",
|
||||
"ru": "ロシア語",
|
||||
"zh-CN": "中国語(簡体)",
|
||||
"zh-TW": "中国語(繁体)"
|
||||
}
|
||||
|
||||
1139
packages/node_modules/@node-red/editor-client/locales/ru/editor.json
vendored
Executable file
1139
packages/node_modules/@node-red/editor-client/locales/ru/editor.json
vendored
Executable file
File diff suppressed because it is too large
Load Diff
23
packages/node_modules/@node-red/editor-client/locales/ru/infotips.json
vendored
Executable file
23
packages/node_modules/@node-red/editor-client/locales/ru/infotips.json
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"info": {
|
||||
"tip0" : "Вы можете удалить выбранные узлы или провода с {{core:delete-selection}}",
|
||||
"tip1" : "Ищите узлы с {{core:search}}",
|
||||
"tip2" : "{{core:toggle-sidebar}} показывает/скрывает эту боковою панель",
|
||||
"tip3" : "Вы можете управлять палитрой узлов с помощью {{core:manage-palette}}",
|
||||
"tip4" : "Узлы конфигурации потока перечисляются на боковой панели. Доступ к списку можно получить из меню или с помощью {{core:show-config-tab}}",
|
||||
"tip5" : "Эти советы можно включить/выключить через настройки",
|
||||
"tip6" : "Перемещайте выбранные узлы клавишами [влево] [вверх] [вниз] и [вправо]. Удерживайте [Shift], чтобы увеличить шаг",
|
||||
"tip7" : "Перетаскивание узла на провод соединит его с обеих сторон",
|
||||
"tip8" : "Экспортируйте выбранные узлы или текущую вкладку с {{core:show-export-dialog}}",
|
||||
"tip9" : "Импортируйте поток, перетаскивая его JSON в редактор или с помощью {{core:show-import-dialog}}",
|
||||
"tip10" : "Нажмите [Shift], [кликните] по порту узла и перетаскивайте подключенные провода на другой узел",
|
||||
"tip11" : "Открывайте вкладку Информация с {{core:show-info-tab}} или вкладку Отладка с {{core:show-debug-tab}}",
|
||||
"tip12" : "Нажмите [ctrl] и [кликните] в рабочей области, чтобы открыть диалог быстрого добавления",
|
||||
"tip13" : "Нажмите [ctrl] и [кликните] по порту узла, чтобы начать быстрое подключение",
|
||||
"tip14" : "Нажмите [Shift] и [кликните] по узлу, чтобы выбрать все соединенные узлы",
|
||||
"tip15" : "Нажмите [ctrl] и [кликните] по узлу, чтобы добавить или убрать его из текущего выбора",
|
||||
"tip16" : "Переключайте вкладки потока с помощью {{core:show-previous-tab}} и {{core:show-next-tab}}",
|
||||
"tip17" : "Вы можете подтвердить изменения в редакторе узла с {{core:confirm-edit-tray}} или отменить их с {{core:cancel-edit-tray}}",
|
||||
"tip18" : "Нажатие {{core:edit-selected-node}} откроет редактор первого узла в текущем выборе"
|
||||
}
|
||||
}
|
||||
274
packages/node_modules/@node-red/editor-client/locales/ru/jsonata.json
vendored
Executable file
274
packages/node_modules/@node-red/editor-client/locales/ru/jsonata.json
vendored
Executable file
@@ -0,0 +1,274 @@
|
||||
{
|
||||
"$string": {
|
||||
"args": "arg[, prettify]",
|
||||
"desc": "Преобразует параметр `arg` в строку, используя следующие правила приведения:\n\n - Строки возвращаются как есть\n - Функции преобразуются в пустую строку\n - Числовая бесконечность и NaN выдают ошибку, поскольку они не могут быть представлены числом в JSON\n - Все остальные значения преобразуются в строку JSON с помощью функции `JSON.stringify`. Если значение `prettify` равно true, тогда будет сгенерирован \"отформатированный\" JSON. То есть каждое поле будет в отдельной строке, а строки будут иметь отступ в зависимости от глубины поля."
|
||||
},
|
||||
"$length": {
|
||||
"args": "str",
|
||||
"desc": "Возвращает количество символов в строке `str`. Выдается ошибка, если `str` не является строкой."
|
||||
},
|
||||
"$substring": {
|
||||
"args": "str, start[, length]",
|
||||
"desc": "Возвращает строку, содержащую символы из первого параметра `str`, начиная с позиции `start` (отсчет с нуля). Если указан `length`, то подстрока будет содержать максимум `length` символов. Если `start` отрицателен, то это означает количество символов с конца `str`."
|
||||
},
|
||||
"$substringBefore": {
|
||||
"args": "str, chars",
|
||||
"desc": "Возвращает подстроку перед первым вхождением последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
|
||||
},
|
||||
"$substringAfter": {
|
||||
"args": "str, chars",
|
||||
"desc": "Возвращает подстроку после первого вхождения последовательности символов `chars` в строке `str`. Если `str` не содержит `chars`, то он возвращает `str`."
|
||||
},
|
||||
"$uppercase": {
|
||||
"args": "str",
|
||||
"desc": "Возвращает строку со всеми символами `str`, преобразованными в верхний регистр."
|
||||
},
|
||||
"$lowercase": {
|
||||
"args": "str",
|
||||
"desc": "Возвращает строку со всеми символами `str`, преобразованными в нижний регистр."
|
||||
},
|
||||
"$trim": {
|
||||
"args": "str",
|
||||
"desc": "Нормализует и обрезает все пробельные символы в строке `str`, выполняя следующие шаги:\n\n - Все символы табуляции, возврата каретки и перевода строки заменяются пробелами.\n- Последовательности пробелов сокращаются до одного пробела.\n- Пробелы в начале и конце `str` удаляются.\n\n Если `str` не указан (то есть эта функция вызывается без аргументов), тогда значение контекста используется в качестве значения `str`. Выдается ошибка, если `str` не является строкой."
|
||||
},
|
||||
"$contains": {
|
||||
"args": "str, pattern",
|
||||
"desc": "Возвращает `true`, если строка `str` соответствует шаблону `pattern`, в противном случае возвращает `false`. Если `str` не указан (то есть эта функция вызывается с одним аргументом), то значение контекста используется как значение `str`. Параметр `pattern` может быть либо строкой, либо регулярным выражением."
|
||||
},
|
||||
"$split": {
|
||||
"args": "str[, separator][, limit]",
|
||||
"desc": "Разбивает строку `str` на массив подстрок. Выдает ошибку, если `str` не является строкой. Необязательный параметр `separator` (строка или регулярное выражение) указывает символы внутри строки `str`, относительно которых она должна быть разделена. Если `separator` не указан, то предполагается пустая строка, и `str` будет разбит на массив из отдельных символов. Выдает ошибку, если `separator` не является строкой. Необязательный параметр `limit` - это число, указывающее максимальное количество подстрок для включения в результирующий массив. Любые дополнительные подстроки отбрасываются. Если `limit` не указан, то весь `str` разделяется без ограничения размера результирующего массива. Выдает ошибку, если `limit` не является положительным числом."
|
||||
},
|
||||
"$join": {
|
||||
"args": "array[, separator]",
|
||||
"desc": "Объединяет массив подстрок в одну объединенную строку, в которой каждая подстрока отделена необязательным параметром `separator`. Выдает ошибку, если входной массив содержит элемент, который не является строкой. Если `separator` не указан, то предполагается, что это пустая строка, то есть нет `separator` между подстроками. Выдает ошибку, если `separator` не является строкой."
|
||||
},
|
||||
"$match": {
|
||||
"args": "str, pattern [, limit]",
|
||||
"desc": "Применяет строку `str` к регулярному выражению `pattern` и возвращает массив объектов, каждый из которых содержит информацию о каждом совпадении внутри `str`."
|
||||
},
|
||||
"$replace": {
|
||||
"args": "str, pattern, replacement [, limit]",
|
||||
"desc": "Находит вхождения шаблона `pattern` в строке `str` и заменяет их на строку `replacement`.\n\nНеобязательный параметр `limit` - это максимальное количество замен."
|
||||
},
|
||||
"$now": {
|
||||
"args":"",
|
||||
"desc":"Создает отметку времени в формате, совместимом с ISO 8601, и возвращает ее как строку."
|
||||
},
|
||||
"$base64encode": {
|
||||
"args":"string",
|
||||
"desc":"Преобразует ASCII-строку в base-64 кодировку. Каждый символ в строке обрабатывается как байт двоичных данных. Для этого необходимо, чтобы все символы в строке находились в диапазоне от 0x00 до 0xFF, который включает все символы строк в URI-кодировке. Символы Юникода за пределами этого диапазона не поддерживаются."
|
||||
},
|
||||
"$base64decode": {
|
||||
"args":"string",
|
||||
"desc":"Преобразует байты в кодировке base-64 в строку, используя кодовую страницу Юникод UTF-8."
|
||||
},
|
||||
"$number": {
|
||||
"args": "arg",
|
||||
"desc": "Преобразует параметр `arg` в число с использованием следующих правил приведения:\n\n - Числа возвращаются как есть\n - Строки, которые содержат последовательность символов, представляющих допустимое в JSON число, преобразуются в это число\n - Все остальные значения вызывают ошибку."
|
||||
},
|
||||
"$abs": {
|
||||
"args":"number",
|
||||
"desc":"Возвращает абсолютное значение числа `number`."
|
||||
},
|
||||
"$floor": {
|
||||
"args":"number",
|
||||
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое меньше или равно `number`."
|
||||
},
|
||||
"$ceil": {
|
||||
"args":"number",
|
||||
"desc":"Возвращает значение числа `number`, округленное до ближайшего целого числа, которое больше или равно `number`."
|
||||
},
|
||||
"$round": {
|
||||
"args":"number [, precision]",
|
||||
"desc":"Возвращает значение числа `number`, округленное до количества десятичных знаков, указанных необязательным параметром `precision`."
|
||||
},
|
||||
"$power": {
|
||||
"args":"base, exponent",
|
||||
"desc":"Возвращает значение числа `base`, возведенное в степень `exponent`."
|
||||
},
|
||||
"$sqrt": {
|
||||
"args":"number",
|
||||
"desc":"Возвращает квадратный корень из значения числа `number`."
|
||||
},
|
||||
"$random": {
|
||||
"args":"",
|
||||
"desc":"Возвращает псевдослучайное число, которе больше или равно нулю и меньше единицы."
|
||||
},
|
||||
"$millis": {
|
||||
"args":"",
|
||||
"desc":"Возвращает число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу) в виде числа. Все вызовы `$millis()` в пределах выполнения выражения будут возвращать одно и то же значение."
|
||||
},
|
||||
"$sum": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает арифметическую сумму массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
|
||||
},
|
||||
"$max": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает максимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
|
||||
},
|
||||
"$min": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает минимальное число в массиве чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
|
||||
},
|
||||
"$average": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает среднее значение массива чисел `array`. Вызывает ошибку, если входной массив `array` содержит элемент, который не является числом."
|
||||
},
|
||||
"$boolean": {
|
||||
"args": "arg",
|
||||
"desc": "Приводит аргумент к логическому значению, используя следующие правила: \n\n - Логические значения возвращаются как есть\n - пустая строка: `false`\n - непустая строка: `true`\n - число равное `0`: `false`\n - ненулевое число: `true`\n - `null` : `false`\n - пустой массив: `false`\n - массив, который содержит хотя бы один элемент, приводимый к `true`: `true`\n - массив, все элементы которого приводятся к `false`: `false`\n - пустой объект: `false`\n - непустой объект: `true`\n - функция: `false`"
|
||||
},
|
||||
"$not": {
|
||||
"args": "arg",
|
||||
"desc": "Возвращает логическое НЕ для аргумента. `arg` сначала приводится к логическому значению"
|
||||
},
|
||||
"$exists": {
|
||||
"args": "arg",
|
||||
"desc": "Возвращает логическое `true`, если выполнение выражения `arg` возвращает значение, или `false`, если выражение ничему не соответствует (например, путь к несуществующему полю)."
|
||||
},
|
||||
"$count": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает количество элементов в массиве"
|
||||
},
|
||||
"$append": {
|
||||
"args": "array, array",
|
||||
"desc": "Присоединяет один массив к другому"
|
||||
},
|
||||
"$sort": {
|
||||
"args":"array [, function]",
|
||||
"desc":"Возвращает массив, содержащий все значения параметра `array`, но отсортированные по порядку.\n\nЕсли указан компаратор `function`, то это должна быть функция, которая принимает два параметра:\n\n`function(val1, val2)`\n\nЭту функцию вызывает алгоритм сортировки для сравнения двух значений: val1 и val2. Если значение val1 следует поместить после значения val2 в желаемом порядке сортировки, то функция должна возвращать логическое значение `true`, чтобы обозначить замену. В противном случае она должна вернуть `false`."
|
||||
},
|
||||
"$reverse": {
|
||||
"args":"array",
|
||||
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но в обратном порядке."
|
||||
},
|
||||
"$shuffle": {
|
||||
"args":"array",
|
||||
"desc":"Возвращает массив, содержащий все значения из параметра `array`, но перемешанный в случайном порядке."
|
||||
},
|
||||
"$zip": {
|
||||
"args":"array, ...",
|
||||
"desc":"Возвращает свернутый (сжатый) массив, содержащий сгруппированные массивы значений из аргументов `array1` … `arrayN` по индексам 0, 1, 2...."
|
||||
},
|
||||
"$keys": {
|
||||
"args": "object",
|
||||
"desc": "Возвращает массив, содержащий ключи объекта. Если аргумент является массивом объектов, то возвращаемый массив содержит недублированный список всех ключей из всех объектов."
|
||||
},
|
||||
"$lookup": {
|
||||
"args": "object, key",
|
||||
"desc": "Возвращает значение, связанное с ключом в объекте. Если первый аргумент является массивом объектов, то просходит поиск по всем объектам в массиве, и возвращаются значения, связанные со всеми вхождениями ключа."
|
||||
},
|
||||
"$spread": {
|
||||
"args": "object",
|
||||
"desc": "Разбивает объект, содержащий пары ключ / значение, на массив объектов, каждый из которых имеет одну пару ключ / значение из входного объекта. Если параметр является массивом объектов, то результирующий массив содержит объект для каждой пары ключ / значение из каждого объекта предоставленного массива."
|
||||
},
|
||||
"$merge": {
|
||||
"args": "array<object>",
|
||||
"desc": "Объединяет массив объектов в один объект, содержащий все пары ключ / значение каждого из объектов входного массива. Если какой-либо из входных объектов содержит один и тот же ключ, возвращаемый объект будет содержать значение последнего в массиве. Вызывает ошибку, если входной массив содержит элемент, который не является объектом."
|
||||
},
|
||||
"$sift": {
|
||||
"args":"object, function",
|
||||
"desc":"Возвращает объект, который содержит только пары ключ / значение из параметра `object`, которые удовлетворяют предикату `function`, переданному в качестве второго параметра.\n\n`function`, которая передается в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, key [, object]])`"
|
||||
},
|
||||
"$each": {
|
||||
"args":"object, function",
|
||||
"desc":"Возвращает массив, который содержит значения, возвращаемые функцией `function` при применении к каждой паре ключ/значение из объекта `object`."
|
||||
},
|
||||
"$map": {
|
||||
"args":"array, function",
|
||||
"desc":"Возвращает массив, содержащий результаты применения функции `function` к каждому значению массива `array`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
|
||||
},
|
||||
"$filter": {
|
||||
"args":"array, function",
|
||||
"desc":"Возвращает массив, содержащий только те значения из массива `array`, которые удовлетворяют предикату `function`.\n\nФункция `function`, указанная в качестве второго параметра, должна иметь следующую сигнатуру:\n\n`function(value [, index [, array]])`"
|
||||
},
|
||||
"$reduce": {
|
||||
"args":"array, function [, init]",
|
||||
"desc":"Возвращает агрегированное значение, полученное в результате последовательного применения функции `function` к каждому значению в массиве в сочетании с результатом от предыдущего применения функции.\n\nФункция должна принимать два аргумента и вести себя как инфиксный оператор между каждым значением в массиве `array`. Сигнатура `function` должна иметь форму: `myfunc($accumulator, $value[, $index[, $array]])`\n\nНеобязательный параметр `init` используется в качестве начального значения в агрегации."
|
||||
},
|
||||
"$flowContext": {
|
||||
"args": "string[, string]",
|
||||
"desc": "Извлекает свойство контекста потока.\n\nЭто функция от Node-RED."
|
||||
},
|
||||
"$globalContext": {
|
||||
"args": "string[, string]",
|
||||
"desc": "Извлекает свойство глобального контекста.\n\nЭто функция от Node-RED."
|
||||
},
|
||||
"$pad": {
|
||||
"args": "string, width [, char]",
|
||||
"desc": "Возвращает копию строки `string` с дополнительным заполнением, если необходимо, чтобы общее количество символов как минимум соответствовало абсолютному значению параметра `width`.\n\nЕсли `width` является положительным числом, то строка дополняется справа; если отрицательным, то дополняется слева.\n\nНеобязательный аргумент `char` указывает символ(ы) для заполнения. Если не указано, по умолчанию используется пробел."
|
||||
},
|
||||
"$fromMillis": {
|
||||
"args": "number",
|
||||
"desc": "Преобразует число, представляющее миллисекунды с начала Unix-эпохи (1 января 1970 года по Гринвичу), в строку отметки времени в формате ISO 8601."
|
||||
},
|
||||
"$formatNumber": {
|
||||
"args": "number, picture [, options]",
|
||||
"desc": "Преобразует число `number` в строку и форматирует ее в десятичное представление, как указано в строке `picture`.\n\nПоведение этой функции соответствует XPath/XQuery-функции fn:format-number, как определено в спецификация XPath F&O 3.1. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и fn:format-number.\n\nНеобязательный третий аргумент `options` используется для переопределения символов форматирования, специфичных для локали по умолчанию, таких как десятичный разделитель. Если аргумент указан, то это должен быть объект, содержащий пары имя/значение, указанные в разделе десятичного формата спецификации XPath F&O 3.1."
|
||||
},
|
||||
"$formatBase": {
|
||||
"args": "number [, radix]",
|
||||
"desc": "Преобразует число `number` в строку и форматирует ее в целое число, представленное в системе счисления, указанной аргументом `radix`. Если `radix` не указан, то по умолчанию используется десятичная. Значение 'radix` может быть от 2 до 36, в противном случае выдается ошибка."
|
||||
},
|
||||
"$toMillis": {
|
||||
"args": "timestamp",
|
||||
"desc": "Преобразует строку `timestamp` в формате ISO 8601 в число миллисекунд с начала Unix-эпохи (1 января 1970 года по Гринвичу). Вызывает ошибку, если строка в неправильном формате."
|
||||
},
|
||||
"$env": {
|
||||
"args": "arg",
|
||||
"desc": "Возвращает значение переменной среды.\n\nЭто функция от Node-RED."
|
||||
},
|
||||
"$eval": {
|
||||
"args": "expr [, context]",
|
||||
"desc": "Анализирует и исполняет строку `expr`, которая содержит JSON или выражение JSONata, используя текущий контекст в качестве контекста для исполнения."
|
||||
},
|
||||
"$formatInteger": {
|
||||
"args": "number, picture",
|
||||
"desc": "Преобразует число `number` в строку и форматирует ее в целочисленное представление, как указано в строке `picture`. Строка `picture` определяет, как форматируется число и имеет тот же синтаксис, что и `fn:format-integer` из спецификации XPath F&O 3.1."
|
||||
},
|
||||
"$parseInteger": {
|
||||
"args": "string, picture",
|
||||
"desc": "Разбирает содержимое строки `string` в целое число (как число JSON), используя формат, указанный в строке `picture`. Строковый параметр `picture` имеет тот же формат, что и `$formatInteger`."
|
||||
},
|
||||
"$error": {
|
||||
"args": "[str]",
|
||||
"desc": "Вызывает ошибку с сообщением. Необязательная строка `str` заменяет сообщение по умолчанию $error() function evaluated`"
|
||||
},
|
||||
"$assert": {
|
||||
"args": "arg, str",
|
||||
"desc": "Если значение `arg` равно true, функция возвращает значение undefined. Если значение `arg` равно false, генерируется исключение с `str` в качестве сообщения об исключении."
|
||||
},
|
||||
"$single": {
|
||||
"args": "array, function",
|
||||
"desc": "Возвращает одно-единственное значение из массива `array`, которое удовлетворяет предикату `function` (то есть когда `function` возвращает логическое `true` при передаче значения). Выдает исключение, если число подходящих значений не одно.\n\nФункция должна соответствовать следующей сигнатуре: `function(value [, index [, array]])` где value - элемент массива, index - позиция этого значения, а весь массив передается в качестве третьего аргумента"
|
||||
},
|
||||
"$encodeUrl": {
|
||||
"args": "str",
|
||||
"desc": "Кодирует компонент Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrlComponent(\"?x=test\")` => `\"%3Fx%3Dtest\"`"
|
||||
},
|
||||
"$encodeUrlComponent": {
|
||||
"args": "str",
|
||||
"desc": "Кодирует Uniform Resource Locator (URL), заменяя каждый экземпляр определенных символов одной, двумя, тремя или четырьмя escape-последовательностями, представляющими кодировку UTF-8 символа.\n\nПример: `$encodeUrl(\"https://mozilla.org/?x=шеллы\")` => `\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\"`"
|
||||
},
|
||||
"$decodeUrl": {
|
||||
"args": "str",
|
||||
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrlComponent.\n\nПример: `$decodeUrlComponent(\"%3Fx%3Dtest\")` => `\"?x=test\"`"
|
||||
},
|
||||
"$decodeUrlComponent": {
|
||||
"args": "str",
|
||||
"desc": "Декодирует компонент Uniform Resource Locator (URL), ранее созданный с помощью encodeUrl. \n\nПример: `$decodeUrl(\"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B\")` => `\"https://mozilla.org/?x=шеллы\"`"
|
||||
},
|
||||
"$distinct": {
|
||||
"args": "array",
|
||||
"desc": "Возвращает массив содержащий все элементы из массива `array`, с удаленными дупликатами"
|
||||
},
|
||||
"$type": {
|
||||
"args": "value",
|
||||
"desc": "Возвращает тип значения `value` в виде строки. Если `value` не определено, то будет возвращено `undefined`"
|
||||
},
|
||||
"$moment": {
|
||||
"args": "[str]",
|
||||
"desc": "Получает date объект, используя библиотеку Moment."
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@
|
||||
"color": "颜色",
|
||||
"position": "位置",
|
||||
"enable": "启用",
|
||||
"disable": "禁用"
|
||||
"disable": "禁用",
|
||||
"upload": "上传"
|
||||
},
|
||||
"type": {
|
||||
"string": "字符串",
|
||||
@@ -197,6 +198,8 @@
|
||||
"flow_plural": "__count__ 个流程",
|
||||
"subflow": "__count__ 个子流程",
|
||||
"subflow_plural": "__count__ 子流程",
|
||||
"replacedNodes": "__count__ 个节点被置换",
|
||||
"replacedNodes_plural": "__count__ 个节点被置换",
|
||||
"pasteNodes": "在这里粘贴节点",
|
||||
"selectFile": "选择要导入的文件",
|
||||
"importNodes": "导入节点",
|
||||
@@ -212,6 +215,9 @@
|
||||
"groupCopied_plural": "已复制 __count__ 个groups",
|
||||
"groupStyleCopied": "已复制组风格",
|
||||
"invalidFlow": "无效的流程: __message__",
|
||||
"recoveredNodes": "复原的节点",
|
||||
"recoveredNodesInfo": "导入节点时,此流上的节点缺少有效的流ID。 它们已被添加到此流中,您可以复原或删除它们。",
|
||||
"recoveredNodesNotification": "<p>导入的节点缺少有效的流ID</p><p>已将它们添加到名为 '__flowName__'的新流中。</p>",
|
||||
"export": {
|
||||
"selected": "已选择的节点",
|
||||
"current": "现在的节点",
|
||||
@@ -226,13 +232,19 @@
|
||||
},
|
||||
"import": {
|
||||
"import": "导入到",
|
||||
"importSelected": "导入所选项",
|
||||
"importCopy": "导入副本",
|
||||
"viewNodes": "查看节点",
|
||||
"newFlow": "新流程",
|
||||
"replace": "置换",
|
||||
"errors": {
|
||||
"notArray": "输入的不是JSON数组",
|
||||
"itemNotObject": "输入的流无效 - 项目 __index__ 不是节点对象",
|
||||
"missingId": "输入的流无效-项 __index__ 缺少'id'属性",
|
||||
"missingType": "输入的流程无效-项 __index__ 缺少'类型'属性"
|
||||
}
|
||||
},
|
||||
"conflictNotification1": "您要导入的某些节点已经存在于工作空间中。",
|
||||
"conflictNotification2": "选择要导入的节点,并确认要替换现有的节点还是导入它们的副本"
|
||||
},
|
||||
"copyMessagePath": "已复制路径",
|
||||
"copyMessageValue": "已复制数值",
|
||||
@@ -533,6 +545,7 @@
|
||||
"sortAZ": "a-z顺序",
|
||||
"sortRecent": "日期顺序",
|
||||
"more": "增加 __count__ 个",
|
||||
"upload": "上传模块tgz文件",
|
||||
"errors": {
|
||||
"catalogLoadFailed": "无法加载节点目录。<br>查看浏览器控制台了解更多信息",
|
||||
"installFailed": "无法安装: __module__<br>__message__<br>查看日志了解更多信息",
|
||||
@@ -709,6 +722,12 @@
|
||||
"committerTip": "保留空白以使用系统默认值",
|
||||
"userName": "用户名",
|
||||
"email": "电子邮件",
|
||||
"workflow": "工作流",
|
||||
"workfowTip": "选择您偏好的工作流",
|
||||
"workflowManual": "手动",
|
||||
"workflowManualTip": "所有更改都必须在“历史记录”侧边栏中手动提交",
|
||||
"workflowAuto": "自动",
|
||||
"workflowAutoTip": "每次部署后都会自动提交更改",
|
||||
"sshKeys": "SSH密钥",
|
||||
"sshKeysTip": "允许您创建到远程git存储库的安全连接。",
|
||||
"add": "添加密钥",
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"color": "顏色",
|
||||
"position": "位置",
|
||||
"enable": "啟用",
|
||||
"disable": "禁用"
|
||||
"disable": "禁用",
|
||||
"upload": "上傳"
|
||||
},
|
||||
"type": {
|
||||
"string": "字符串",
|
||||
@@ -197,6 +198,8 @@
|
||||
"flow_plural": "__count__ 多流程",
|
||||
"subflow": "__count__ 子流程",
|
||||
"subflow_plural": "__count__ 多子流程",
|
||||
"replacedNodes": "__count__ 個節點被置換",
|
||||
"replacedNodes_plural": "__count__ 個節點被置換",
|
||||
"pasteNodes": "在這裡粘貼節點",
|
||||
"selectFile": "匯入所選檔案",
|
||||
"importNodes": "匯入節點",
|
||||
@@ -212,6 +215,9 @@
|
||||
"groupCopied_plural": "已複製 __count__ 個groups",
|
||||
"groupStyleCopied": "已複製組風格",
|
||||
"invalidFlow": "無效的流程: __message__",
|
||||
"recoveredNodes": "復原的節點",
|
||||
"recoveredNodesInfo": "導入節點時,此流上的節點缺少有效的流ID。它們已被添加到此流中,您可以復原或刪除它們。",
|
||||
"recoveredNodesNotification": "<p>導入的節點缺少有效的流ID</p><p>已將它們添加到名為 '__flowName__'的新流中。</p>",
|
||||
"export": {
|
||||
"selected": "已選擇的節點",
|
||||
"current": "現在的節點",
|
||||
@@ -226,13 +232,19 @@
|
||||
},
|
||||
"import": {
|
||||
"import": "匯入到",
|
||||
"importSelected": "導入所選項",
|
||||
"importCopy": "導入副本",
|
||||
"viewNodes": "查看節點",
|
||||
"newFlow": "新流程",
|
||||
"replace": "置換",
|
||||
"errors": {
|
||||
"notArray": "輸入的不是JSON數組",
|
||||
"itemNotObject": "輸入的流程無效-項目 __index__ 不是節點對象",
|
||||
"missingId": "輸入的流程無效-項 __index__ 缺少“ id”屬性",
|
||||
"missingType": "輸入的流程無效-項 __index__ 缺少“類型”屬性"
|
||||
}
|
||||
},
|
||||
"conflictNotification1": "您要導入的某些節點已經存在於工作空間中。",
|
||||
"conflictNotification2": "選擇要導入的節點,並確認要替換現有的節點還是導入它們的副本"
|
||||
},
|
||||
"copyMessagePath": "已複製路徑",
|
||||
"copyMessageValue": "已複製數值",
|
||||
@@ -533,6 +545,7 @@
|
||||
"sortAZ": "a-z順序",
|
||||
"sortRecent": "日期順序",
|
||||
"more": "增加 __count__ 個",
|
||||
"upload": "上傳模塊tgz文件",
|
||||
"errors": {
|
||||
"catalogLoadFailed": "無法載入節點目錄。<br>查看瀏覽器控制臺瞭解更多資訊",
|
||||
"installFailed": "無法安裝: __module__<br>__message__<br>查看日誌瞭解更多資訊",
|
||||
@@ -709,6 +722,12 @@
|
||||
"committerTip": "保留空白以使用系統默認值",
|
||||
"userName": "用戶名",
|
||||
"email": "電子郵件",
|
||||
"workflow": "工作流",
|
||||
"workfowTip": "選擇您偏好的工作流",
|
||||
"workflowManual": "手動",
|
||||
"workflowManualTip": "所有更改都必須在“歷史記錄”側邊欄中手動提交",
|
||||
"workflowAuto": "自動",
|
||||
"workflowAutoTip": "每次部署後都會自動提交更改",
|
||||
"sshKeys": "SSH密鑰",
|
||||
"sshKeysTip": "允許您創建到遠程git存儲庫的安全連接。",
|
||||
"add": "添加密鑰",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.8",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
||||
ace.define("ace/snippets/nrjavascript",[],function(e,t,n){"use strict";t.snippetText='# Prototype\nsnippet proto\n ${1:class_name}.prototype.${2:method_name} = function(${3:first_argument}) {\n ${4:// body...}\n };\n# Function\nsnippet fun\n function ${1?:function_name}(${2:argument}) {\n ${3:// body...}\n }\n# Anonymous Function\nregex /((=)\\s*|(:)\\s*|(\\()|\\b)/f/(\\))?/\nsnippet f\n function${M1?: ${1:functionName}}($2) {\n ${0:$TM_SELECTED_TEXT}\n }${M2?;}${M3?,}${M4?)}\n# Immediate function\ntrigger \\(?f\\(\nendTrigger \\)?\nsnippet f(\n (function(${1}) {\n ${0:${TM_SELECTED_TEXT:/* code */}}\n }(${1}));\n# if\nsnippet if\n if (${1:true}) {\n ${0}\n }\n# if ... else\nsnippet ife\n if (${1:true}) {\n ${2}\n } else {\n ${0}\n }\n# tertiary conditional\nsnippet ter\n ${1:/* condition */} ? ${2:a} : ${3:b}\n# switch\nsnippet switch\n switch (${1:expression}) {\n case \'${3:case}\':\n ${4:// code}\n break;\n ${5}\n default:\n ${2:// code}\n }\n# case\nsnippet case\n case \'${1:case}\':\n ${2:// code}\n break;\n ${3}\n\n# while (...) {...}\nsnippet wh\n while (${1:/* condition */}) {\n ${0:/* code */}\n }\n# try\nsnippet try\n try {\n ${0:/* code */}\n } catch (e) {}\n# do...while\nsnippet do\n do {\n ${2:/* code */}\n } while (${1:/* condition */});\n# Object Method\nsnippet :f\nregex /([,{[])|^\\s*/:f/\n ${1:method_name}: function(${2:attribute}) {\n ${0}\n }${3:,}\n# setTimeout function\nsnippet setTimeout\nregex /\\b/st|timeout|setTimeo?u?t?/\n setTimeout(function() {${3:$TM_SELECTED_TEXT}}, ${1:10});\n# console.log (Firebug)\nsnippet cl\n console.log(${1});\n# return\nsnippet ret\n return ${1:result}\n# for (property in object ) { ... }\nsnippet fori\n for (var ${1:prop} in ${2:Things}) {\n ${0:$2[$1]}\n }\n# hasOwnProperty\nsnippet has\n hasOwnProperty(${1})\n# docstring\nsnippet /**\n /**\n * ${1:description}\n *\n */\nsnippet @par\nregex /^\\s*\\*\\s*/@(para?m?)?/\n @param {${1:type}} ${2:name} ${3:description}\nsnippet @ret\n @return {${1:type}} ${2:description}\n# JSON.parse\nsnippet jsonp\n JSON.parse(${1:jstr});\n# JSON.stringify\nsnippet jsons\n JSON.stringify(${1:object});\n# self-defining function\nsnippet sdf\n var ${1:function_name} = function(${2:argument}) {\n ${3:// initial code ...}\n\n $1 = function($2) {\n ${4:// main code}\n };\n }\n# \nsnippet for-\n for (var ${1:i} = ${2:Things}.length; ${1:i}--; ) {\n ${0:${2:Things}[${1:i}];}\n }\n# for (...) {...}\nsnippet for\n for (var ${1:i} = 0; $1 < ${2:Things}.length; $1++) {\n ${3:$2[$1]}$0\n }\n# for (...) {...} (Improved Native For-Loop)\nsnippet forr\n for (var ${1:i} = ${2:Things}.length - 1; $1 >= 0; $1--) {\n ${3:$2[$1]}$0\n }\n# Node-RED Specific Funcs\nsnippet nodes\n node.send(${1:msg})\nsnippet clone\n RED.util.cloneMessage(${1:msg})\nsnippet nodel\n node.log($1)\nsnippet nodew\n node.warn($1)\nsnippet nodee\n node.error($1)\nsnippet noded\n node.debug($1)\nsnippet done\n node.done($1)\nsnippet flowg\n flow.get($1)\nsnippet flows\n flow.set($1, $2)\nsnippet globalg\n global.get($1)\nsnippet globals\n global.set($1, $2)\n',t.scope="nrjavascript"});
|
||||
(function() {
|
||||
; (function() {
|
||||
ace.require(["ace/snippets/nrjavascript"], function(m) {
|
||||
if (typeof module == "object" && typeof exports == "object" && module) {
|
||||
module.exports = m;
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -76,13 +76,12 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
|
||||
(function() {
|
||||
this.setOptions = function(options) {
|
||||
this.options = options || {
|
||||
this.options = {
|
||||
// undef: true,
|
||||
// unused: true,
|
||||
esnext: true,
|
||||
moz: true,
|
||||
esversion: 9,
|
||||
devel: true,
|
||||
browser: true,
|
||||
browser: false,
|
||||
node: true,
|
||||
laxcomma: true,
|
||||
laxbreak: true,
|
||||
@@ -92,8 +91,17 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
maxerr: 100,
|
||||
expr: true,
|
||||
multistr: true,
|
||||
globalstrict: true
|
||||
strict: false,
|
||||
sub: true,
|
||||
asi: true
|
||||
};
|
||||
if (options) {
|
||||
for (var opt in options) {
|
||||
if (options.hasOwnProperty(opt)) {
|
||||
this.options[opt] = options.opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.doc.getValue() && this.deferredUpdate.schedule(100);
|
||||
};
|
||||
|
||||
@@ -119,6 +127,8 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
if (!value)
|
||||
return this.sender.emit("annotate", []);
|
||||
|
||||
var originalValue = value;
|
||||
|
||||
// [Node-RED] wrap the code in a function
|
||||
value = "async function __nodered__(msg) {\n"+value+"\n}";
|
||||
|
||||
@@ -138,6 +148,7 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
continue;
|
||||
var raw = error.raw;
|
||||
var type = "warning";
|
||||
var line = error.line - 2;
|
||||
|
||||
if (raw == "Missing semicolon.") {
|
||||
var str = error.evidence.substr(error.character);
|
||||
@@ -166,9 +177,62 @@ oop.inherits(NRJavaScriptWorker, Mirror);
|
||||
type = "info";
|
||||
}
|
||||
|
||||
if (raw === "Unmatched '{a}'." && line === -1) {
|
||||
// This is an unmatched { error. It has incorrectly matched it
|
||||
// against the { in the added line. Need to find the next valid {
|
||||
// This code scans through the original code looking for the first '{'
|
||||
// that is not in a comment or string.
|
||||
// It will incorrectly find a '{' if it is inside a regex... but
|
||||
// at least the error will be shown somwhere. There are only
|
||||
// so many hours in the day to fix every tiny edge case of an
|
||||
// edge case.
|
||||
var inSingleComment = false;
|
||||
var inMultiComment = false;
|
||||
var inString = false;
|
||||
var stringQ;
|
||||
var lineNumber = 0;
|
||||
for (var pos = 0;pos<originalValue.length;pos++) {
|
||||
var c = originalValue[pos];
|
||||
if (c === "\\") {
|
||||
pos++;
|
||||
} else if (inSingleComment) {
|
||||
if (c === "\n") {
|
||||
lineNumber++;
|
||||
inSingleComment = false;
|
||||
}
|
||||
} else if (inMultiComment) {
|
||||
if (c === "*" && originalValue[pos+1] === "/") {
|
||||
pos++;
|
||||
inMultiComment = false;
|
||||
} else if (c === "\n") {
|
||||
lineNumber++;
|
||||
}
|
||||
} else if (inString) {
|
||||
if (c === stringQ) {
|
||||
inString = false;
|
||||
}
|
||||
} else if (c === "'" || c === "\"") {
|
||||
inString = true;
|
||||
stringQ = c;
|
||||
} else if (c === "/") {
|
||||
if (originalValue[pos+1] === "/") {
|
||||
inSingleComment = true;
|
||||
} else if (originalValue[pos+1] === "*") {
|
||||
inMultiComment = true;
|
||||
}
|
||||
} else if (c === "\n") {
|
||||
lineNumber++;
|
||||
} else if (c === "{") {
|
||||
// found it!
|
||||
break;
|
||||
}
|
||||
}
|
||||
line = lineNumber;
|
||||
}
|
||||
|
||||
errors.push({
|
||||
// [Node-RED] offset the row for the added line
|
||||
row: error.line-2,
|
||||
row: Math.max(0,line),
|
||||
column: error.character-1,
|
||||
text: error.reason,
|
||||
type: type,
|
||||
|
||||
@@ -808,17 +808,7 @@ RED.nodes.fontAwesome = (function() {
|
||||
"fa-youtube": "\uf167",
|
||||
};
|
||||
|
||||
var iconList = [];
|
||||
var isUsed = {};
|
||||
Object.keys(iconMap).forEach(function(icon) {
|
||||
var unicode = iconMap[icon];
|
||||
// skip icons with a same unicode
|
||||
if (isUsed[unicode] !== true) {
|
||||
iconList.push(icon);
|
||||
isUsed[unicode] = true;
|
||||
}
|
||||
});
|
||||
isUsed = undefined;
|
||||
var iconList = Object.keys(iconMap);
|
||||
|
||||
return {
|
||||
getIconUnicode: function(name) {
|
||||
|
||||
@@ -37,22 +37,38 @@ RED.history = (function() {
|
||||
inverseEv.events.push(r);
|
||||
}
|
||||
} else if (ev.t == 'replace') {
|
||||
inverseEv = {
|
||||
t: 'replace',
|
||||
config: RED.nodes.createCompleteNodeSet(),
|
||||
changed: {},
|
||||
rev: RED.nodes.version()
|
||||
};
|
||||
RED.nodes.clear();
|
||||
var imported = RED.nodes.import(ev.config);
|
||||
imported[0].forEach(function(n) {
|
||||
if (ev.changed[n.id]) {
|
||||
n.changed = true;
|
||||
inverseEv.changed[n.id] = true;
|
||||
}
|
||||
})
|
||||
if (ev.complete) {
|
||||
// This is a replace of everything. We can short-cut
|
||||
// the logic by clearing everyting first, then importing
|
||||
// the ev.config.
|
||||
// Used by RED.diff.mergeDiff
|
||||
inverseEv = {
|
||||
t: 'replace',
|
||||
config: RED.nodes.createCompleteNodeSet(),
|
||||
changed: {},
|
||||
rev: RED.nodes.version()
|
||||
};
|
||||
RED.nodes.clear();
|
||||
var imported = RED.nodes.import(ev.config);
|
||||
imported.nodes.forEach(function(n) {
|
||||
if (ev.changed[n.id]) {
|
||||
n.changed = true;
|
||||
inverseEv.changed[n.id] = true;
|
||||
}
|
||||
})
|
||||
|
||||
RED.nodes.version(ev.rev);
|
||||
RED.nodes.version(ev.rev);
|
||||
} else {
|
||||
var importMap = {};
|
||||
ev.config.forEach(function(n) {
|
||||
importMap[n.id] = "replace";
|
||||
})
|
||||
var importedResult = RED.nodes.import(ev.config,{importMap: importMap})
|
||||
inverseEv = {
|
||||
t: 'replace',
|
||||
config: importedResult.removedNodes
|
||||
}
|
||||
}
|
||||
} else if (ev.t == 'add') {
|
||||
inverseEv = {
|
||||
t: "delete",
|
||||
@@ -85,7 +101,7 @@ RED.history = (function() {
|
||||
}
|
||||
if (ev.groups) {
|
||||
inverseEv.groups = [];
|
||||
for (i=0;i<ev.groups.length;i++) {
|
||||
for (i = ev.groups.length - 1;i>=0;i--) {
|
||||
group = ev.groups[i];
|
||||
modifiedTabs[group.z] = true;
|
||||
// The order of groups is important
|
||||
@@ -214,7 +230,7 @@ RED.history = (function() {
|
||||
inverseEv.groups = [];
|
||||
var groupsToAdd = {};
|
||||
ev.groups.forEach(function(g) { groupsToAdd[g.id] = g; });
|
||||
for (i=0;i<ev.groups.length;i++) {
|
||||
for (i = ev.groups.length - 1;i>=0;i--) {
|
||||
RED.nodes.addGroup(ev.groups[i])
|
||||
modifiedTabs[ev.groups[i].z] = true;
|
||||
// The order of groups is important
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
RED.nodes = (function() {
|
||||
|
||||
var node_defs = {};
|
||||
var nodes = [];
|
||||
var nodes = {};
|
||||
var nodeTabMap = {};
|
||||
|
||||
var configNodes = {};
|
||||
@@ -189,6 +189,7 @@ RED.nodes = (function() {
|
||||
})();
|
||||
|
||||
function getID() {
|
||||
// return Math.floor(Math.random()*15728640 + 1048576).toString(16)
|
||||
return (1+Math.random()*4294967295).toString(16);
|
||||
}
|
||||
|
||||
@@ -216,7 +217,7 @@ RED.nodes = (function() {
|
||||
});
|
||||
n.i = nextId+1;
|
||||
}
|
||||
nodes.push(n);
|
||||
nodes[n.id] = n;
|
||||
if (nodeTabMap[n.z]) {
|
||||
nodeTabMap[n.z][n.id] = n;
|
||||
} else {
|
||||
@@ -233,12 +234,8 @@ RED.nodes = (function() {
|
||||
function getNode(id) {
|
||||
if (id in configNodes) {
|
||||
return configNodes[id];
|
||||
} else {
|
||||
for (var n in nodes) {
|
||||
if (nodes[n].id == id) {
|
||||
return nodes[n];
|
||||
}
|
||||
}
|
||||
} else if (id in nodes) {
|
||||
return nodes[id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -252,58 +249,56 @@ RED.nodes = (function() {
|
||||
delete configNodes[id];
|
||||
RED.events.emit('nodes:remove',node);
|
||||
RED.workspaces.refresh();
|
||||
} else {
|
||||
node = getNode(id);
|
||||
if (node) {
|
||||
nodes.splice(nodes.indexOf(node),1);
|
||||
if (nodeTabMap[node.z]) {
|
||||
delete nodeTabMap[node.z][node.id];
|
||||
}
|
||||
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
||||
removedLinks.forEach(removeLink);
|
||||
var updatedConfigNode = false;
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d)) {
|
||||
var property = node._def.defaults[d];
|
||||
if (property.type) {
|
||||
var type = registry.getNodeType(property.type);
|
||||
if (type && type.category == "config") {
|
||||
var configNode = configNodes[node[d]];
|
||||
if (configNode) {
|
||||
updatedConfigNode = true;
|
||||
if (configNode._def.exclusive) {
|
||||
removeNode(node[d]);
|
||||
removedNodes.push(configNode);
|
||||
} else {
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(node),1);
|
||||
}
|
||||
} else if (id in nodes) {
|
||||
node = nodes[id];
|
||||
delete nodes[id]
|
||||
if (nodeTabMap[node.z]) {
|
||||
delete nodeTabMap[node.z][node.id];
|
||||
}
|
||||
removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
|
||||
removedLinks.forEach(removeLink);
|
||||
var updatedConfigNode = false;
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults.hasOwnProperty(d)) {
|
||||
var property = node._def.defaults[d];
|
||||
if (property.type) {
|
||||
var type = registry.getNodeType(property.type);
|
||||
if (type && type.category == "config") {
|
||||
var configNode = configNodes[node[d]];
|
||||
if (configNode) {
|
||||
updatedConfigNode = true;
|
||||
if (configNode._def.exclusive) {
|
||||
removeNode(node[d]);
|
||||
removedNodes.push(configNode);
|
||||
} else {
|
||||
var users = configNode.users;
|
||||
users.splice(users.indexOf(node),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type.indexOf("subflow:") === 0) {
|
||||
var subflowId = node.type.substring(8);
|
||||
var sf = RED.nodes.subflow(subflowId);
|
||||
if (sf) {
|
||||
sf.instances.splice(sf.instances.indexOf(node),1);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedConfigNode) {
|
||||
RED.workspaces.refresh();
|
||||
}
|
||||
try {
|
||||
if (node._def.oneditdelete) {
|
||||
node._def.oneditdelete.call(node);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log("oneditdelete",node.id,node.type,err.toString());
|
||||
}
|
||||
RED.events.emit('nodes:remove',node);
|
||||
}
|
||||
|
||||
if (node.type.indexOf("subflow:") === 0) {
|
||||
var subflowId = node.type.substring(8);
|
||||
var sf = RED.nodes.subflow(subflowId);
|
||||
if (sf) {
|
||||
sf.instances.splice(sf.instances.indexOf(node),1);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedConfigNode) {
|
||||
RED.workspaces.refresh();
|
||||
}
|
||||
try {
|
||||
if (node._def.oneditdelete) {
|
||||
node._def.oneditdelete.call(node);
|
||||
}
|
||||
} catch(err) {
|
||||
console.log("oneditdelete",node.id,node.type,err.toString());
|
||||
}
|
||||
RED.events.emit('nodes:remove',node);
|
||||
}
|
||||
|
||||
|
||||
@@ -377,10 +372,13 @@ RED.nodes = (function() {
|
||||
workspacesOrder.splice(workspacesOrder.indexOf(id),1);
|
||||
var i;
|
||||
var node;
|
||||
for (i=0;i<nodes.length;i++) {
|
||||
node = nodes[i];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
// TODO: this should use nodeTabMap
|
||||
for (i in nodes) {
|
||||
if (nodes.hasOwnProperty(i)) {
|
||||
node = nodes[i];
|
||||
if (node.z == id) {
|
||||
removedNodes.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i in configNodes) {
|
||||
@@ -487,17 +485,19 @@ RED.nodes = (function() {
|
||||
}
|
||||
|
||||
function subflowContains(sfid,nodeid) {
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
if (node.z === sfid) {
|
||||
var m = /^subflow:(.+)$/.exec(node.type);
|
||||
if (m) {
|
||||
if (m[1] === nodeid) {
|
||||
return true;
|
||||
} else {
|
||||
var result = subflowContains(m[1],nodeid);
|
||||
if (result) {
|
||||
for (var i in nodes) {
|
||||
if (nodes.hasOwnProperty(i)) {
|
||||
var node = nodes[i];
|
||||
if (node.z === sfid) {
|
||||
var m = /^subflow:(.+)$/.exec(node.type);
|
||||
if (m) {
|
||||
if (m[1] === nodeid) {
|
||||
return true;
|
||||
} else {
|
||||
var result = subflowContains(m[1],nodeid);
|
||||
if (result) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -553,6 +553,9 @@ RED.nodes = (function() {
|
||||
node.id = n.id;
|
||||
node.type = n.type;
|
||||
node.z = n.z;
|
||||
if (node.z === 0 || node.z === "") {
|
||||
delete node.z;
|
||||
}
|
||||
if (n.d === true) {
|
||||
node.d = true;
|
||||
}
|
||||
@@ -611,7 +614,9 @@ RED.nodes = (function() {
|
||||
node.y = n.y;
|
||||
node.w = n.w;
|
||||
node.h = n.h;
|
||||
node.nodes = node.nodes.map(function(n) { return n.id });
|
||||
// In 1.1.0, we have seen an instance of this array containing `undefined`
|
||||
// Until we know how that can happen, add a filter here to remove them
|
||||
node.nodes = node.nodes.filter(function(n) { return !!n }).map(function(n) { return n.id });
|
||||
}
|
||||
if (n._def.category != "config") {
|
||||
node.x = n.x;
|
||||
@@ -735,6 +740,16 @@ RED.nodes = (function() {
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
function createExportableSubflow(id) {
|
||||
var sf = getSubflow(id);
|
||||
var nodeSet = [sf];
|
||||
var sfNodeIds = Object.keys(nodeTabMap[sf.id]||{});
|
||||
for (var i=0, l=sfNodeIds.length; i<l; i++) {
|
||||
nodeSet.push(nodeTabMap[sf.id][sfNodeIds[i]]);
|
||||
}
|
||||
return createExportableNodeSet(nodeSet);
|
||||
}
|
||||
/**
|
||||
* Converts the current node selection to an exportable JSON Object
|
||||
**/
|
||||
@@ -824,9 +839,10 @@ RED.nodes = (function() {
|
||||
nns.push(convertNode(configNodes[i], exportCredentials));
|
||||
}
|
||||
}
|
||||
for (i=0;i<nodes.length;i++) {
|
||||
var node = nodes[i];
|
||||
nns.push(convertNode(node, exportCredentials));
|
||||
for (i in nodes) {
|
||||
if (nodes.hasOwnProperty(i)) {
|
||||
nns.push(convertNode(nodes[i], exportCredentials));
|
||||
}
|
||||
}
|
||||
return nns;
|
||||
}
|
||||
@@ -896,11 +912,164 @@ RED.nodes = (function() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function importNodes(newNodesObj,createNewIds,createMissingWorkspace) {
|
||||
function identifyImportConflicts(importedNodes) {
|
||||
var imported = {
|
||||
tabs: {},
|
||||
subflows: {},
|
||||
groups: {},
|
||||
configs: {},
|
||||
nodes: {},
|
||||
all: [],
|
||||
conflicted: {},
|
||||
zMap: {},
|
||||
}
|
||||
|
||||
importedNodes.forEach(function(n) {
|
||||
imported.all.push(n);
|
||||
if (n.type === "tab") {
|
||||
imported.tabs[n.id] = n;
|
||||
} else if (n.type === "subflow") {
|
||||
imported.subflows[n.id] = n;
|
||||
} else if (n.type === "group") {
|
||||
imported.groups[n.id] = n;
|
||||
} else if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) {
|
||||
imported.nodes[n.id] = n;
|
||||
} else {
|
||||
imported.configs[n.id] = n;
|
||||
}
|
||||
var nodeZ = n.z || "__global__";
|
||||
imported.zMap[nodeZ] = imported.zMap[nodeZ] || [];
|
||||
imported.zMap[nodeZ].push(n)
|
||||
if (nodes[n.id] || configNodes[n.id] || workspaces[n.id] || subflows[n.id] || groups[n.id]) {
|
||||
imported.conflicted[n.id] = n;
|
||||
}
|
||||
})
|
||||
return imported;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the provided nodes.
|
||||
* This must contain complete Subflow defs or complete Flow Tabs.
|
||||
* It does not replace an individual node in the middle of a flow.
|
||||
*/
|
||||
function replaceNodes(newNodes) {
|
||||
var zMap = {};
|
||||
var newSubflows = {};
|
||||
var newConfigNodes = {};
|
||||
var removedNodes = [];
|
||||
// Figure out what we're being asked to replace - subflows/configNodes
|
||||
// TODO: config nodes
|
||||
newNodes.forEach(function(n) {
|
||||
if (n.type === "subflow") {
|
||||
newSubflows[n.id] = n;
|
||||
} else if (!n.hasOwnProperty('x') && !n.hasOwnProperty('y')) {
|
||||
newConfigNodes[n.id] = n;
|
||||
}
|
||||
if (n.z) {
|
||||
zMap[n.z] = zMap[n.z] || [];
|
||||
zMap[n.z].push(n);
|
||||
}
|
||||
})
|
||||
|
||||
// Filter out config nodes inside a subflow def that is being replaced
|
||||
var configNodeIds = Object.keys(newConfigNodes);
|
||||
configNodeIds.forEach(function(id) {
|
||||
var n = newConfigNodes[id];
|
||||
if (newSubflows[n.z]) {
|
||||
// This config node is in a subflow to be replaced.
|
||||
// - remove from the list as it'll get handled with the subflow
|
||||
delete newConfigNodes[id];
|
||||
}
|
||||
});
|
||||
// Rebuild the list of ids
|
||||
configNodeIds = Object.keys(newConfigNodes);
|
||||
|
||||
// ------------------------------
|
||||
// Replace subflow definitions
|
||||
//
|
||||
// For each of the subflows to be replaced:
|
||||
var newSubflowIds = Object.keys(newSubflows);
|
||||
newSubflowIds.forEach(function(id) {
|
||||
var n = newSubflows[id];
|
||||
// Get a snapshot of the existing subflow definition
|
||||
removedNodes = removedNodes.concat(createExportableSubflow(id));
|
||||
// Remove the old subflow definition - but leave the instances in place
|
||||
var removalResult = RED.subflow.removeSubflow(n.id, true);
|
||||
// Create the list of nodes for the new subflow def
|
||||
var subflowNodes = [n].concat(zMap[n.id]);
|
||||
// Import the new subflow - no clashes should occur as we've removed
|
||||
// the old version
|
||||
var result = importNodes(subflowNodes);
|
||||
newSubflows[id] = getSubflow(id);
|
||||
})
|
||||
|
||||
// Having replaced the subflow definitions, now need to update the
|
||||
// instance nodes.
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (/^subflow:/.test(n.type)) {
|
||||
var sfId = n.type.substring(8);
|
||||
if (newSubflows[sfId]) {
|
||||
// This is an instance of one of the replaced subflows
|
||||
// - update the new def's instances array to include this one
|
||||
newSubflows[sfId].instances.push(n);
|
||||
// - update the instance's _def to point to the new def
|
||||
n._def = RED.nodes.getType(n.type);
|
||||
// - set all the flags so the view refreshes properly
|
||||
n.dirty = true;
|
||||
n.changed = true;
|
||||
n._colorChanged = true;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
newSubflowIds.forEach(function(id) {
|
||||
var n = newSubflows[id];
|
||||
RED.events.emit("subflows:change",n);
|
||||
})
|
||||
// Just in case the imported subflow changed color.
|
||||
RED.utils.clearNodeColorCache();
|
||||
|
||||
// ------------------------------
|
||||
// Replace config nodes
|
||||
//
|
||||
configNodeIds.forEach(function(id) {
|
||||
removedNodes = removedNodes.concat(convertNode(getNode(id)));
|
||||
removeNode(id);
|
||||
importNodes([newConfigNodes[id]])
|
||||
});
|
||||
|
||||
return {
|
||||
removedNodes: removedNodes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Options:
|
||||
* - generateIds - whether to replace all node ids
|
||||
* - addFlow - whether to import nodes to a new tab
|
||||
* - importToCurrent
|
||||
* - importMap - how to resolve any conflicts.
|
||||
* - id:import - import as-is
|
||||
* - id:copy - import with new id
|
||||
* - id:replace - import over the top of existing
|
||||
*/
|
||||
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
|
||||
options = options || {
|
||||
generateIds: false,
|
||||
addFlow: false,
|
||||
}
|
||||
options.importMap = options.importMap || {};
|
||||
|
||||
var createNewIds = options.generateIds;
|
||||
var createMissingWorkspace = options.addFlow;
|
||||
var i;
|
||||
var n;
|
||||
var newNodes;
|
||||
var nodeZmap = {};
|
||||
var recoveryWorkspace;
|
||||
if (typeof newNodesObj === "string") {
|
||||
if (newNodesObj === "") {
|
||||
return;
|
||||
@@ -925,14 +1094,59 @@ RED.nodes = (function() {
|
||||
// copies of the flow would get loaded at the same time.
|
||||
// If the user hit deploy they would have saved those duplicates.
|
||||
var seenIds = {};
|
||||
var existingNodes = [];
|
||||
var nodesToReplace = [];
|
||||
|
||||
newNodes = newNodes.filter(function(n) {
|
||||
var id = n.id;
|
||||
if (seenIds[n.id]) {
|
||||
return false;
|
||||
}
|
||||
seenIds[n.id] = true;
|
||||
|
||||
if (!options.generateIds) {
|
||||
if (!options.importMap[id]) {
|
||||
// No conflict resolution for this node
|
||||
var existing = nodes[id] || configNodes[id] || workspaces[id] || subflows[id] || groups[id];
|
||||
if (existing) {
|
||||
existingNodes.push({existing:existing, imported:n});
|
||||
}
|
||||
} else if (options.importMap[id] === "replace") {
|
||||
nodesToReplace.push(n);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
if (existingNodes.length > 0) {
|
||||
var errorMessage = RED._("clipboard.importDuplicate",{count:existingNodes.length});
|
||||
var nodeList = $("<ul>");
|
||||
var existingNodesCount = Math.min(5,existingNodes.length);
|
||||
for (var i=0;i<existingNodesCount;i++) {
|
||||
var conflict = existingNodes[i];
|
||||
$("<li>").text(
|
||||
conflict.existing.id+
|
||||
" [ "+conflict.existing.type+ ((conflict.imported.type !== conflict.existing.type)?" | "+conflict.imported.type:"")+" ]").appendTo(nodeList)
|
||||
}
|
||||
if (existingNodesCount !== existingNodes.length) {
|
||||
$("<li>").text(RED._("deploy.confirm.plusNMore",{count:existingNodes.length-existingNodesCount})).appendTo(nodeList)
|
||||
}
|
||||
var wrapper = $("<p>").append(nodeList);
|
||||
|
||||
var existingNodesError = new Error(errorMessage+wrapper.html());
|
||||
existingNodesError.code = "import_conflict";
|
||||
existingNodesError.importConfig = identifyImportConflicts(newNodes);
|
||||
throw existingNodesError;
|
||||
}
|
||||
var removedNodes;
|
||||
if (nodesToReplace.length > 0) {
|
||||
var replaceResult = replaceNodes(nodesToReplace);
|
||||
removedNodes = replaceResult.removedNodes;
|
||||
}
|
||||
|
||||
|
||||
var isInitialLoad = false;
|
||||
if (!initialLoad) {
|
||||
isInitialLoad = true;
|
||||
@@ -941,6 +1155,7 @@ RED.nodes = (function() {
|
||||
var unknownTypes = [];
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
var id = n.id;
|
||||
// TODO: remove workspace in next release+1
|
||||
if (n.type != "workspace" &&
|
||||
n.type != "tab" &&
|
||||
@@ -954,11 +1169,32 @@ RED.nodes = (function() {
|
||||
if (n.z) {
|
||||
nodeZmap[n.z] = nodeZmap[n.z] || [];
|
||||
nodeZmap[n.z].push(n);
|
||||
} else if (isInitialLoad && n.hasOwnProperty('x') && n.hasOwnProperty('y') && !n.z) {
|
||||
// Hit the rare issue where node z values get set to 0.
|
||||
// Repair the flow - but we really need to track that down.
|
||||
if (!recoveryWorkspace) {
|
||||
recoveryWorkspace = {
|
||||
id: RED.nodes.id(),
|
||||
type: "tab",
|
||||
disabled: false,
|
||||
label: RED._("clipboard.recoveredNodes"),
|
||||
info: RED._("clipboard.recoveredNodesInfo")
|
||||
}
|
||||
addWorkspace(recoveryWorkspace);
|
||||
RED.workspaces.add(recoveryWorkspace);
|
||||
nodeZmap[recoveryWorkspace.id] = [];
|
||||
}
|
||||
n.z = recoveryWorkspace.id;
|
||||
nodeZmap[recoveryWorkspace.id].push(n);
|
||||
}
|
||||
|
||||
}
|
||||
if (!isInitialLoad && unknownTypes.length > 0) {
|
||||
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
|
||||
var typeList = $("<ul>");
|
||||
unknownTypes.forEach(function(t) {
|
||||
$("<li>").text(t).appendTo(typeList);
|
||||
})
|
||||
typeList = typeList[0].outerHTML;
|
||||
RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
|
||||
}
|
||||
|
||||
@@ -991,17 +1227,22 @@ RED.nodes = (function() {
|
||||
var workspace_map = {};
|
||||
var new_subflows = [];
|
||||
var subflow_map = {};
|
||||
var subflow_blacklist = {};
|
||||
var subflow_denylist = {};
|
||||
var node_map = {};
|
||||
var new_nodes = [];
|
||||
var new_links = [];
|
||||
var new_groups = [];
|
||||
var new_group_set = new Set();
|
||||
var nid;
|
||||
var def;
|
||||
var configNode;
|
||||
var missingWorkspace = null;
|
||||
var d;
|
||||
|
||||
if (recoveryWorkspace) {
|
||||
new_workspaces.push(recoveryWorkspace);
|
||||
}
|
||||
|
||||
// Find all tabs and subflow templates
|
||||
for (i=0;i<newNodes.length;i++) {
|
||||
n = newNodes[i];
|
||||
@@ -1013,7 +1254,10 @@ RED.nodes = (function() {
|
||||
if (defaultWorkspace == null) {
|
||||
defaultWorkspace = n;
|
||||
}
|
||||
if (createNewIds) {
|
||||
if (activeWorkspace === 0) {
|
||||
activeWorkspace = n.id;
|
||||
}
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
nid = getID();
|
||||
workspace_map[n.id] = nid;
|
||||
n.id = nid;
|
||||
@@ -1022,12 +1266,15 @@ RED.nodes = (function() {
|
||||
RED.workspaces.add(n);
|
||||
new_workspaces.push(n);
|
||||
} else if (n.type === "subflow") {
|
||||
var matchingSubflow = checkForMatchingSubflow(n,nodeZmap[n.id]);
|
||||
var matchingSubflow;
|
||||
if (!options.importMap[n.id]) {
|
||||
matchingSubflow = checkForMatchingSubflow(n,nodeZmap[n.id]);
|
||||
}
|
||||
if (matchingSubflow) {
|
||||
subflow_blacklist[n.id] = matchingSubflow;
|
||||
subflow_denylist[n.id] = matchingSubflow;
|
||||
} else {
|
||||
subflow_map[n.id] = n;
|
||||
if (createNewIds) {
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
nid = getID();
|
||||
n.id = nid;
|
||||
}
|
||||
@@ -1053,7 +1300,7 @@ RED.nodes = (function() {
|
||||
n.status.id = getID();
|
||||
}
|
||||
new_subflows.push(n);
|
||||
addSubflow(n,createNewIds);
|
||||
addSubflow(n,createNewIds || options.importMap[n.id] === "copy");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1073,9 +1320,9 @@ RED.nodes = (function() {
|
||||
def = registry.getNodeType(n.type);
|
||||
if (def && def.category == "config") {
|
||||
var existingConfigNode = null;
|
||||
if (createNewIds) {
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
if (n.z) {
|
||||
if (subflow_blacklist[n.z]) {
|
||||
if (subflow_denylist[n.z]) {
|
||||
continue;
|
||||
} else if (subflow_map[n.z]) {
|
||||
n.z = subflow_map[n.z].id;
|
||||
@@ -1094,23 +1341,28 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
existingConfigNode = RED.nodes.node(n.id);
|
||||
if (existingConfigNode) {
|
||||
if (n.z && existingConfigNode.z !== n.z) {
|
||||
existingConfigNode = null;
|
||||
// Check the config nodes on n.z
|
||||
for (var cn in configNodes) {
|
||||
if (configNodes.hasOwnProperty(cn)) {
|
||||
if (configNodes[cn].z === n.z && compareNodes(configNodes[cn],n,false)) {
|
||||
existingConfigNode = configNodes[cn];
|
||||
node_map[n.id] = configNodes[cn];
|
||||
break;
|
||||
if (options.importMap[n.id] !== "copy") {
|
||||
existingConfigNode = RED.nodes.node(n.id);
|
||||
if (existingConfigNode) {
|
||||
if (n.z && existingConfigNode.z !== n.z) {
|
||||
existingConfigNode = null;
|
||||
// Check the config nodes on n.z
|
||||
for (var cn in configNodes) {
|
||||
if (configNodes.hasOwnProperty(cn)) {
|
||||
if (configNodes[cn].z === n.z && compareNodes(configNodes[cn],n,false)) {
|
||||
existingConfigNode = configNodes[cn];
|
||||
node_map[n.id] = configNodes[cn];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (n.z && !workspaces[n.z] && !subflow_map[n.z]) {
|
||||
n.z = activeWorkspace;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
|
||||
@@ -1122,6 +1374,9 @@ RED.nodes = (function() {
|
||||
users:[],
|
||||
_config:{}
|
||||
};
|
||||
if (!n.z) {
|
||||
delete configNode.z;
|
||||
}
|
||||
if (n.hasOwnProperty('d')) {
|
||||
configNode.d = n.d;
|
||||
}
|
||||
@@ -1141,7 +1396,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
configNode.label = def.label;
|
||||
configNode._def = def;
|
||||
if (createNewIds) {
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
configNode.id = getID();
|
||||
}
|
||||
node_map[n.id] = configNode;
|
||||
@@ -1181,8 +1436,8 @@ RED.nodes = (function() {
|
||||
if (n.hasOwnProperty('g')) {
|
||||
node.g = n.g;
|
||||
}
|
||||
if (createNewIds) {
|
||||
if (subflow_blacklist[n.z]) {
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
if (subflow_denylist[n.z]) {
|
||||
continue;
|
||||
} else if (subflow_map[node.z]) {
|
||||
node.z = subflow_map[node.z].id;
|
||||
@@ -1229,8 +1484,8 @@ RED.nodes = (function() {
|
||||
node._config.y = node.y;
|
||||
} else if (n.type.substring(0,7) === "subflow") {
|
||||
var parentId = n.type.split(":")[1];
|
||||
var subflow = subflow_blacklist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
||||
if (createNewIds) {
|
||||
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
parentId = subflow.id;
|
||||
node.type = "subflow:"+parentId;
|
||||
node._def = registry.getNodeType(node.type);
|
||||
@@ -1264,6 +1519,9 @@ RED.nodes = (function() {
|
||||
delete node.wires;
|
||||
delete node.inputLabels;
|
||||
delete node.outputLabels;
|
||||
if (!n.z) {
|
||||
delete node.z;
|
||||
}
|
||||
}
|
||||
var orig = {};
|
||||
for (var p in n) {
|
||||
@@ -1324,6 +1582,7 @@ RED.nodes = (function() {
|
||||
new_nodes.push(node);
|
||||
} else if (node.type === "group") {
|
||||
new_groups.push(node);
|
||||
new_group_set.add(node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1427,18 +1686,53 @@ RED.nodes = (function() {
|
||||
delete n.status.wires;
|
||||
}
|
||||
}
|
||||
// Order the groups to ensure they are outer-most to inner-most
|
||||
var groupDepthMap = {};
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
if (n.g && node_map[n.g]) {
|
||||
n.g = node_map[n.g].id;
|
||||
} else {
|
||||
|
||||
if (n.g && !new_group_set.has(n.g)) {
|
||||
delete n.g;
|
||||
}
|
||||
n.nodes = n.nodes.map(function(id) {
|
||||
return node_map[id];
|
||||
})
|
||||
// Just in case the group references a node that doesn't exist for some reason
|
||||
n.nodes = n.nodes.filter(function(v) {
|
||||
if (v) {
|
||||
// Repair any nodes that have forgotten they are in this group
|
||||
if (v.g !== n.id) {
|
||||
v.g = n.id;
|
||||
}
|
||||
}
|
||||
return !!v
|
||||
});
|
||||
if (!n.g) {
|
||||
groupDepthMap[n.id] = 0;
|
||||
}
|
||||
}
|
||||
var changedDepth;
|
||||
do {
|
||||
changedDepth = false;
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
if (n.g) {
|
||||
if (groupDepthMap[n.id] !== groupDepthMap[n.g] + 1) {
|
||||
groupDepthMap[n.id] = groupDepthMap[n.g] + 1;
|
||||
changedDepth = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while(changedDepth);
|
||||
|
||||
new_groups.sort(function(A,B) {
|
||||
return groupDepthMap[A.id] - groupDepthMap[B.id];
|
||||
});
|
||||
for (i=0;i<new_groups.length;i++) {
|
||||
n = new_groups[i];
|
||||
addGroup(n);
|
||||
}
|
||||
|
||||
// Now the nodes have been fully updated, add them.
|
||||
for (i=0;i<new_nodes.length;i++) {
|
||||
var node = new_nodes[i];
|
||||
@@ -1453,24 +1747,47 @@ RED.nodes = (function() {
|
||||
}
|
||||
|
||||
RED.workspaces.refresh();
|
||||
return [new_nodes,new_links,new_groups,new_workspaces,new_subflows,missingWorkspace];
|
||||
|
||||
|
||||
if (recoveryWorkspace) {
|
||||
var notification = RED.notify(RED._("clipboard.recoveredNodesNotification",{flowName:RED._("clipboard.recoveredNodes")}),{
|
||||
type:"warning",
|
||||
fixed:true,
|
||||
buttons: [
|
||||
{text: RED._("common.label.close"), click: function() { notification.close() }}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
nodes:new_nodes,
|
||||
links:new_links,
|
||||
groups:new_groups,
|
||||
workspaces:new_workspaces,
|
||||
subflows:new_subflows,
|
||||
missingWorkspace: missingWorkspace,
|
||||
removedNodes: removedNodes
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: supports filter.z|type
|
||||
function filterNodes(filter) {
|
||||
var result = [];
|
||||
var searchSet = nodes;
|
||||
var searchSet = null;
|
||||
var doZFilter = false;
|
||||
if (filter.hasOwnProperty("z")) {
|
||||
if (Object.hasOwnProperty("values") && nodeTabMap.hasOwnProperty(filter.z) ) {
|
||||
searchSet = Object.values(nodeTabMap[filter.z]);
|
||||
if (nodeTabMap.hasOwnProperty(filter.z)) {
|
||||
searchSet = Object.keys(nodeTabMap[filter.z]);
|
||||
} else {
|
||||
doZFilter = true;
|
||||
}
|
||||
}
|
||||
if (searchSet === null) {
|
||||
searchSet = Object.keys(nodes);
|
||||
}
|
||||
|
||||
for (var n=0;n<searchSet.length;n++) {
|
||||
var node = searchSet[n];
|
||||
var node = nodes[searchSet[n]];
|
||||
if (filter.hasOwnProperty("type") && node.type !== filter.type) {
|
||||
continue;
|
||||
}
|
||||
@@ -1539,7 +1856,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
|
||||
function clear() {
|
||||
nodes = [];
|
||||
nodes = {};
|
||||
links = [];
|
||||
nodeTabMap = {};
|
||||
configNodes = {};
|
||||
@@ -1557,6 +1874,8 @@ RED.nodes = (function() {
|
||||
});
|
||||
defaultWorkspace = null;
|
||||
initialLoad = null;
|
||||
workspaces = {};
|
||||
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true, true);
|
||||
RED.palette.refresh();
|
||||
@@ -1567,7 +1886,7 @@ RED.nodes = (function() {
|
||||
RED.events.emit("workspace:clear");
|
||||
|
||||
// var node_defs = {};
|
||||
// var nodes = [];
|
||||
// var nodes = {};
|
||||
// var configNodes = {};
|
||||
// var links = [];
|
||||
// var defaultWorkspace;
|
||||
@@ -1627,12 +1946,13 @@ RED.nodes = (function() {
|
||||
if (configNodes.hasOwnProperty(n.id)) {
|
||||
delete configNodes[n.id];
|
||||
} else {
|
||||
nodes.splice(nodes.indexOf(n),1);
|
||||
delete nodes[n.id];
|
||||
if (nodeTabMap[n.z]) {
|
||||
delete nodeTabMap[n.z][n.id];
|
||||
}
|
||||
}
|
||||
reimportList.push(convertNode(n));
|
||||
RED.events.emit('nodes:remove',n);
|
||||
});
|
||||
|
||||
// Remove any links between nodes that are going to be reimported.
|
||||
@@ -1648,9 +1968,9 @@ RED.nodes = (function() {
|
||||
// Force the redraw to be synchronous so the view updates
|
||||
// *now* and removes the unknown node
|
||||
RED.view.redraw(true, true);
|
||||
var result = importNodes(reimportList,false);
|
||||
var result = importNodes(reimportList,{generateIds:false});
|
||||
var newNodeMap = {};
|
||||
result[0].forEach(function(n) {
|
||||
result.nodes.forEach(function(n) {
|
||||
newNodeMap[n.id] = n;
|
||||
});
|
||||
RED.nodes.eachLink(function(l) {
|
||||
@@ -1707,9 +2027,11 @@ RED.nodes = (function() {
|
||||
groups: function(z) { return groupsByZ[z]||[] },
|
||||
|
||||
eachNode: function(cb) {
|
||||
for (var n=0;n<nodes.length;n++) {
|
||||
if (cb(nodes[n]) === false) {
|
||||
break;
|
||||
for (var id in nodes) {
|
||||
if (nodes.hasOwnProperty(id)) {
|
||||
if (cb(nodes[id]) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1762,6 +2084,8 @@ RED.nodes = (function() {
|
||||
|
||||
import: importNodes,
|
||||
|
||||
identifyImportConflicts: identifyImportConflicts,
|
||||
|
||||
getAllFlowNodes: getAllFlowNodes,
|
||||
createExportableNodeSet: createExportableNodeSet,
|
||||
createCompleteNodeSet: createCompleteNodeSet,
|
||||
|
||||
@@ -37,5 +37,21 @@
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (new Set([0]).size === 0) {
|
||||
// IE does not support passing an iterable to Set constructor
|
||||
var _Set = Set;
|
||||
/*global Set:true */
|
||||
Set = function Set(iterable) {
|
||||
var set = new _Set();
|
||||
if (iterable) {
|
||||
iterable.forEach(set.add, set);
|
||||
}
|
||||
return set;
|
||||
};
|
||||
Set.prototype = _Set.prototype;
|
||||
Set.prototype.constructor = Set;
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -178,11 +178,21 @@ var RED = (function() {
|
||||
var currentHash = window.location.hash;
|
||||
RED.nodes.version(nodes.rev);
|
||||
loader.reportProgress(RED._("event.importFlows"),90 )
|
||||
RED.nodes.import(nodes.flows);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
if (/^#flow\/.+$/.test(currentHash)) {
|
||||
RED.workspaces.show(currentHash.substring(6));
|
||||
try {
|
||||
RED.nodes.import(nodes.flows);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
if (/^#flow\/.+$/.test(currentHash)) {
|
||||
RED.workspaces.show(currentHash.substring(6));
|
||||
}
|
||||
} catch(err) {
|
||||
RED.notify(
|
||||
RED._("event.importError", {message: err.message}),
|
||||
{
|
||||
fixed: true,
|
||||
type: 'error'
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
done();
|
||||
@@ -372,7 +382,7 @@ var RED = (function() {
|
||||
node.status = msg;
|
||||
node.dirtyStatus = true;
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
RED.view.redrawStatus(node);
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
||||
|
||||
@@ -21,7 +21,18 @@ RED.actions = (function() {
|
||||
var result = [];
|
||||
Object.keys(actions).forEach(function(action) {
|
||||
var shortcut = RED.keyboard.getShortcut(action);
|
||||
result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined,user:shortcut?shortcut.user:undefined})
|
||||
var isUser = false;
|
||||
if (shortcut) {
|
||||
isUser = shortcut.user;
|
||||
} else {
|
||||
isUser = !!RED.keyboard.getUserShortcut(action);
|
||||
}
|
||||
result.push({
|
||||
id:action,
|
||||
scope:shortcut?shortcut.scope:undefined,
|
||||
key:shortcut?shortcut.key:undefined,
|
||||
user:isUser
|
||||
})
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ RED.clipboard = (function() {
|
||||
var libraryBrowser;
|
||||
var examplesBrowser;
|
||||
|
||||
var pendingImportConfig;
|
||||
|
||||
function setupDialogs() {
|
||||
dialog = $('<div id="red-ui-clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("#red-ui-editor")
|
||||
@@ -42,14 +44,14 @@ RED.clipboard = (function() {
|
||||
"ui-widget-overlay": "red-ui-editor-dialog"
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
{ // red-ui-clipboard-dialog-cancel
|
||||
id: "red-ui-clipboard-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
{ // red-ui-clipboard-dialog-download
|
||||
id: "red-ui-clipboard-dialog-download",
|
||||
class: "primary",
|
||||
text: RED._("clipboard.download"),
|
||||
@@ -64,7 +66,7 @@ RED.clipboard = (function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
{ // red-ui-clipboard-dialog-export
|
||||
id: "red-ui-clipboard-dialog-export",
|
||||
class: "primary",
|
||||
text: RED._("clipboard.export.copy"),
|
||||
@@ -134,14 +136,14 @@ RED.clipboard = (function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
{ // red-ui-clipboard-dialog-ok
|
||||
id: "red-ui-clipboard-dialog-ok",
|
||||
class: "primary",
|
||||
text: RED._("common.label.import"),
|
||||
click: function() {
|
||||
var addNewFlow = ($("#red-ui-clipboard-dialog-import-opt > a.selected").attr('id') === 'red-ui-clipboard-dialog-import-opt-new');
|
||||
if (activeTab === "red-ui-clipboard-dialog-import-tab-clipboard") {
|
||||
RED.view.importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
|
||||
importNodes($("#red-ui-clipboard-dialog-import-text").val(),addNewFlow);
|
||||
} else {
|
||||
var selectedPath;
|
||||
if (activeTab === "red-ui-clipboard-dialog-import-tab-library") {
|
||||
@@ -151,15 +153,51 @@ RED.clipboard = (function() {
|
||||
}
|
||||
if (selectedPath.path) {
|
||||
$.get('library/'+selectedPath.library+'/'+selectedPath.type+'/'+selectedPath.path, function(data) {
|
||||
RED.view.importNodes(data,addNewFlow);
|
||||
importNodes(data,addNewFlow);
|
||||
});
|
||||
}
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{ // red-ui-clipboard-dialog-import-conflict
|
||||
id: "red-ui-clipboard-dialog-import-conflict",
|
||||
class: "primary",
|
||||
text: RED._("clipboard.import.importSelected"),
|
||||
click: function() {
|
||||
var importMap = {};
|
||||
$('#red-ui-clipboard-dialog-import-conflicts-list input[type="checkbox"]').each(function() {
|
||||
importMap[$(this).attr("data-node-id")] = this.checked?"import":"skip";
|
||||
})
|
||||
|
||||
$('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').each(function() {
|
||||
if (!$(this).attr("disabled")) {
|
||||
importMap[$(this).attr("data-node-id")] = this.checked?"replace":"copy"
|
||||
}
|
||||
})
|
||||
// skip - don't import
|
||||
// import - import as-is
|
||||
// copy - import with new id
|
||||
// replace - import over the top of existing
|
||||
pendingImportConfig.importOptions.importMap = importMap;
|
||||
|
||||
var newNodes = pendingImportConfig.importNodes.filter(function(n) {
|
||||
if (!importMap[n.id] || importMap[n.z]) {
|
||||
importMap[n.id] = importMap[n.z];
|
||||
}
|
||||
return importMap[n.id] !== "skip"
|
||||
})
|
||||
// console.table(pendingImportConfig.importNodes.map(function(n) { return {id:n.id,type:n.type,result:importMap[n.id]}}))
|
||||
RED.view.importNodes(newNodes, pendingImportConfig.importOptions);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function( event, ui ) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
if (popover) {
|
||||
popover.close(true);
|
||||
currentPopoverError = null;
|
||||
@@ -232,6 +270,14 @@ RED.clipboard = (function() {
|
||||
'</span>'+
|
||||
'</div>';
|
||||
|
||||
importConflictsDialog =
|
||||
'<div class="form-row">'+
|
||||
'<div class="form-row"><p data-i18n="clipboard.import.conflictNotification1"></p><p data-i18n="clipboard.import.conflictNotification2"></p></div>'+
|
||||
'<div class="red-ui-clipboard-dialog-import-conflicts-list-container">'+
|
||||
'<div id="red-ui-clipboard-dialog-import-conflicts-list"></div>'+
|
||||
'</div>'+
|
||||
'</div>';
|
||||
|
||||
}
|
||||
|
||||
var validateExportFilenameTimeout
|
||||
@@ -355,7 +401,7 @@ RED.clipboard = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function importNodes(mode) {
|
||||
function showImportNodes(mode) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -441,6 +487,8 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-cancel").show();
|
||||
$("#red-ui-clipboard-dialog-export").hide();
|
||||
$("#red-ui-clipboard-dialog-download").hide();
|
||||
$("#red-ui-clipboard-dialog-import-conflict").hide();
|
||||
|
||||
$("#red-ui-clipboard-dialog-ok").button("disable");
|
||||
$("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
|
||||
$("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
|
||||
@@ -481,7 +529,9 @@ RED.clipboard = (function() {
|
||||
}
|
||||
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes"))
|
||||
.dialog("option","width",700)
|
||||
.dialog("open");
|
||||
popover = RED.popover.create({
|
||||
target: $("#red-ui-clipboard-dialog-import-text"),
|
||||
trigger: "manual",
|
||||
@@ -490,7 +540,7 @@ RED.clipboard = (function() {
|
||||
});
|
||||
}
|
||||
|
||||
function exportNodes(mode) {
|
||||
function showExportNodes(mode) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
@@ -627,6 +677,8 @@ RED.clipboard = (function() {
|
||||
$("#red-ui-clipboard-dialog-ok").hide();
|
||||
$("#red-ui-clipboard-dialog-cancel").hide();
|
||||
$("#red-ui-clipboard-dialog-export").hide();
|
||||
$("#red-ui-clipboard-dialog-import-conflict").hide();
|
||||
|
||||
var selection = RED.workspaces.selection();
|
||||
if (selection.length > 0) {
|
||||
$("#red-ui-clipboard-dialog-export-rng-selected").trigger("click");
|
||||
@@ -653,12 +705,15 @@ RED.clipboard = (function() {
|
||||
}
|
||||
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
|
||||
dialog.dialog("option","title",RED._("clipboard.exportNodes"))
|
||||
.dialog("option","width",700)
|
||||
.dialog("open");
|
||||
|
||||
$("#red-ui-clipboard-dialog-export-text").trigger("focus");
|
||||
$("#red-ui-clipboard-dialog-cancel").show();
|
||||
$("#red-ui-clipboard-dialog-export").show();
|
||||
$("#red-ui-clipboard-dialog-download").show();
|
||||
$("#red-ui-clipboard-dialog-import-conflict").hide();
|
||||
|
||||
}
|
||||
|
||||
@@ -718,6 +773,12 @@ RED.clipboard = (function() {
|
||||
// representation or null
|
||||
return null;
|
||||
}
|
||||
if (value.type === 'bigint') {
|
||||
return value.data.toString();
|
||||
}
|
||||
if (value.type === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
@@ -742,19 +803,315 @@ RED.clipboard = (function() {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function importNodes(nodesStr,addFlow) {
|
||||
var newNodes = nodesStr;
|
||||
if (typeof nodesStr === 'string') {
|
||||
try {
|
||||
nodesStr = nodesStr.trim();
|
||||
if (nodesStr.length === 0) {
|
||||
return;
|
||||
}
|
||||
newNodes = JSON.parse(nodesStr);
|
||||
} catch(err) {
|
||||
var e = new Error(RED._("clipboard.invalidFlow",{message:err.message}));
|
||||
e.code = "NODE_RED";
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
var importOptions = {generateIds: false, addFlow: addFlow};
|
||||
try {
|
||||
RED.view.importNodes(newNodes, importOptions);
|
||||
} catch(error) {
|
||||
// Thrown for import_conflict
|
||||
confirmImport(error.importConfig, newNodes, importOptions);
|
||||
}
|
||||
}
|
||||
|
||||
function confirmImport(importConfig,importNodes,importOptions) {
|
||||
var notification = RED.notify("<p>"+RED._("clipboard.import.conflictNotification1")+"</p>",{
|
||||
type: "info",
|
||||
fixed: true,
|
||||
buttons: [
|
||||
{text: RED._("common.label.cancel"), click: function() { notification.close(); }},
|
||||
{text: RED._("clipboard.import.viewNodes"), click: function() {
|
||||
notification.close();
|
||||
showImportConflicts(importConfig,importNodes,importOptions);
|
||||
}},
|
||||
{text: RED._("clipboard.import.importCopy"), click: function() {
|
||||
notification.close();
|
||||
// generateIds=true to avoid conflicts
|
||||
// and default to the 'old' behaviour around matching
|
||||
// config nodes and subflows
|
||||
importOptions.generateIds = true;
|
||||
RED.view.importNodes(importNodes, importOptions);
|
||||
}}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
function showImportConflicts(importConfig,importNodes,importOptions) {
|
||||
|
||||
pendingImportConfig = {
|
||||
importConfig: importConfig,
|
||||
importNodes: importNodes,
|
||||
importOptions: importOptions
|
||||
}
|
||||
|
||||
var id,node;
|
||||
var treeData = [];
|
||||
var container;
|
||||
var addedHeader = false;
|
||||
for (id in importConfig.subflows) {
|
||||
if (importConfig.subflows.hasOwnProperty(id)) {
|
||||
if (!addedHeader) {
|
||||
treeData.push({gutter:$('<span data-i18n="menu.label.subflows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
|
||||
addedHeader = true;
|
||||
}
|
||||
node = importConfig.subflows[id];
|
||||
var isConflicted = importConfig.conflicted[node.id];
|
||||
var isSelected = !isConflicted;
|
||||
var elements = getNodeElement(node, isConflicted, isSelected );
|
||||
container = {
|
||||
id: node.id,
|
||||
gutter: elements.gutter.element,
|
||||
element: elements.element,
|
||||
class: isSelected?"":"disabled",
|
||||
deferBuild: true,
|
||||
children: []
|
||||
}
|
||||
treeData.push(container);
|
||||
if (importConfig.zMap[id]) {
|
||||
importConfig.zMap[id].forEach(function(node) {
|
||||
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
|
||||
container.children.push({
|
||||
id: node.id,
|
||||
gutter: childElements.gutter.element,
|
||||
element: childElements.element,
|
||||
class: isSelected?"":"disabled"
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
addedHeader = false;
|
||||
for (id in importConfig.tabs) {
|
||||
if (importConfig.tabs.hasOwnProperty(id)) {
|
||||
if (!addedHeader) {
|
||||
treeData.push({gutter:$('<span data-i18n="menu.label.flows"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
|
||||
addedHeader = true;
|
||||
}
|
||||
node = importConfig.tabs[id];
|
||||
var isConflicted = importConfig.conflicted[node.id];
|
||||
var isSelected = true;
|
||||
var elements = getNodeElement(node, isConflicted, isSelected);
|
||||
container = {
|
||||
id: node.id,
|
||||
gutter: elements.gutter.element,
|
||||
element: elements.element,
|
||||
icon: "red-ui-icons red-ui-icons-flow",
|
||||
deferBuild: true,
|
||||
class: isSelected?"":"disabled",
|
||||
children: []
|
||||
}
|
||||
treeData.push(container);
|
||||
if (importConfig.zMap[id]) {
|
||||
importConfig.zMap[id].forEach(function(node) {
|
||||
var childElements = getNodeElement(node, importConfig.conflicted[node.id], isSelected, elements.gutter.cb);
|
||||
container.children.push({
|
||||
id: node.id,
|
||||
gutter: childElements.gutter.element,
|
||||
element: childElements.element,
|
||||
class: isSelected?"":"disabled"
|
||||
})
|
||||
// console.log(" ["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
addedHeader = false;
|
||||
var extraNodes = [];
|
||||
importConfig.all.forEach(function(node) {
|
||||
if (node.type !== "tab" && node.type !== "subflow" && !importConfig.tabs[node.z] && !importConfig.subflows[node.z]) {
|
||||
var isConflicted = importConfig.conflicted[node.id];
|
||||
var isSelected = !isConflicted || !importConfig.configs[node.id];
|
||||
var elements = getNodeElement(node, isConflicted, isSelected);
|
||||
var item = {
|
||||
id: node.id,
|
||||
gutter: elements.gutter.element,
|
||||
element: elements.element,
|
||||
class: isSelected?"":"disabled"
|
||||
}
|
||||
if (importConfig.configs[node.id]) {
|
||||
extraNodes.push(item);
|
||||
} else {
|
||||
if (!addedHeader) {
|
||||
treeData.push({gutter:$('<span data-i18n="menu.label.nodes"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
|
||||
addedHeader = true;
|
||||
}
|
||||
treeData.push(item);
|
||||
}
|
||||
// console.log("["+(importConfig.conflicted[node.id]?"*":" ")+"] "+node.type+" "+node.id);
|
||||
}
|
||||
})
|
||||
if (extraNodes.length > 0) {
|
||||
treeData.push({gutter:$('<span data-i18n="menu.label.displayConfig"></span>'), label: '', class:"red-ui-clipboard-dialog-import-conflicts-item-header"})
|
||||
addedHeader = true;
|
||||
treeData = treeData.concat(extraNodes);
|
||||
|
||||
}
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(importConflictsDialog));
|
||||
|
||||
|
||||
var nodeList = $("#red-ui-clipboard-dialog-import-conflicts-list").css({position:"absolute",top:0,right:0,bottom:0,left:0}).treeList({
|
||||
data: treeData
|
||||
})
|
||||
|
||||
dialogContainer.i18n();
|
||||
var dialogHeight = 400;
|
||||
var winHeight = $(window).height();
|
||||
if (winHeight < 600) {
|
||||
dialogHeight = 400 - (600 - winHeight);
|
||||
}
|
||||
$(".red-ui-clipboard-dialog-box").height(dialogHeight);
|
||||
|
||||
$("#red-ui-clipboard-dialog-ok").hide();
|
||||
$("#red-ui-clipboard-dialog-cancel").show();
|
||||
$("#red-ui-clipboard-dialog-export").hide();
|
||||
$("#red-ui-clipboard-dialog-download").hide();
|
||||
$("#red-ui-clipboard-dialog-import-conflict").show();
|
||||
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes"))
|
||||
.dialog("option","width",500)
|
||||
.dialog( "open" );
|
||||
|
||||
}
|
||||
|
||||
function getNodeElement(n, isConflicted, isSelected, parent) {
|
||||
var element;
|
||||
if (n.type === "tab") {
|
||||
element = getFlowLabel(n, isSelected);
|
||||
} else {
|
||||
element = getNodeLabel(n, isConflicted, isSelected);
|
||||
}
|
||||
var controls = $('<div>',{class:"red-ui-clipboard-dialog-import-conflicts-controls"}).appendTo(element);
|
||||
controls.on("click", function(evt) { evt.stopPropagation(); });
|
||||
if (isConflicted && !parent) {
|
||||
var cb = $('<label><input '+(isSelected?'':'disabled ')+'type="checkbox" data-node-id="'+n.id+'"> <span data-i18n="clipboard.import.replace"></span></label>').appendTo(controls);
|
||||
if (n.type === "tab" || (n.type !== "subflow" && n.hasOwnProperty("x") && n.hasOwnProperty("y"))) {
|
||||
cb.hide();
|
||||
}
|
||||
}
|
||||
return {
|
||||
element: element,
|
||||
gutter: getGutter(n, isSelected, parent)
|
||||
}
|
||||
}
|
||||
|
||||
function getGutter(n, isSelected, parent) {
|
||||
var span = $("<label>",{class:"red-ui-clipboard-dialog-import-conflicts-gutter"});
|
||||
var cb = $('<input data-node-id="'+n.id+'" type="checkbox" '+(isSelected?"checked":"")+'>').appendTo(span);
|
||||
|
||||
if (parent) {
|
||||
cb.attr("disabled",true);
|
||||
parent.addChild(cb);
|
||||
}
|
||||
span.on("click", function(evt) {
|
||||
evt.stopPropagation();
|
||||
})
|
||||
cb.on("change", function(evt) {
|
||||
var state = this.checked;
|
||||
span.parent().toggleClass("disabled",!!!state);
|
||||
span.parent().find('.red-ui-clipboard-dialog-import-conflicts-controls input[type="checkbox"]').attr("disabled",!!!state);
|
||||
childItems.forEach(function(c) {
|
||||
c.attr("checked",state);
|
||||
c.trigger("change");
|
||||
});
|
||||
})
|
||||
var childItems = [];
|
||||
|
||||
var checkbox = {
|
||||
addChild: function(c) {
|
||||
childItems.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cb: checkbox,
|
||||
element: span
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeLabelText(n) {
|
||||
var label = n.name || n.type+": "+n.id;
|
||||
if (n._def.label) {
|
||||
try {
|
||||
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+n.type+".label",err);
|
||||
}
|
||||
}
|
||||
var newlineIndex = label.indexOf("\\n");
|
||||
if (newlineIndex > -1) {
|
||||
label = label.substring(0,newlineIndex)+"...";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
function getFlowLabel(n) {
|
||||
n = JSON.parse(JSON.stringify(n));
|
||||
n._def = RED.nodes.getType(n.type) || {};
|
||||
if (n._def) {
|
||||
n._ = n._def._;
|
||||
}
|
||||
|
||||
var div = $('<div>',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"});
|
||||
var contentDiv = $('<div>',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div);
|
||||
var label = (typeof n === "string")? n : n.label;
|
||||
var newlineIndex = label.indexOf("\\n");
|
||||
if (newlineIndex > -1) {
|
||||
label = label.substring(0,newlineIndex)+"...";
|
||||
}
|
||||
contentDiv.text(label);
|
||||
// A conflicted flow should not be imported by default.
|
||||
return div;
|
||||
}
|
||||
|
||||
function getNodeLabel(n, isConflicted) {
|
||||
n = JSON.parse(JSON.stringify(n));
|
||||
n._def = RED.nodes.getType(n.type) || {};
|
||||
if (n._def) {
|
||||
n._ = n._def._;
|
||||
}
|
||||
var div = $('<div>',{class:"red-ui-info-outline-item"});
|
||||
RED.utils.createNodeIcon(n).appendTo(div);
|
||||
var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div);
|
||||
var labelText = getNodeLabelText(n);
|
||||
var label = $('<div>',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv);
|
||||
if (labelText) {
|
||||
label.text(labelText)
|
||||
} else {
|
||||
label.html(n.type)
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
setupDialogs();
|
||||
|
||||
$('<input type="text" id="red-ui-clipboard-hidden" tabIndex="-1">').appendTo("#red-ui-editor");
|
||||
|
||||
RED.actions.add("core:show-export-dialog",exportNodes);
|
||||
RED.actions.add("core:show-import-dialog",importNodes);
|
||||
RED.actions.add("core:show-export-dialog",showExportNodes);
|
||||
RED.actions.add("core:show-import-dialog",showImportNodes);
|
||||
|
||||
RED.actions.add("core:show-library-export-dialog",function() { exportNodes('library') });
|
||||
RED.actions.add("core:show-library-import-dialog",function() { importNodes('library') });
|
||||
RED.actions.add("core:show-library-export-dialog",function() { showExportNodes('library') });
|
||||
RED.actions.add("core:show-library-import-dialog",function() { showImportNodes('library') });
|
||||
|
||||
RED.actions.add("core:show-examples-import-dialog",function() { importNodes('examples') });
|
||||
RED.actions.add("core:show-examples-import-dialog",function() { showImportNodes('examples') });
|
||||
|
||||
RED.events.on("editor:open",function() { disabled = true; });
|
||||
RED.events.on("editor:close",function() { disabled = false; });
|
||||
@@ -788,7 +1145,7 @@ RED.clipboard = (function() {
|
||||
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var data = event.originalEvent.dataTransfer.getData("text/plain");
|
||||
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
|
||||
RED.view.importNodes(data);
|
||||
importNodes(data);
|
||||
} else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
|
||||
var files = event.originalEvent.dataTransfer.files;
|
||||
if (files.length === 1) {
|
||||
@@ -796,7 +1153,7 @@ RED.clipboard = (function() {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(theFile) {
|
||||
return function(e) {
|
||||
RED.view.importNodes(e.target.result);
|
||||
importNodes(e.target.result);
|
||||
};
|
||||
})(file);
|
||||
reader.readAsText(file);
|
||||
@@ -807,8 +1164,8 @@ RED.clipboard = (function() {
|
||||
});
|
||||
|
||||
},
|
||||
import: importNodes,
|
||||
export: exportNodes,
|
||||
import: showImportNodes,
|
||||
export: showExportNodes,
|
||||
copyText: copyText
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -27,26 +27,26 @@
|
||||
this.partialFlag = false;
|
||||
this.stateValue = 0;
|
||||
var initialState = this.element.prop('checked');
|
||||
this.options = [
|
||||
this.states = [
|
||||
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-square-o"></i></span>').appendTo(this.uiElement),
|
||||
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-check-square-o"></i></span>').appendTo(this.uiElement),
|
||||
$('<span class="red-ui-checkboxSet-option hide"><i class="fa fa-minus-square-o"></i></span>').appendTo(this.uiElement)
|
||||
];
|
||||
if (initialState) {
|
||||
this.options[1].show();
|
||||
this.states[1].show();
|
||||
} else {
|
||||
this.options[0].show();
|
||||
this.states[0].show();
|
||||
}
|
||||
|
||||
this.element.on("change", function() {
|
||||
if (this.checked) {
|
||||
that.options[0].hide();
|
||||
that.options[1].show();
|
||||
that.options[2].hide();
|
||||
that.states[0].hide();
|
||||
that.states[1].show();
|
||||
that.states[2].hide();
|
||||
} else {
|
||||
that.options[1].hide();
|
||||
that.options[0].show();
|
||||
that.options[2].hide();
|
||||
that.states[1].hide();
|
||||
that.states[0].show();
|
||||
that.states[2].hide();
|
||||
}
|
||||
var isChecked = this.checked;
|
||||
that.children.forEach(function(child) {
|
||||
@@ -106,17 +106,17 @@
|
||||
var trueState = this.partialFlag||state;
|
||||
this.element.prop('checked',trueState);
|
||||
if (state === true) {
|
||||
this.options[0].hide();
|
||||
this.options[1].show();
|
||||
this.options[2].hide();
|
||||
this.states[0].hide();
|
||||
this.states[1].show();
|
||||
this.states[2].hide();
|
||||
} else if (state === false) {
|
||||
this.options[2].hide();
|
||||
this.options[1].hide();
|
||||
this.options[0].show();
|
||||
this.states[2].hide();
|
||||
this.states[1].hide();
|
||||
this.states[0].show();
|
||||
} else if (state === null) {
|
||||
this.options[0].hide();
|
||||
this.options[1].hide();
|
||||
this.options[2].show();
|
||||
this.states[0].hide();
|
||||
this.states[1].hide();
|
||||
this.states[2].show();
|
||||
}
|
||||
if (!suppressEvent) {
|
||||
this.element.trigger('change',null);
|
||||
|
||||
@@ -91,6 +91,9 @@
|
||||
if (v!=="auto" && v!=="") {
|
||||
that.topContainer.css(s,v);
|
||||
that.uiContainer.css(s,"0");
|
||||
if (s === "top" && that.options.header) {
|
||||
that.uiContainer.css(s,"20px")
|
||||
}
|
||||
that.element.css(s,'auto');
|
||||
}
|
||||
})
|
||||
|
||||
@@ -84,6 +84,7 @@ RED.popover = (function() {
|
||||
var targetHeight = target.outerHeight();
|
||||
var divHeight = div.height();
|
||||
var divWidth = div.width();
|
||||
var paddingRight = 10;
|
||||
|
||||
var viewportTop = $(window).scrollTop();
|
||||
var viewportLeft = $(window).scrollLeft();
|
||||
@@ -105,7 +106,7 @@ RED.popover = (function() {
|
||||
d = "right";
|
||||
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
|
||||
left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
|
||||
} else if (left+divWidth > viewportRight) {
|
||||
} else if (left+divWidth+paddingRight > viewportRight) {
|
||||
d = "left";
|
||||
top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
|
||||
left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
|
||||
|
||||
@@ -119,6 +119,9 @@
|
||||
if (evt.keyCode === 27) {
|
||||
that.element.val("");
|
||||
}
|
||||
if (evt.keyCode === 13) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
})
|
||||
this.element.on("keyup",function(evt) {
|
||||
that._change($(this).val());
|
||||
|
||||
@@ -29,7 +29,7 @@ RED.tabs = (function() {
|
||||
var currentTabWidth;
|
||||
var currentActiveTabWidth = 0;
|
||||
var collapsibleMenu;
|
||||
|
||||
var preferredOrder = options.order;
|
||||
var ul = options.element || $("#"+options.id);
|
||||
var wrapper = ul.wrap( "<div>" ).parent();
|
||||
var scrollContainer = ul.wrap( "<div>" ).parent();
|
||||
@@ -132,11 +132,11 @@ RED.tabs = (function() {
|
||||
activateTab(id);
|
||||
}
|
||||
};
|
||||
if (tabs[id].pinned) {
|
||||
pinnedOptions.push(opt);
|
||||
} else {
|
||||
// if (tabs[id].pinned) {
|
||||
// pinnedOptions.push(opt);
|
||||
// } else {
|
||||
options.push(opt);
|
||||
}
|
||||
// }
|
||||
});
|
||||
options = pinnedOptions.concat(options);
|
||||
collapsibleMenu = RED.menu.init({options: options});
|
||||
@@ -363,23 +363,39 @@ RED.tabs = (function() {
|
||||
var tabWidth;
|
||||
|
||||
if (options.collapsible) {
|
||||
var availableCount = collapsedButtonsRow.children().length;
|
||||
var visibleCount = collapsedButtonsRow.children(":visible").length;
|
||||
tabWidth = width - collapsedButtonsRow.width()-10;
|
||||
if (tabWidth < 198) {
|
||||
var delta = 198 - tabWidth;
|
||||
var maxTabWidth = 198;
|
||||
var minTabWidth = 80;
|
||||
if (tabWidth <= minTabWidth || (tabWidth < maxTabWidth && visibleCount > 5)) {
|
||||
// The tab is too small. Hide the next button to make room
|
||||
// Start at the end of the button row, -1 for the menu button
|
||||
var b = collapsedButtonsRow.find("a:last").prev();
|
||||
var index = collapsedButtonsRow.children().length - 2;
|
||||
// Work backwards to find the first visible button
|
||||
while (b.is(":not(:visible)")) {
|
||||
b = b.prev();
|
||||
index--;
|
||||
}
|
||||
if (!b.hasClass("red-ui-tab-link-button-pinned")) {
|
||||
// If it isn't a pinned button, hide it to get the room
|
||||
if (tabWidth <= minTabWidth || visibleCount>6) {//}!b.hasClass("red-ui-tab-link-button-pinned")) {
|
||||
b.hide();
|
||||
}
|
||||
tabWidth = width - collapsedButtonsRow.width()-10;
|
||||
tabWidth = Math.max(minTabWidth,width - collapsedButtonsRow.width()-10);
|
||||
} else {
|
||||
var space = width - 198 - collapsedButtonsRow.width();
|
||||
if (visibleCount !== availableCount) {
|
||||
if (visibleCount < 6) {
|
||||
tabWidth = minTabWidth;
|
||||
} else {
|
||||
tabWidth = maxTabWidth;
|
||||
}
|
||||
}
|
||||
var space = width - tabWidth - collapsedButtonsRow.width();
|
||||
if (space > 40) {
|
||||
collapsedButtonsRow.find("a:not(:visible):first").show();
|
||||
tabWidth = width - collapsedButtonsRow.width()-10;
|
||||
}
|
||||
tabWidth = width - collapsedButtonsRow.width()-10;
|
||||
}
|
||||
tabs.css({width:tabWidth});
|
||||
|
||||
@@ -469,7 +485,7 @@ RED.tabs = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
var tabAPI = {
|
||||
addTab: function(tab,targetIndex) {
|
||||
if (options.onselect) {
|
||||
var selection = ul.find("li.red-ui-tab.selected");
|
||||
@@ -531,11 +547,93 @@ RED.tabs = (function() {
|
||||
evt.preventDefault();
|
||||
activateTab(tab.id);
|
||||
});
|
||||
pinnedLink.data("tabId",tab.id)
|
||||
if (tab.pinned) {
|
||||
pinnedLink.addClass("red-ui-tab-link-button-pinned");
|
||||
pinnedTabsCount++;
|
||||
}
|
||||
RED.popover.tooltip($(pinnedLink), tab.name, tab.action);
|
||||
if (options.onreorder) {
|
||||
var pinnedLinkIndex;
|
||||
var pinnedLinks = [];
|
||||
var startPinnedIndex;
|
||||
pinnedLink.draggable({
|
||||
distance: 10,
|
||||
axis:"x",
|
||||
containment: ".red-ui-tab-link-buttons",
|
||||
start: function(event,ui) {
|
||||
dragActive = true;
|
||||
$(".red-ui-tab-link-buttons").width($(".red-ui-tab-link-buttons").width());
|
||||
if (dblClickArmed) { dblClickArmed = false; return false }
|
||||
collapsedButtonsRow.children().each(function(i) {
|
||||
pinnedLinks[i] = {
|
||||
el:$(this),
|
||||
text: $(this).text(),
|
||||
left: $(this).position().left,
|
||||
width: $(this).width(),
|
||||
menu: $(this).hasClass("red-ui-tab-link-button-menu")
|
||||
};
|
||||
if ($(this).is(pinnedLink)) {
|
||||
pinnedLinkIndex = i;
|
||||
startPinnedIndex = i;
|
||||
}
|
||||
});
|
||||
collapsedButtonsRow.children().each(function(i) {
|
||||
if (i!==pinnedLinkIndex) {
|
||||
$(this).css({
|
||||
position: 'absolute',
|
||||
left: pinnedLinks[i].left+"px",
|
||||
width: pinnedLinks[i].width+2,
|
||||
transition: "left 0.3s"
|
||||
});
|
||||
}
|
||||
})
|
||||
if (!pinnedLink.hasClass('active')) {
|
||||
pinnedLink.css({'zIndex':1});
|
||||
}
|
||||
},
|
||||
drag: function(event,ui) {
|
||||
ui.position.left += pinnedLinks[pinnedLinkIndex].left;
|
||||
var tabCenter = ui.position.left + pinnedLinks[pinnedLinkIndex].width/2;
|
||||
for (var i=0;i<pinnedLinks.length;i++) {
|
||||
if (i === pinnedLinkIndex || pinnedLinks[i].menu || pinnedLinks[i].el.is(":not(:visible)")) {
|
||||
continue;
|
||||
}
|
||||
if (tabCenter > pinnedLinks[i].left && tabCenter < pinnedLinks[i].left+pinnedLinks[i].width) {
|
||||
if (i < pinnedLinkIndex) {
|
||||
pinnedLinks[i].left += pinnedLinks[pinnedLinkIndex].width+8;
|
||||
pinnedLinks[pinnedLinkIndex].el.detach().insertBefore(pinnedLinks[i].el);
|
||||
} else {
|
||||
pinnedLinks[i].left -= pinnedLinks[pinnedLinkIndex].width+8;
|
||||
pinnedLinks[pinnedLinkIndex].el.detach().insertAfter(pinnedLinks[i].el);
|
||||
}
|
||||
pinnedLinks[i].el.css({left:pinnedLinks[i].left+"px"});
|
||||
|
||||
pinnedLinks.splice(i, 0, pinnedLinks.splice(pinnedLinkIndex, 1)[0]);
|
||||
|
||||
pinnedLinkIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: function(event,ui) {
|
||||
dragActive = false;
|
||||
collapsedButtonsRow.children().css({position:"relative",left:"",transition:""});
|
||||
$(".red-ui-tab-link-buttons").width('auto');
|
||||
pinnedLink.css({zIndex:""});
|
||||
updateTabWidths();
|
||||
if (startPinnedIndex !== pinnedLinkIndex) {
|
||||
if (collapsibleMenu) {
|
||||
collapsibleMenu.remove();
|
||||
collapsibleMenu = null;
|
||||
}
|
||||
var newOrder = $.makeArray(collapsedButtonsRow.children().map(function() { return $(this).data('tabId');}));
|
||||
tabAPI.order(newOrder);
|
||||
options.onreorder(newOrder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
link.on("mouseup",onTabClick);
|
||||
@@ -565,7 +663,7 @@ RED.tabs = (function() {
|
||||
if (ul.find("li.red-ui-tab").length == 1) {
|
||||
activateTab(link);
|
||||
}
|
||||
if (options.onreorder) {
|
||||
if (options.onreorder && !options.collapsible) {
|
||||
var originalTabOrder;
|
||||
var tabDragIndex;
|
||||
var tabElements = [];
|
||||
@@ -652,6 +750,9 @@ RED.tabs = (function() {
|
||||
collapsibleMenu.remove();
|
||||
collapsibleMenu = null;
|
||||
}
|
||||
if (preferredOrder) {
|
||||
tabAPI.order(preferredOrder);
|
||||
}
|
||||
},
|
||||
removeTab: removeTab,
|
||||
activateTab: activateTab,
|
||||
@@ -673,10 +774,8 @@ RED.tabs = (function() {
|
||||
},
|
||||
selection: getSelection,
|
||||
order: function(order) {
|
||||
preferredOrder = order;
|
||||
var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));
|
||||
if (existingTabOrder.length !== order.length) {
|
||||
return
|
||||
}
|
||||
var i;
|
||||
var match = true;
|
||||
for (i=0;i<order.length;i++) {
|
||||
@@ -692,12 +791,41 @@ RED.tabs = (function() {
|
||||
var existingTabs = ul.children().detach().each(function() {
|
||||
existingTabMap[$(this).data("tabId")] = $(this);
|
||||
});
|
||||
var pinnedButtons = {};
|
||||
if (options.collapsible) {
|
||||
collapsedButtonsRow.children().detach().each(function() {
|
||||
var id = $(this).data("tabId");
|
||||
if (!id) {
|
||||
id = "__menu__"
|
||||
}
|
||||
pinnedButtons[id] = $(this);
|
||||
});
|
||||
}
|
||||
for (i=0;i<order.length;i++) {
|
||||
existingTabMap[order[i]].appendTo(ul);
|
||||
if (existingTabMap[order[i]]) {
|
||||
existingTabMap[order[i]].appendTo(ul);
|
||||
if (options.collapsible) {
|
||||
pinnedButtons[order[i]].appendTo(collapsedButtonsRow);
|
||||
}
|
||||
delete existingTabMap[order[i]];
|
||||
}
|
||||
}
|
||||
// Add any tabs that aren't known in the order
|
||||
for (i in existingTabMap) {
|
||||
if (existingTabMap.hasOwnProperty(i)) {
|
||||
existingTabMap[i].appendTo(ul);
|
||||
if (options.collapsible) {
|
||||
pinnedButtons[i].appendTo(collapsedButtonsRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.collapsible) {
|
||||
pinnedButtons["__menu__"].appendTo(collapsedButtonsRow);
|
||||
updateTabWidths();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return tabAPI;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
invertState = this.options.invertState;
|
||||
}
|
||||
var baseClass = this.options.baseClass || "red-ui-button";
|
||||
var enabledIcon = this.options.enabledIcon || "fa-check-square-o";
|
||||
var disabledIcon = this.options.disabledIcon || "fa-square-o";
|
||||
var enabledIcon = this.options.hasOwnProperty('enabledIcon')?this.options.enabledIcon : "fa-check-square-o";
|
||||
var disabledIcon = this.options.hasOwnProperty('disabledIcon')?this.options.disabledIcon : "fa-square-o";
|
||||
var enabledLabel = this.options.hasOwnProperty('enabledLabel') ? this.options.enabledLabel : RED._("editor:workspace.enabled");
|
||||
var disabledLabel = this.options.hasOwnProperty('disabledLabel') ? this.options.disabledLabel : RED._("editor:workspace.disabled");
|
||||
|
||||
@@ -46,25 +46,41 @@
|
||||
this.element.on("focus", function() {
|
||||
that.button.focus();
|
||||
});
|
||||
this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"><i class="fa"></i> <span></span></button>');
|
||||
this.button = $('<button type="button" class="red-ui-toggleButton '+baseClass+' toggle single"></button>');
|
||||
if (enabledLabel || disabledLabel) {
|
||||
this.buttonLabel = $("<span>").appendTo(this.button);
|
||||
}
|
||||
|
||||
if (this.options.class) {
|
||||
this.button.addClass(this.options.class)
|
||||
}
|
||||
this.element.after(this.button);
|
||||
this.buttonIcon = this.button.find("i");
|
||||
this.buttonLabel = this.button.find("span");
|
||||
|
||||
if (enabledIcon && disabledIcon) {
|
||||
this.buttonIcon = $('<i class="fa"></i>').prependTo(this.button);
|
||||
}
|
||||
|
||||
// Quick hack to find the maximum width of the button
|
||||
this.button.addClass("selected");
|
||||
this.buttonIcon.addClass(enabledIcon);
|
||||
this.buttonLabel.text(enabledLabel);
|
||||
if (this.buttonIcon) {
|
||||
this.buttonIcon.addClass(enabledIcon);
|
||||
}
|
||||
if (this.buttonLabel) {
|
||||
this.buttonLabel.text(enabledLabel);
|
||||
}
|
||||
var width = this.button.width();
|
||||
this.button.removeClass("selected");
|
||||
this.buttonIcon.removeClass(enabledIcon);
|
||||
that.buttonIcon.addClass(disabledIcon);
|
||||
that.buttonLabel.text(disabledLabel);
|
||||
if (this.buttonIcon) {
|
||||
this.buttonIcon.removeClass(enabledIcon);
|
||||
that.buttonIcon.addClass(disabledIcon);
|
||||
}
|
||||
if (this.buttonLabel) {
|
||||
that.buttonLabel.text(disabledLabel);
|
||||
}
|
||||
width = Math.max(width,this.button.width());
|
||||
this.buttonIcon.removeClass(disabledIcon);
|
||||
if (this.buttonIcon) {
|
||||
this.buttonIcon.removeClass(disabledIcon);
|
||||
}
|
||||
|
||||
// Fix the width of the button so it doesn't jump around when toggled
|
||||
if (width > 0) {
|
||||
@@ -73,7 +89,7 @@
|
||||
|
||||
this.button.on("click",function(e) {
|
||||
e.stopPropagation();
|
||||
if (that.buttonIcon.hasClass(disabledIcon)) {
|
||||
if (!that.state) {
|
||||
that.element.prop("checked",!invertState);
|
||||
} else {
|
||||
that.element.prop("checked",invertState);
|
||||
@@ -84,14 +100,24 @@
|
||||
this.element.on("change", function(e) {
|
||||
if ($(this).prop("checked") !== invertState) {
|
||||
that.button.addClass("selected");
|
||||
that.buttonIcon.addClass(enabledIcon);
|
||||
that.buttonIcon.removeClass(disabledIcon);
|
||||
that.buttonLabel.text(enabledLabel);
|
||||
that.state = true;
|
||||
if (that.buttonIcon) {
|
||||
that.buttonIcon.addClass(enabledIcon);
|
||||
that.buttonIcon.removeClass(disabledIcon);
|
||||
}
|
||||
if (that.buttonLabel) {
|
||||
that.buttonLabel.text(enabledLabel);
|
||||
}
|
||||
} else {
|
||||
that.button.removeClass("selected");
|
||||
that.buttonIcon.addClass(disabledIcon);
|
||||
that.buttonIcon.removeClass(enabledIcon);
|
||||
that.buttonLabel.text(disabledLabel);
|
||||
that.state = false;
|
||||
if (that.buttonIcon) {
|
||||
that.buttonIcon.addClass(disabledIcon);
|
||||
that.buttonIcon.removeClass(enabledIcon);
|
||||
}
|
||||
if (that.buttonLabel) {
|
||||
that.buttonLabel.text(disabledLabel);
|
||||
}
|
||||
}
|
||||
})
|
||||
this.element.trigger("change");
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
this._selected = new Set();
|
||||
this._topList = $('<ol class="red-ui-treeList-list">').css({
|
||||
position:'absolute',
|
||||
top: 0,
|
||||
top:0,
|
||||
left:0,
|
||||
right:0,
|
||||
bottom:0
|
||||
@@ -181,6 +181,9 @@
|
||||
that._addSubtree(that._topList,container,item,0);
|
||||
}
|
||||
};
|
||||
if (this.options.header) {
|
||||
topListOptions.header = this.options.header;
|
||||
}
|
||||
if (this.options.rootSortable !== false && !!this.options.sortable) {
|
||||
topListOptions.sortable = this.options.sortable;
|
||||
topListOptions.connectWith = '.red-ui-treeList-sortable';
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
valueLabel: function(container,value) {
|
||||
var that = this;
|
||||
container.css("pointer-events","none");
|
||||
container.css("flex-grow",0);
|
||||
this.elementDiv.hide();
|
||||
var buttons = $('<div>').css({
|
||||
position: "absolute",
|
||||
@@ -184,22 +185,25 @@
|
||||
width:"20px"
|
||||
}).appendTo(buttons).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
var cursorPosition = that.input[0].selectionStart;
|
||||
var currentType = that.input.attr("type");
|
||||
if (currentType === "text") {
|
||||
that.input.attr("type","password");
|
||||
eyeCon.removeClass("fa-eye-slash").addClass("fa-eye");
|
||||
setTimeout(function() {
|
||||
that.input.focus();
|
||||
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
|
||||
},50);
|
||||
} else {
|
||||
that.input.attr("type","text");
|
||||
eyeCon.removeClass("fa-eye").addClass("fa-eye-slash");
|
||||
setTimeout(function() {
|
||||
that.input.focus();
|
||||
that.input[0].setSelectionRange(cursorPosition, cursorPosition);
|
||||
},50);
|
||||
}
|
||||
}).hide();
|
||||
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-1px").appendTo(eyeButton);
|
||||
var eyeCon = $('<i class="fa fa-eye"></i>').css("margin-left","-2px").appendTo(eyeButton);
|
||||
|
||||
if (value === "__PWRD__") {
|
||||
var innerContainer = $('<div><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i><i class="fa fa-asterisk"></i></div>').css({
|
||||
@@ -284,7 +288,7 @@
|
||||
this.input.css('width','100%');
|
||||
this.uiSelect.width(m[1]);
|
||||
this.uiWidth = null;
|
||||
} else {
|
||||
} else if (this.uiWidth !== 0){
|
||||
this.uiSelect.width(this.uiWidth);
|
||||
}
|
||||
["Right","Left"].forEach(function(d) {
|
||||
@@ -304,7 +308,11 @@
|
||||
|
||||
this.element.attr('type','hidden');
|
||||
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
if (!this.options.types && this.options.type) {
|
||||
this.options.types = [this.options.type]
|
||||
} else {
|
||||
this.options.types = this.options.types||Object.keys(allOptions);
|
||||
}
|
||||
|
||||
this.selectTrigger = $('<button class="red-ui-typedInput-type-select" tabindex="0"></button>').prependTo(this.uiSelect);
|
||||
$('<i class="red-ui-typedInput-icon fa fa-caret-down"></i>').toggle(this.options.types.length > 1).appendTo(this.selectTrigger);
|
||||
@@ -872,6 +880,9 @@
|
||||
this.elementDiv.hide();
|
||||
this.valueLabelContainer.hide();
|
||||
} else if (opt.valueLabel) {
|
||||
// Reset any CSS the custom label may have set
|
||||
this.valueLabelContainer.css("pointer-events","");
|
||||
this.valueLabelContainer.css("flex-grow",1);
|
||||
this.valueLabelContainer.show();
|
||||
this.valueLabelContainer.empty();
|
||||
this.elementDiv.hide();
|
||||
@@ -954,6 +965,18 @@
|
||||
},
|
||||
hide: function() {
|
||||
this.uiSelect.hide();
|
||||
},
|
||||
disable: function(val) {
|
||||
if(val === true) {
|
||||
this.uiSelect.attr("disabled", "disabled");
|
||||
} else if (val === false) {
|
||||
this.uiSelect.attr("disabled", null); //remove attr
|
||||
} else {
|
||||
this.uiSelect.attr("disabled", val); //user value
|
||||
}
|
||||
},
|
||||
disabled: function() {
|
||||
return this.uiSelect.attr("disabled");
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
@@ -1411,7 +1411,7 @@ RED.diff = (function() {
|
||||
// Restore the original flow so subsequent merge resolutions can properly
|
||||
// identify new-vs-old
|
||||
RED.nodes.originalFlow(originalFlow);
|
||||
imported[0].forEach(function(n) {
|
||||
imported.nodes.forEach(function(n) {
|
||||
if (nodeChangedStates[n.id] || localChangedStates[n.id]) {
|
||||
n.changed = true;
|
||||
}
|
||||
|
||||
@@ -1630,6 +1630,7 @@ RED.editor = (function() {
|
||||
show: function() {
|
||||
if (editing_node) {
|
||||
RED.sidebar.info.refresh(editing_node);
|
||||
RED.sidebar.help.show(editing_node.type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1836,6 +1837,7 @@ RED.editor = (function() {
|
||||
show: function() {
|
||||
if (editing_config_node) {
|
||||
RED.sidebar.info.refresh(editing_config_node);
|
||||
RED.sidebar.help.show(type, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2722,7 +2724,7 @@ RED.editor = (function() {
|
||||
if (options.globals) {
|
||||
setTimeout(function() {
|
||||
if (!!session.$worker) {
|
||||
session.$worker.send("setOptions", [{globals: options.globals, esversion:6, sub:true, asi:true, maxerr:1000}]);
|
||||
session.$worker.send("setOptions", [{globals: options.globals, maxerr:1000}]);
|
||||
}
|
||||
},100);
|
||||
}
|
||||
@@ -2786,7 +2788,13 @@ RED.editor = (function() {
|
||||
editExpression: function(options) { showTypeEditor("_expression", options) },
|
||||
editJSON: function(options) { showTypeEditor("_json", options) },
|
||||
editMarkdown: function(options) { showTypeEditor("_markdown", options) },
|
||||
editText: function(options) { showTypeEditor("_text", options) },
|
||||
editText: function(options) {
|
||||
if (options.mode == "markdown") {
|
||||
showTypeEditor("_markdown", options)
|
||||
} else {
|
||||
showTypeEditor("_text", options)
|
||||
}
|
||||
},
|
||||
editBuffer: function(options) { showTypeEditor("_buffer", options) },
|
||||
buildEditForm: buildEditForm,
|
||||
validateNode: validateNode,
|
||||
|
||||
@@ -83,7 +83,8 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
var defaultGroupStyle = {
|
||||
label: true
|
||||
label: true,
|
||||
"label-position": "nw"
|
||||
};
|
||||
|
||||
var groupDef = {
|
||||
@@ -97,25 +98,25 @@ RED.group = (function() {
|
||||
var style = this.style || {};
|
||||
RED.colorPicker.create({
|
||||
id:"node-input-style-stroke",
|
||||
value: style.stroke || "#a4a4a4",
|
||||
value: style.stroke || defaultGroupStyle.stroke || "#a4a4a4",
|
||||
palette: colorPalette,
|
||||
cellPerRow: colorCount,
|
||||
cellWidth: 16,
|
||||
cellHeight: 16,
|
||||
cellMargin: 3,
|
||||
none: true,
|
||||
opacity: style['stroke-opacity'] || 1.0
|
||||
opacity: style.hasOwnProperty('stroke-opacity')?style['stroke-opacity']:(defaultGroupStyle.hasOwnProperty('stroke-opacity')?defaultGroupStyle['stroke-opacity']:1.0)
|
||||
}).appendTo("#node-input-row-style-stroke");
|
||||
RED.colorPicker.create({
|
||||
id:"node-input-style-fill",
|
||||
value: style.fill || "none",
|
||||
value: style.fill || defaultGroupStyle.fill ||"none",
|
||||
palette: colorPalette,
|
||||
cellPerRow: colorCount,
|
||||
cellWidth: 16,
|
||||
cellHeight: 16,
|
||||
cellMargin: 3,
|
||||
none: true,
|
||||
opacity: style['fill-opacity'] || 1.0
|
||||
opacity: style.hasOwnProperty('fill-opacity')?style['fill-opacity']:(defaultGroupStyle.hasOwnProperty('fill-opacity')?defaultGroupStyle['fill-opacity']:1.0)
|
||||
}).appendTo("#node-input-row-style-fill");
|
||||
|
||||
createLayoutPicker({
|
||||
@@ -125,7 +126,7 @@ RED.group = (function() {
|
||||
|
||||
RED.colorPicker.create({
|
||||
id:"node-input-style-color",
|
||||
value: style.color || "#a4a4a4",
|
||||
value: style.color || defaultGroupStyle.color ||"#a4a4a4",
|
||||
palette: colorPalette,
|
||||
cellPerRow: colorCount,
|
||||
cellWidth: 16,
|
||||
@@ -161,12 +162,13 @@ RED.group = (function() {
|
||||
delete this.style.color;
|
||||
}
|
||||
|
||||
if (this.style["stroke-opacity"] === "1") {
|
||||
delete this.style["stroke-opacity"]
|
||||
}
|
||||
if (this.style["fill-opacity"] === "1") {
|
||||
delete this.style["fill-opacity"]
|
||||
}
|
||||
var node = this;
|
||||
['stroke','fill','stroke-opacity','fill-opacity','color','label-position'].forEach(function(prop) {
|
||||
if (node.style[prop] === defaultGroupStyle[prop]) {
|
||||
delete node.style[prop]
|
||||
}
|
||||
})
|
||||
|
||||
this.resize = true;
|
||||
},
|
||||
set:{
|
||||
@@ -219,9 +221,17 @@ RED.group = (function() {
|
||||
"stroke-opacity": groupStyle.strokeOpacity,
|
||||
fill: convertColorToHex(groupStyle.fill),
|
||||
"fill-opacity": groupStyle.fillOpacity,
|
||||
label: true
|
||||
label: true,
|
||||
"label-position": "nw"
|
||||
}
|
||||
groupStyleDiv.remove();
|
||||
groupStyleDiv = $("<div>",{
|
||||
class:"red-ui-flow-group-label",
|
||||
style: "position: absolute; top: -1000px;"
|
||||
}).appendTo(document.body);
|
||||
groupStyle = getComputedStyle(groupStyleDiv[0]);
|
||||
defaultGroupStyle.color = convertColorToHex(groupStyle.fill);
|
||||
groupStyleDiv.remove();
|
||||
}
|
||||
|
||||
function convertColorToHex(c) {
|
||||
@@ -237,6 +247,7 @@ RED.group = (function() {
|
||||
var groupStyleClipboard;
|
||||
|
||||
function copyGroupStyle() {
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes && selection.nodes.length === 1 && selection.nodes[0].type === 'group') {
|
||||
groupStyleClipboard = JSON.parse(JSON.stringify(selection.nodes[0].style));
|
||||
@@ -244,6 +255,7 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
function pasteGroupStyle() {
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
if (groupStyleClipboard) {
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
@@ -277,6 +289,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function groupSelection() {
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var group = createGroup(selection.nodes);
|
||||
@@ -293,6 +306,7 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
function ungroupSelection() {
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var newSelection = [];
|
||||
@@ -341,8 +355,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function mergeSelection() {
|
||||
// TODO: this currently creates an entirely new group. Need to merge properties
|
||||
// of any existing group
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var nodes = [];
|
||||
@@ -370,11 +383,16 @@ RED.group = (function() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
var existingGroup;
|
||||
|
||||
// Second pass, ungroup any groups in the selection and add their contents
|
||||
// to the selection
|
||||
for (var i=0; i<selection.nodes.length; i++) {
|
||||
n = selection.nodes[i];
|
||||
if (n.type === "group") {
|
||||
if (!existingGroup) {
|
||||
existingGroup = n;
|
||||
}
|
||||
ungroupHistoryEvent.groups.push(n);
|
||||
nodes = nodes.concat(ungroup(n));
|
||||
} else {
|
||||
@@ -388,6 +406,10 @@ RED.group = (function() {
|
||||
// Finally, create the new group
|
||||
var group = createGroup(nodes);
|
||||
if (group) {
|
||||
if (existingGroup) {
|
||||
group.style = existingGroup.style;
|
||||
group.name = existingGroup.name;
|
||||
}
|
||||
RED.view.select({nodes:[group]})
|
||||
}
|
||||
historyEvent.events.push({
|
||||
@@ -401,6 +423,7 @@ RED.group = (function() {
|
||||
}
|
||||
|
||||
function removeSelection() {
|
||||
if (RED.view.state() !== RED.state.DEFAULT) { return }
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var nodes = [];
|
||||
|
||||
@@ -17,6 +17,8 @@ RED.keyboard = (function() {
|
||||
|
||||
var isMac = /Mac/i.test(window.navigator.platform);
|
||||
|
||||
var handlersActive = true;
|
||||
|
||||
var handlers = {};
|
||||
var partialState;
|
||||
|
||||
@@ -68,6 +70,11 @@ RED.keyboard = (function() {
|
||||
}
|
||||
}
|
||||
}
|
||||
function getUserKey(action) {
|
||||
var currentEditorSettings = RED.settings.get('editor') || {};
|
||||
var userKeymap = currentEditorSettings.keymap || {};
|
||||
return userKeymap[action];
|
||||
}
|
||||
function init() {
|
||||
// Migrate from pre-0.18
|
||||
migrateOldKeymap();
|
||||
@@ -225,6 +232,9 @@ RED.keyboard = (function() {
|
||||
}
|
||||
}
|
||||
d3.select(window).on("keydown",function() {
|
||||
if (!handlersActive) {
|
||||
return;
|
||||
}
|
||||
if (metaKeyCodes[d3.event.keyCode]) {
|
||||
return;
|
||||
}
|
||||
@@ -250,6 +260,19 @@ RED.keyboard = (function() {
|
||||
var i=0;
|
||||
if (typeof key === 'string') {
|
||||
if (typeof cbdown === 'string') {
|
||||
if (!ondown && !defaultKeyMap.hasOwnProperty(cbdown)) {
|
||||
defaultKeyMap[cbdown] = {
|
||||
scope:scope,
|
||||
key:key,
|
||||
user:false
|
||||
}
|
||||
}
|
||||
if (!ondown) {
|
||||
var userAction = getUserKey(cbdown);
|
||||
if (userAction) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
actionToKeyMap[cbdown] = {scope:scope,key:key};
|
||||
if (typeof ondown === 'boolean') {
|
||||
actionToKeyMap[cbdown].user = ondown;
|
||||
@@ -412,11 +435,9 @@ RED.keyboard = (function() {
|
||||
});
|
||||
revertButton.on("click", function(e) {
|
||||
e.stopPropagation();
|
||||
RED.keyboard.revertToDefault(object.id);
|
||||
container.empty();
|
||||
container.removeClass('keyboard-shortcut-entry-expanded');
|
||||
var shortcut = RED.keyboard.getShortcut(object.id);
|
||||
var userKeymap = RED.settings.get('keymap') || {};
|
||||
// var userKeymap = RED.settings.get('keymap') || {};
|
||||
|
||||
var currentEditorSettings = RED.settings.get('editor') || {};
|
||||
var userKeymap = currentEditorSettings.keymap || {};
|
||||
@@ -424,6 +445,9 @@ RED.keyboard = (function() {
|
||||
currentEditorSettings.keymap = userKeymap;
|
||||
RED.settings.set('editor',currentEditorSettings);
|
||||
|
||||
RED.keyboard.revertToDefault(object.id);
|
||||
|
||||
var shortcut = RED.keyboard.getShortcut(object.id);
|
||||
var obj = {
|
||||
id:object.id,
|
||||
scope:shortcut?shortcut.scope:undefined,
|
||||
@@ -570,6 +594,13 @@ RED.keyboard = (function() {
|
||||
return pane;
|
||||
}
|
||||
|
||||
function enable() {
|
||||
handlersActive = true;
|
||||
}
|
||||
function disable() {
|
||||
handlersActive = false;
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add: addHandler,
|
||||
@@ -577,9 +608,12 @@ RED.keyboard = (function() {
|
||||
getShortcut: function(actionName) {
|
||||
return actionToKeyMap[actionName];
|
||||
},
|
||||
getUserShortcut: getUserKey,
|
||||
revertToDefault: revertToDefault,
|
||||
formatKey: formatKey,
|
||||
validateKey: validateKey
|
||||
validateKey: validateKey,
|
||||
disable: disable,
|
||||
enable: enable
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -472,6 +472,8 @@ RED.library = (function() {
|
||||
autoOpen: false,
|
||||
width: 800,
|
||||
resizable: false,
|
||||
open: function( event, ui ) { RED.keyboard.disable() },
|
||||
close: function( event, ui ) { RED.keyboard.enable() },
|
||||
classes: {
|
||||
"ui-dialog": "red-ui-editor-dialog",
|
||||
"ui-dialog-titlebar-close": "hide",
|
||||
@@ -556,9 +558,11 @@ RED.library = (function() {
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
if (libraryEditor) {
|
||||
libraryEditor.destroy();
|
||||
libraryEditor = null;
|
||||
|
||||
@@ -542,8 +542,6 @@ RED.palette.editor = (function() {
|
||||
return settingsPane;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createSettingsPane() {
|
||||
settingsPane = $('<div id="red-ui-settings-tab-palette"></div>');
|
||||
var content = $('<div id="red-ui-palette-editor">'+
|
||||
@@ -574,7 +572,11 @@ RED.palette.editor = (function() {
|
||||
minimumActiveTabWidth: 110
|
||||
});
|
||||
|
||||
createNodeTab(content);
|
||||
createInstallTab(content);
|
||||
}
|
||||
|
||||
function createNodeTab(content) {
|
||||
var modulesTab = $('<div>',{class:"red-ui-palette-editor-tab"}).appendTo(content);
|
||||
|
||||
editorTabs.addTab({
|
||||
@@ -726,9 +728,9 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createInstallTab(content) {
|
||||
var installTab = $('<div>',{class:"red-ui-palette-editor-tab hide"}).appendTo(content);
|
||||
|
||||
editorTabs.addTab({
|
||||
@@ -761,7 +763,6 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$('<span>').text(RED._("palette.editor.sort")+' ').appendTo(toolBar);
|
||||
var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar);
|
||||
var sortRelevance = $('<a href="#" class="red-ui-palette-editor-install-sort-option red-ui-sidebar-header-button-toggle selected"><i class="fa fa-sort-amount-desc"></i></a>').appendTo(sortGroup);
|
||||
@@ -795,6 +796,7 @@ RED.palette.editor = (function() {
|
||||
loadedIndex = {};
|
||||
initInstallTab();
|
||||
})
|
||||
RED.popover.tooltip(refreshButton,RED._("palette.editor.refresh"));
|
||||
|
||||
packageList = $('<ol>',{style:"position: absolute;top: 79px;bottom: 0;left: 0;right: 0px;"}).appendTo(installTab).editableList({
|
||||
addButton: false,
|
||||
@@ -878,8 +880,87 @@ RED.palette.editor = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
if (RED.settings.theme('palette.upload') !== false) {
|
||||
var uploadSpan = $('<span class="button-group">').prependTo(toolBar);
|
||||
var uploadButton = $('<button type="button" class="red-ui-sidebar-header-button red-ui-palette-editor-upload-button"><label><i class="fa fa-upload"></i><form id="red-ui-palette-editor-upload-form" enctype="multipart/form-data"><input name="tarball" type="file" accept=".tgz"></label></button>').appendTo(uploadSpan);
|
||||
|
||||
var uploadInput = uploadButton.find('input[type="file"]');
|
||||
uploadInput.on("change", function(evt) {
|
||||
if (this.files.length > 0) {
|
||||
uploadFilenameLabel.text(this.files[0].name)
|
||||
uploadToolbar.slideDown(200);
|
||||
}
|
||||
})
|
||||
|
||||
var uploadToolbar = $('<div class="red-ui-palette-editor-upload"></div>').appendTo(installTab);
|
||||
var uploadForm = $('<div>').appendTo(uploadToolbar);
|
||||
var uploadFilename = $('<div class="placeholder-input"><i class="fa fa-upload"></i> </div>').appendTo(uploadForm);
|
||||
var uploadFilenameLabel = $('<span></span>').appendTo(uploadFilename);
|
||||
var uploadButtons = $('<div class="red-ui-palette-editor-upload-buttons"></div>').appendTo(uploadForm);
|
||||
$('<button class="editor-button"></button>').text(RED._("common.label.cancel")).appendTo(uploadButtons).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
uploadToolbar.slideUp(200);
|
||||
uploadInput.val("");
|
||||
});
|
||||
$('<button class="editor-button primary"></button>').text(RED._("common.label.upload")).appendTo(uploadButtons).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
|
||||
var spinner = RED.utils.addSpinnerOverlay(uploadToolbar, true);
|
||||
var buttonRow = $('<div style="position: relative;bottom: calc(50% + 17px); padding-right: 10px;text-align: right;"></div>').appendTo(spinner);
|
||||
$('<button class="red-ui-button"></button>').text(RED._("eventLog.view")).appendTo(buttonRow).on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
RED.actions.invoke("core:show-event-log");
|
||||
});
|
||||
RED.eventLog.startEvent(RED._("palette.editor.confirm.button.install")+" : "+uploadInput[0].files[0].name);
|
||||
|
||||
var data = new FormData();
|
||||
data.append("tarball",uploadInput[0].files[0]);
|
||||
var filename = uploadInput[0].files[0].name;
|
||||
$.ajax({
|
||||
url: 'nodes',
|
||||
data: data,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
method: 'POST',
|
||||
}).always(function(data,textStatus,xhr) {
|
||||
spinner.remove();
|
||||
uploadInput.val("");
|
||||
uploadToolbar.slideUp(200);
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
var message = textStatus;
|
||||
if (xhr.responseJSON) {
|
||||
message = xhr.responseJSON.message;
|
||||
}
|
||||
var notification = RED.notify(RED._('palette.editor.errors.installFailed',{module: filename,message:message}),{
|
||||
type: 'error',
|
||||
modal: true,
|
||||
fixed: true,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
notification.close();
|
||||
}
|
||||
},{
|
||||
text: RED._("eventLog.view"),
|
||||
click: function() {
|
||||
notification.close();
|
||||
RED.actions.invoke("core:show-event-log");
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
uploadInput.val("");
|
||||
uploadToolbar.slideUp(200);
|
||||
})
|
||||
})
|
||||
RED.popover.tooltip(uploadButton,RED._("palette.editor.upload"));
|
||||
}
|
||||
|
||||
$('<div id="red-ui-palette-module-install-shade" class="red-ui-palette-module-shade hide"><div class="red-ui-palette-module-shade-status"></div><img src="red/images/spin.svg" class="red-ui-palette-spinner"/></div>').appendTo(installTab);
|
||||
}
|
||||
|
||||
function update(entry,version,url,container,done) {
|
||||
if (RED.settings.theme('palette.editable') === false) {
|
||||
done(new Error('Palette not editable'));
|
||||
|
||||
@@ -417,7 +417,8 @@ RED.palette = (function() {
|
||||
RED.workspaces.show(nt.substring(8));
|
||||
e.preventDefault();
|
||||
});
|
||||
nodeInfo = RED.utils.renderMarkdown(def.info||"");
|
||||
var subflow = RED.nodes.subflow(nt.substring(8));
|
||||
nodeInfo = RED.utils.renderMarkdown(subflow.info||"");
|
||||
}
|
||||
setLabel(nt,d,label,nodeInfo);
|
||||
|
||||
|
||||
@@ -166,34 +166,42 @@ RED.projects.settings = (function() {
|
||||
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
|
||||
}
|
||||
|
||||
function editSummary(activeProject, summary, container) {
|
||||
function editSummary(activeProject, summary, container, version, versionContainer) {
|
||||
var editButton = container.prev();
|
||||
editButton.hide();
|
||||
container.empty();
|
||||
versionContainer.empty();
|
||||
var bg = $('<span class="button-row" style="position: relative; float: right; margin-right:0;"></span>').appendTo(container);
|
||||
var input = $('<input type="text" style="width: calc(100% - 150px); margin-right: 10px;">').val(summary||"").appendTo(container);
|
||||
var versionInput = $('<input type="text" style="width: calc(100% - 150px); margin-right: 10px;">').val(version||"").appendTo(versionContainer);
|
||||
|
||||
$('<button class="red-ui-button">' + RED._("common.label.cancel") + '</button>')
|
||||
.appendTo(bg)
|
||||
.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
updateProjectSummary(activeProject.summary, container);
|
||||
updateProjectVersion(activeProject.version, versionContainer);
|
||||
editButton.show();
|
||||
});
|
||||
$('<button class="red-ui-button">' + RED._("common.label.save") + '</button>')
|
||||
.appendTo(bg)
|
||||
.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
var v = input.val();
|
||||
updateProjectSummary(v, container);
|
||||
var spinner = utils.addSpinnerOverlay(container);
|
||||
var newSummary = input.val();
|
||||
var newVersion = versionInput.val();
|
||||
updateProjectSummary(newSummary, container);
|
||||
updateProjectVersion(newVersion, versionContainer);
|
||||
var spinner = utils.addSpinnerOverlay(container).addClass('red-ui-component-spinner-contain');
|
||||
var done = function(err,res) {
|
||||
if (err) {
|
||||
spinner.remove();
|
||||
return editSummary(activeProject, summary, container);
|
||||
return editSummary(activeProject, summary, container, version, versionContainer);
|
||||
}
|
||||
activeProject.summary = v;
|
||||
activeProject.summary = newSummary;
|
||||
activeProject.version = newVersion;
|
||||
spinner.remove();
|
||||
updateProjectSummary(activeProject.summary, container);
|
||||
updateProjectVersion(activeProject.version, versionContainer);
|
||||
editButton.show();
|
||||
}
|
||||
utils.sendRequest({
|
||||
@@ -214,31 +222,39 @@ RED.projects.settings = (function() {
|
||||
}
|
||||
},
|
||||
}
|
||||
},{summary:v});
|
||||
},{summary:newSummary, version: newVersion});
|
||||
});
|
||||
}
|
||||
function updateProjectSummary(summary, container) {
|
||||
container.empty();
|
||||
if (summary) {
|
||||
container.text(summary).removeClass('node-info-node');
|
||||
container.text(summary).removeClass('red-ui-help-info-none');
|
||||
} else {
|
||||
container.text(RED._("sidebar.project.noSummaryAvailable")).addClass('red-ui-help-info-none');
|
||||
}
|
||||
}
|
||||
|
||||
function updateProjectVersion(version, container) {
|
||||
container.empty();
|
||||
if (version) {
|
||||
container.text(version);
|
||||
}
|
||||
}
|
||||
function createMainPane(activeProject) {
|
||||
|
||||
var pane = $('<div id="red-ui-project-settings-tab-main" class="red-ui-project-settings-tab-pane red-ui-help"></div>');
|
||||
$('<h1>').text(activeProject.name).appendTo(pane);
|
||||
var summary = $('<div style="position: relative">').appendTo(pane);
|
||||
var summaryContent = $('<div></div>').appendTo(summary);
|
||||
var versionContent = $('<div></div>').addClass('red-ui-help-info-none').appendTo(summary);
|
||||
updateProjectSummary(activeProject.summary, summaryContent);
|
||||
updateProjectVersion(activeProject.version, versionContent);
|
||||
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
$('<button class="red-ui-button red-ui-button-small" style="float: right;">' + RED._('sidebar.project.editDescription') + '</button>')
|
||||
.prependTo(summary)
|
||||
.on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
editSummary(activeProject, activeProject.summary, summaryContent);
|
||||
editSummary(activeProject, activeProject.summary, summaryContent, activeProject.version, versionContent);
|
||||
});
|
||||
}
|
||||
$('<hr>').appendTo(pane);
|
||||
@@ -1017,7 +1033,7 @@ RED.projects.settings = (function() {
|
||||
|
||||
var credentialSecretExistingRow = $('<div class="red-ui-settings-row red-ui-settings-row-credentials"></div>').appendTo(credentialFormRows);
|
||||
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.currentKey")).appendTo(credentialSecretExistingRow);
|
||||
var credentialSecretExistingInput = $('<input type="password">').appendTo(credentialSecretExistingRow)
|
||||
var credentialSecretExistingInput = $('<input type="text">').appendTo(credentialSecretExistingRow).typedInput({type:"cred"})
|
||||
.on("change keyup paste",function() {
|
||||
if (popover) {
|
||||
popover.close();
|
||||
@@ -1030,7 +1046,7 @@ RED.projects.settings = (function() {
|
||||
|
||||
|
||||
$('<label for=""></label>').text(RED._("sidebar.project.projectSettings.newKey")).appendTo(credentialSecretNewRow);
|
||||
var credentialSecretNewInput = $('<input type="password">').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles);
|
||||
var credentialSecretNewInput = $('<input type="text">').appendTo(credentialSecretNewRow).typedInput({type:"cred"}).on("change keyup paste",checkFiles);
|
||||
|
||||
var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i>' + RED._("sidebar.project.projectSettings.credentialsAlert") + '</div>').hide().appendTo(credentialFormRows);
|
||||
|
||||
@@ -1573,8 +1589,6 @@ RED.projects.settings = (function() {
|
||||
updateForm();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function createSettingsPane(activeProject) {
|
||||
var pane = $('<div id="red-ui-project-settings-tab-settings" class="red-ui-project-settings-tab-pane red-ui-help"></div>');
|
||||
createFilesSection(activeProject,pane);
|
||||
|
||||
@@ -38,13 +38,34 @@ RED.projects.userSettings = (function() {
|
||||
$('<label for="user-settings-gitconfig-email"></label>').text(RED._("editor:sidebar.project.userSettings.email")).appendTo(row);
|
||||
gitEmailInput = $('<input type="text" id="user-settings-gitconfig-email">').appendTo(row);
|
||||
gitEmailInput.val(currentGitSettings.user.email||"");
|
||||
|
||||
}
|
||||
|
||||
function createWorkflowSection(pane) {
|
||||
|
||||
var currentGitSettings = RED.settings.get('git') || {};
|
||||
currentGitSettings.workflow = currentGitSettings.workflow || {};
|
||||
currentGitSettings.workflow.mode = currentGitSettings.workflow.mode || "manual";
|
||||
|
||||
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.workflow")).appendTo(pane);
|
||||
|
||||
var workflowContainer = $('<div class="red-ui-settings-section"></div>').appendTo(pane);
|
||||
$('<div class="red-ui-settings-section-description"></div>').appendTo(workflowContainer).text(RED._("editor:sidebar.project.userSettings.workfowTip"));
|
||||
|
||||
var row = $('<div class="red-ui-settings-row"></div>').appendTo(workflowContainer);
|
||||
$('<label><input type="radio" name="user-setting-gitworkflow" value="manual"> <div style="margin-left: 3px; display: inline-block"><div data-i18n="editor:sidebar.project.userSettings.workflowManual"></div><div style="color:#aaa;" data-i18n="editor:sidebar.project.userSettings.workflowManualTip"></div></div></label>').appendTo(row);
|
||||
row = $('<div class="red-ui-settings-row"></div>').appendTo(workflowContainer);
|
||||
$('<label><input type="radio" name="user-setting-gitworkflow" value="auto"> <div style="margin-left: 3px; display: inline-block"><div data-i18n="editor:sidebar.project.userSettings.workflowAuto"></div><div style="color:#aaa;" data-i18n="editor:sidebar.project.userSettings.workflowAutoTip"></div></div></label>').appendTo(row);
|
||||
|
||||
workflowContainer.find('[name="user-setting-gitworkflow"][type="radio"][value="'+currentGitSettings.workflow.mode+'"]').prop('checked',true)
|
||||
|
||||
}
|
||||
|
||||
|
||||
function createSSHKeySection(pane) {
|
||||
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(pane);
|
||||
var container = $('<div class="red-ui-settings-section"></div>').appendTo(pane);
|
||||
var popover;
|
||||
var title = $('<h3></h3>').text(RED._("editor:sidebar.project.userSettings.sshKeys")).appendTo(container);
|
||||
var subtitle = $('<div class="red-ui-settings-section-description"></div>').appendTo(container).text(RED._("editor:sidebar.project.userSettings.sshKeysTip"));
|
||||
|
||||
var addKeyButton = $('<button id="user-settings-gitconfig-add-key" class="red-ui-button red-ui-button-small" style="float: right; margin-right: 10px;">'+RED._("editor:sidebar.project.userSettings.add")+'</button>')
|
||||
@@ -391,6 +412,7 @@ RED.projects.userSettings = (function() {
|
||||
function createSettingsPane(activeProject) {
|
||||
var pane = $('<div id="red-ui-settings-tab-gitconfig" class="project-settings-tab-pane red-ui-help"></div>');
|
||||
createGitUserSection(pane);
|
||||
createWorkflowSection(pane);
|
||||
createSSHKeySection(pane);
|
||||
return pane;
|
||||
}
|
||||
@@ -407,6 +429,9 @@ RED.projects.userSettings = (function() {
|
||||
currentGitSettings.user = currentGitSettings.user || {};
|
||||
currentGitSettings.user.name = gitUsernameInput.val();
|
||||
currentGitSettings.user.email = gitEmailInput.val();
|
||||
currentGitSettings.workflow = currentGitSettings.workflow || {};
|
||||
currentGitSettings.workflow.mode = $('[name="user-setting-gitworkflow"][type="radio"]:checked').val()
|
||||
|
||||
RED.settings.set('git', currentGitSettings);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -81,8 +81,8 @@ RED.projects = (function() {
|
||||
$('<p>').text(RED._("projects.welcome.desc2")).appendTo(body);
|
||||
|
||||
var row = $('<div style="text-align: center"></div>').appendTo(body);
|
||||
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
|
||||
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
|
||||
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.welcome.create")+'</button>').appendTo(row);
|
||||
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.welcome.clone")+'</button>').appendTo(row);
|
||||
|
||||
createAsEmpty.on("click", function(e) {
|
||||
e.preventDefault();
|
||||
@@ -511,7 +511,8 @@ RED.projects = (function() {
|
||||
|
||||
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.clone-project.passwd")+'</label>').appendTo(subrow);
|
||||
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
|
||||
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
|
||||
projectRepoPasswordInput.typedInput({type:"cred"});
|
||||
// -----------------------------------------------------
|
||||
|
||||
// Repo credentials - key/passphrase -------------------
|
||||
@@ -539,12 +540,12 @@ RED.projects = (function() {
|
||||
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.clone-project.passphrase")+'</label>').appendTo(subrow);
|
||||
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
|
||||
|
||||
projectRepoPassphrase.typedInput({type:"cred"});
|
||||
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
|
||||
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
|
||||
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.clone-project.ssh-key-desc")+'</div>').appendTo(sshwarningRow);
|
||||
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
|
||||
$('<button class="red-ui-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
|
||||
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.clone-project.ssh-key-add")+'</button>').appendTo(subrow).on("click", function(e) {
|
||||
e.preventDefault();
|
||||
dialog.dialog( "close" );
|
||||
RED.userSettings.show('gitconfig');
|
||||
@@ -558,8 +559,8 @@ RED.projects = (function() {
|
||||
// Secret - clone
|
||||
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(body);
|
||||
$('<label>'+RED._("projects.clone-project.credential-key")+'</label>').appendTo(row);
|
||||
projectSecretInput = $('<input type="password"></input>').appendTo(row);
|
||||
|
||||
projectSecretInput = $('<input style="width: 100%" type="password"></input>').appendTo(row);
|
||||
projectSecretInput.typedInput({type:"cred"});
|
||||
|
||||
|
||||
return container;
|
||||
@@ -894,6 +895,7 @@ RED.projects = (function() {
|
||||
$('<label class="red-ui-projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">'+RED._("projects.encryption-config.use-custom")+'</span></label>').appendTo(row);
|
||||
row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
|
||||
emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row);
|
||||
emptyProjectCredentialInput.typedInput({type:"cred"});
|
||||
emptyProjectCredentialInput.on("change keyup paste", validateForm);
|
||||
|
||||
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
|
||||
@@ -1169,11 +1171,11 @@ RED.projects = (function() {
|
||||
|
||||
row = $('<div class="form-row button-group"></div>').appendTo(container);
|
||||
|
||||
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
|
||||
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
|
||||
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
|
||||
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
|
||||
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
|
||||
var openProject = $('<button data-type="open" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-folder-open"></i><br/>'+RED._("projects.create.open")+'</button>').appendTo(row);
|
||||
var createAsEmpty = $('<button data-type="empty" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>'+RED._("projects.create.create")+'</button>').appendTo(row);
|
||||
// var createAsCopy = $('<button data-type="copy" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i class="fa fa-long-arrow-right fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Copy existing</button>').appendTo(row);
|
||||
var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>'+RED._("projects.create.clone")+'</button>').appendTo(row);
|
||||
// var createAsClone = $('<button data-type="clone" class="red-ui-button red-ui-projects-dialog-button red-ui-projects-dialog-screen-create-type toggle"><i class="fa fa-git fa-2x"></i><i class="fa fa-arrows-h fa-2x"></i><i class="fa fa-archive fa-2x"></i><br/>Clone Repository</button>').appendTo(row);
|
||||
row.find(".red-ui-projects-dialog-screen-create-type").on("click", function(evt) {
|
||||
evt.preventDefault();
|
||||
container.find(".red-ui-projects-dialog-screen-create-type").removeClass('selected');
|
||||
@@ -1298,6 +1300,7 @@ RED.projects = (function() {
|
||||
$('<label class="red-ui-projects-edit-form-inline-label">'+RED._("projects.create.encryption-key")+'</label>').appendTo(row);
|
||||
// row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
|
||||
emptyProjectCredentialInput = $('<input type="password"></input>').appendTo(row);
|
||||
emptyProjectCredentialInput.typedInput({type:"cred"});
|
||||
emptyProjectCredentialInput.on("change keyup paste", validateForm);
|
||||
$('<label class="red-ui-projects-edit-form-sublabel"><small>'+RED._("projects.create.desc0")+'</small></label>').appendTo(row);
|
||||
|
||||
@@ -1356,7 +1359,8 @@ RED.projects = (function() {
|
||||
|
||||
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-repo-pass">'+RED._("projects.create.password")+'</label>').appendTo(subrow);
|
||||
projectRepoPasswordInput = $('<input id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
|
||||
projectRepoPasswordInput = $('<input style="width:100%" id="red-ui-projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
|
||||
projectRepoPasswordInput.typedInput({type:"cred"});
|
||||
// -----------------------------------------------------
|
||||
|
||||
// Repo credentials - key/passphrase -------------------
|
||||
@@ -1384,12 +1388,13 @@ RED.projects = (function() {
|
||||
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-repo-passphrase">'+RED._("projects.create.passphrase")+'</label>').appendTo(subrow);
|
||||
projectRepoPassphrase = $('<input id="red-ui-projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
|
||||
projectRepoPassphrase.typedInput({type:"cred"});
|
||||
|
||||
subrow = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
|
||||
var sshwarningRow = $('<div class="red-ui-projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
|
||||
$('<div class="form-row"><i class="fa fa-warning"></i> '+RED._("projects.create.desc2")+'</div>').appendTo(sshwarningRow);
|
||||
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
|
||||
$('<button class="red-ui-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
|
||||
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("projects.create.add-ssh-key")+'</button>').appendTo(subrow).on("click", function(e) {
|
||||
e.preventDefault();
|
||||
$('#red-ui-projects-dialog-cancel').trigger("click");
|
||||
RED.userSettings.show('gitconfig');
|
||||
@@ -1403,8 +1408,8 @@ RED.projects = (function() {
|
||||
// Secret - clone
|
||||
row = $('<div class="hide form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-clone"></div>').appendTo(container);
|
||||
$('<label>'+RED._("projects.create.credentials-encryption-key")+'</label>').appendTo(row);
|
||||
projectSecretInput = $('<input type="password"></input>').appendTo(row);
|
||||
|
||||
projectSecretInput = $('<input style="width:100%" type="password"></input>').appendTo(row);
|
||||
projectSecretInput.typedInput({type:"cred"});
|
||||
|
||||
switch(options.screen||"empty") {
|
||||
case "empty": createAsEmpty.trigger("click"); break;
|
||||
@@ -1617,14 +1622,14 @@ RED.projects = (function() {
|
||||
function deleteProject(row,name,done) {
|
||||
var cover = $('<div class="red-ui-projects-dialog-project-list-entry-delete-confirm"></div>').on("click", function(evt) { evt.stopPropagation(); }).appendTo(row);
|
||||
$('<span>').text(RED._("projects.delete.confirm")).appendTo(cover);
|
||||
$('<button class="red-ui-button">'+RED._("common.label.cancel")+'</button>')
|
||||
$('<button class="red-ui-button red-ui-projects-dialog-button">'+RED._("common.label.cancel")+'</button>')
|
||||
.appendTo(cover)
|
||||
.on("click", function(e) {
|
||||
e.stopPropagation();
|
||||
cover.remove();
|
||||
done(true);
|
||||
});
|
||||
$('<button class="red-ui-button primary">'+RED._("common.label.delete")+'</button>')
|
||||
$('<button class="red-ui-button red-ui-projects-dialog-button primary">'+RED._("common.label.delete")+'</button>')
|
||||
.appendTo(cover)
|
||||
.on("click", function(e) {
|
||||
e.stopPropagation();
|
||||
@@ -1808,7 +1813,7 @@ RED.projects = (function() {
|
||||
header.addClass("selectable");
|
||||
|
||||
var tools = $('<div class="red-ui-projects-dialog-project-list-entry-tools"></div>').appendTo(header);
|
||||
$('<button class="red-ui-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
|
||||
$('<button class="red-ui-button red-ui-projects-dialog-button red-ui-button-small" style="float: right;"><i class="fa fa-trash"></i></button>')
|
||||
.appendTo(tools)
|
||||
.on("click", function(e) {
|
||||
e.stopPropagation();
|
||||
@@ -1962,7 +1967,8 @@ RED.projects = (function() {
|
||||
var isSSH = false;
|
||||
if (/^https?:\/\//.test(url)) {
|
||||
$('<div class="form-row"><label for="projects-user-auth-username">'+RED._("projects.send-req.username")+'</label><input id="projects-user-auth-username" type="text"></input></div>'+
|
||||
'<div class="form-row"><label for=projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
|
||||
'<div class="form-row"><label for="projects-user-auth-password">'+RED._("projects.send-req.password")+'</label><input id="projects-user-auth-password" type="password"></input></div>').appendTo(message);
|
||||
message.find("#projects-user-auth-password").typedInput({type:"cred"})
|
||||
} else if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(url)) {
|
||||
isSSH = true;
|
||||
var row = $('<div class="form-row"></div>').appendTo(message);
|
||||
@@ -1980,7 +1986,7 @@ RED.projects = (function() {
|
||||
});
|
||||
row = $('<div class="form-row"></div>').appendTo(message);
|
||||
$('<label for="projects-user-auth-passphrase">'+RED._("projects.send-req.passphrase")+'</label>').appendTo(row);
|
||||
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row);
|
||||
$('<input id="projects-user-auth-passphrase" type="password"></input>').appendTo(row).typedInput({type:"cred"});
|
||||
}
|
||||
|
||||
var notification = RED.notify(message,{
|
||||
@@ -2263,6 +2269,12 @@ RED.projects = (function() {
|
||||
autoOpen: false,
|
||||
width: 600,
|
||||
resizable: false,
|
||||
open: function(e) {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
},
|
||||
classes: {
|
||||
"ui-dialog": "red-ui-editor-dialog",
|
||||
"ui-dialog-titlebar-close": "hide",
|
||||
|
||||
@@ -293,14 +293,20 @@ RED.sidebar.versionControl = (function() {
|
||||
if (activeProject) {
|
||||
// TODO: this is a full refresh of the files - should be able to
|
||||
// just do an incremental refresh
|
||||
allChanges = {};
|
||||
unstagedChangesList.editableList('empty');
|
||||
stagedChangesList.editableList('empty');
|
||||
unmergedChangesList.editableList('empty');
|
||||
|
||||
$.getJSON("projects/"+activeProject.name+"/status",function(result) {
|
||||
refreshFiles(result);
|
||||
});
|
||||
var workflowMode = ((RED.settings.get('git') || {}).workflow || {}).mode || "manual";
|
||||
if (workflowMode === 'auto') {
|
||||
refresh(true);
|
||||
} else {
|
||||
allChanges = {};
|
||||
unstagedChangesList.editableList('empty');
|
||||
stagedChangesList.editableList('empty');
|
||||
unmergedChangesList.editableList('empty');
|
||||
|
||||
$.getJSON("projects/"+activeProject.name+"/status",function(result) {
|
||||
refreshFiles(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
RED.events.on("login",function() {
|
||||
|
||||
@@ -232,7 +232,11 @@ RED.sidebar = (function() {
|
||||
}
|
||||
},
|
||||
// minimumActiveTabWidth: 70,
|
||||
collapsible: true
|
||||
collapsible: true,
|
||||
onreorder: function(order) {
|
||||
RED.settings.set("editor.sidebar.order",order);
|
||||
},
|
||||
order: RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])
|
||||
// scrollable: true
|
||||
});
|
||||
|
||||
|
||||
@@ -453,14 +453,16 @@ RED.subflow = (function() {
|
||||
$("#red-ui-workspace-chart").css({"margin-top": "0"});
|
||||
}
|
||||
|
||||
function removeSubflow(id) {
|
||||
function removeSubflow(id, keepInstanceNodes) {
|
||||
// TODO: A lot of this logic is common with RED.nodes.removeWorkspace
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var removedGroups = [];
|
||||
|
||||
var activeSubflow = RED.nodes.subflow(id);
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+id) {
|
||||
if (!keepInstanceNodes && n.type == "subflow:"+id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
if (n.z == id) {
|
||||
@@ -472,7 +474,9 @@ RED.subflow = (function() {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
RED.nodes.groups(id).forEach(function(n) {
|
||||
removedGroups.push(n);
|
||||
})
|
||||
var removedConfigNodes = [];
|
||||
for (var i=0;i<removedNodes.length;i++) {
|
||||
var removedEntities = RED.nodes.remove(removedNodes[i].id);
|
||||
@@ -482,6 +486,18 @@ RED.subflow = (function() {
|
||||
// TODO: this whole delete logic should be in RED.nodes.removeSubflow..
|
||||
removedNodes = removedNodes.concat(removedConfigNodes);
|
||||
|
||||
removedGroups = RED.nodes.groups(id).filter(function(g) { return !g.g; });
|
||||
for (i=0;i<removedGroups.length;i++) {
|
||||
removedGroups[i].nodes.forEach(function(n) {
|
||||
if (n.type === "group") {
|
||||
removedGroups.push(n);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Now remove them in the reverse order
|
||||
for (i=removedGroups.length-1; i>=0; i--) {
|
||||
RED.nodes.removeGroup(removedGroups[i]);
|
||||
}
|
||||
RED.nodes.removeSubflow(activeSubflow);
|
||||
RED.workspaces.remove(activeSubflow);
|
||||
RED.nodes.dirty(true);
|
||||
@@ -490,6 +506,7 @@ RED.subflow = (function() {
|
||||
return {
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
groups: removedGroups,
|
||||
subflows: [activeSubflow]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,10 +261,12 @@ RED.sidebar.help = (function() {
|
||||
|
||||
}
|
||||
|
||||
function show(type) {
|
||||
RED.sidebar.show("help");
|
||||
function show(type, bringToFront) {
|
||||
if (bringToFront !== false) {
|
||||
RED.sidebar.show("help");
|
||||
}
|
||||
if (type) {
|
||||
hideTOC();
|
||||
// hideTOC();
|
||||
showHelp(type);
|
||||
}
|
||||
resizeStack();
|
||||
|
||||
@@ -79,7 +79,7 @@ RED.sidebar.info.outliner = (function() {
|
||||
try {
|
||||
label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||"";
|
||||
} catch(err) {
|
||||
console.log("Definition error: "+type+".label",err);
|
||||
console.log("Definition error: "+n.type+".label",err);
|
||||
}
|
||||
}
|
||||
var newlineIndex = label.indexOf("\\n");
|
||||
|
||||
@@ -112,14 +112,14 @@ RED.touch.radialMenu = (function() {
|
||||
if (!opt.disabled) {
|
||||
if (p[0]>opt.x-30 && p[0]<opt.x+30 && p[1]>opt.y-30 && p[1]<opt.y+30) {
|
||||
if (opt !== activeOption) {
|
||||
opt.el.style("background","#999");
|
||||
opt.el.classed("selected",true);
|
||||
activeOption = opt;
|
||||
}
|
||||
} else if (opt === activeOption) {
|
||||
opt.el.style("background","#fff");
|
||||
activeOption = null;
|
||||
} else {
|
||||
opt.el.style("background","#fff");
|
||||
if (opt === activeOption) {
|
||||
activeOption = null;
|
||||
}
|
||||
opt.el.classed("selected",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,15 @@ RED.utils = (function() {
|
||||
} else if (value === null) {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">null</span>');
|
||||
} else if (typeof value === 'object') {
|
||||
if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
|
||||
if (value.hasOwnProperty('type') && value.type === 'undefined') {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-null">undefined</span>');
|
||||
} else if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('buffer['+value.length+']');
|
||||
} else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('array['+value.length+']');
|
||||
} else if (value.hasOwnProperty('type') && value.type === 'function') {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta"></span>').text('function');
|
||||
} else if (value.hasOwnProperty('type') && value.type === 'number') {
|
||||
} else if (value.hasOwnProperty('type') && (value.type === 'number' || value.type === 'bigint')) {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-number"></span>').text(value.data);
|
||||
} else {
|
||||
result = $('<span class="red-ui-debug-msg-object-value red-ui-debug-msg-type-meta">object</span>');
|
||||
@@ -348,7 +350,9 @@ RED.utils = (function() {
|
||||
}
|
||||
if (obj === null || obj === undefined) {
|
||||
$('<span class="red-ui-debug-msg-type-null">'+obj+'</span>').appendTo(entryObj);
|
||||
} else if (obj.__enc__ && obj.type === 'number') {
|
||||
} else if (obj.__enc__ && obj.type === 'undefined') {
|
||||
$('<span class="red-ui-debug-msg-type-null">undefined</span>').appendTo(entryObj);
|
||||
} else if (obj.__enc__ && (obj.type === 'number' || obj.type === 'bigint')) {
|
||||
e = $('<span class="red-ui-debug-msg-type-number red-ui-debug-msg-object-header"></span>').text(obj.data).appendTo(entryObj);
|
||||
} else if (typeHint === "function" || (obj.__enc__ && obj.type === 'function')) {
|
||||
e = $('<span class="red-ui-debug-msg-type-meta red-ui-debug-msg-object-header"></span>').text("function").appendTo(entryObj);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,11 @@ RED.user = (function() {
|
||||
closeOnEscape: !!opts.cancelable,
|
||||
width: 600,
|
||||
resizable: false,
|
||||
draggable: false
|
||||
draggable: false,
|
||||
close: function( event, ui ) {
|
||||
$("#node-dialog-login").dialog('destroy').remove();
|
||||
RED.keyboard.enable()
|
||||
}
|
||||
});
|
||||
|
||||
$("#node-dialog-login-fields").empty();
|
||||
@@ -98,10 +102,10 @@ RED.user = (function() {
|
||||
data: body
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.settings.set("auth-tokens",data);
|
||||
$("#node-dialog-login").dialog('destroy').remove();
|
||||
if (opts.updateMenu) {
|
||||
updateUserMenu();
|
||||
}
|
||||
$("#node-dialog-login").dialog("close");
|
||||
done();
|
||||
}).fail(function(jqXHR,textStatus,errorThrown) {
|
||||
RED.settings.remove("auth-tokens");
|
||||
@@ -143,7 +147,8 @@ RED.user = (function() {
|
||||
}
|
||||
if (opts.cancelable) {
|
||||
$("#node-dialog-login-cancel").button().on("click", function( event ) {
|
||||
$("#node-dialog-login").dialog('destroy').remove();
|
||||
$("#node-dialog-login").dialog('close');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,8 +157,7 @@ RED.user = (function() {
|
||||
$("#node-dialog-login-image").load(function() {
|
||||
dialog.dialog("open");
|
||||
}).attr("src",loginImageSrc);
|
||||
|
||||
|
||||
RED.keyboard.disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -215,7 +219,7 @@ RED.user = (function() {
|
||||
|
||||
function init() {
|
||||
if (RED.settings.user) {
|
||||
if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu")) {
|
||||
if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu") || RED.settings.editorTheme.userMenu) {
|
||||
|
||||
var userMenu = $('<li><a id="red-ui-header-button-user" class="button hide" href="#"></a></li>')
|
||||
.prependTo(".red-ui-header-toolbar");
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
**/
|
||||
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.red-ui-editor {
|
||||
font-size: $primary-font-size;
|
||||
|
||||
@@ -289,3 +289,4 @@ $group-default-fill: none;
|
||||
$group-default-fill-opacity: 1;
|
||||
$group-default-stroke: #999;
|
||||
$group-default-stroke-opacity: 1;
|
||||
$group-default-label-color: #a4a4a4;
|
||||
@@ -597,6 +597,7 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle {
|
||||
padding: 4px;
|
||||
color: $secondary-text-color;
|
||||
font-size: 0.9em;
|
||||
line-height: 24px;
|
||||
}
|
||||
button {
|
||||
float: right;
|
||||
|
||||
@@ -106,6 +106,11 @@
|
||||
pointer-events: stroke;
|
||||
stroke-opacity: 0;
|
||||
stroke-width: 3;
|
||||
|
||||
&.red-ui-flow-group-outline-select-background {
|
||||
stroke: $view-background;
|
||||
stroke-width: 6;
|
||||
}
|
||||
}
|
||||
.red-ui-flow-group-body {
|
||||
pointer-events: none;
|
||||
@@ -117,6 +122,7 @@
|
||||
}
|
||||
.red-ui-flow-group-label {
|
||||
@include disable-selection;
|
||||
fill: $group-default-label-color;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +140,7 @@
|
||||
}
|
||||
.red-ui-flow-node-icon-group {
|
||||
.fa-lg {
|
||||
@include disable-selection;
|
||||
stroke: none;
|
||||
fill: $node-icon-color;
|
||||
text-anchor: middle;
|
||||
@@ -244,7 +251,7 @@ g.red-ui-flow-node-selected {
|
||||
stroke-dasharray: none;
|
||||
}
|
||||
}
|
||||
@each $current-color in red green yellow blue grey {
|
||||
@each $current-color in red green yellow blue grey gray {
|
||||
.red-ui-flow-node-status-dot-#{$current-color} {
|
||||
fill: map-get($node-status-colors,$current-color);
|
||||
stroke: map-get($node-status-colors,$current-color);
|
||||
@@ -336,7 +343,10 @@ g.red-ui-flow-link-unknown path.red-ui-flow-link-line {
|
||||
stroke-dasharray: 10, 4;
|
||||
}
|
||||
|
||||
@keyframes red-ui-flow-port-tooltip-fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||
// @keyframes *must* be on multiple lines so build-custom-theme can filter them out
|
||||
@keyframes red-ui-flow-port-tooltip-fadeIn {
|
||||
from { opacity:0; } to { opacity:1; }
|
||||
}
|
||||
|
||||
.red-ui-flow-port-tooltip {
|
||||
opacity:0;
|
||||
|
||||
@@ -153,3 +153,61 @@
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.red-ui-clipboard-dialog-import-conflicts-list-container {
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
|
||||
li:not(:first-child) .red-ui-clipboard-dialog-import-conflicts-item-header {
|
||||
// border-top: 1px solid $secondary-border-color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.red-ui-clipboard-dialog-import-conflicts-item-header {
|
||||
background: $tertiary-background;
|
||||
& > span:first-child {
|
||||
color: $header-text-color;
|
||||
padding-left: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.red-ui-clipboard-dialog-import-conflicts-controls {
|
||||
position: absolute;
|
||||
top:0;
|
||||
bottom: 0;
|
||||
right: 0px;
|
||||
text-align: center;
|
||||
color: $form-text-color;
|
||||
.form-row & label {
|
||||
padding: 2px 0;
|
||||
line-height: 23px;
|
||||
margin-bottom: 0;
|
||||
width: 80px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
border-left: 1px solid $secondary-border-color;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
#red-ui-clipboard-dialog-import-conflicts-list .disabled .red-ui-info-outline-item {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.form-row label.red-ui-clipboard-dialog-import-conflicts-gutter {
|
||||
box-sizing: border-box;
|
||||
width: 22px;
|
||||
text-align: center;
|
||||
.red-ui-editor-dialog & input[type="checkbox"] {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
@@ -71,38 +71,9 @@
|
||||
}
|
||||
|
||||
.red-ui-notification-shake-horizontal {
|
||||
-webkit-animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
|
||||
animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes red-ui-notification-shake-horizontal {
|
||||
0%,
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
10%,
|
||||
30%,
|
||||
50%,
|
||||
70% {
|
||||
-webkit-transform: translateX(-1px);
|
||||
transform: translateX(-1px);
|
||||
}
|
||||
20%,
|
||||
40%,
|
||||
60% {
|
||||
-webkit-transform: translateX(1px);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
// 80% {
|
||||
// -webkit-transform: translateX(1px);
|
||||
// transform: translateX(1px);
|
||||
// }
|
||||
// 90% {
|
||||
// -webkit-transform: translateX(-1px);
|
||||
// transform: translateX(-1px);
|
||||
// }
|
||||
animation: red-ui-notification-shake-horizontal 0.3s steps(2, end) both;
|
||||
}
|
||||
// @keyframes *must* be on multiple lines so build-custom-theme can filter them out
|
||||
@keyframes red-ui-notification-shake-horizontal {
|
||||
0%,
|
||||
100% {
|
||||
@@ -122,12 +93,4 @@
|
||||
-webkit-transform: translateX(1px);
|
||||
transform: translateX(1px);
|
||||
}
|
||||
// 80% {
|
||||
// -webkit-transform: translateX(1px);
|
||||
// transform: translateX(1px);
|
||||
// }
|
||||
// 90% {
|
||||
// -webkit-transform: translateX(-1px);
|
||||
// transform: translateX(-1px);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -237,3 +237,45 @@ ul.red-ui-palette-module-error-list {
|
||||
#red-ui-palette-module-install-shade {
|
||||
padding-top: 80px;
|
||||
}
|
||||
button.red-ui-palette-editor-upload-button {
|
||||
padding: 0;
|
||||
height: 25px;
|
||||
margin-top: -1px;
|
||||
|
||||
input[type="file"] {
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
.red-ui-settings-tabs-content & label {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
form {
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
.red-ui-palette-editor-upload {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 44px;
|
||||
padding: 20px;
|
||||
background: $secondary-background;
|
||||
border-bottom: 1px $secondary-border-color solid;
|
||||
box-shadow: 1px 1px 4px $shadow;
|
||||
|
||||
.placeholder-input {
|
||||
width: calc(100% - 180px);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.red-ui-palette-editor-upload-buttons {
|
||||
float: right;
|
||||
button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
.red-ui-palette-icon-fa {
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
top: calc(50% - 7px);
|
||||
left: 3px;
|
||||
}
|
||||
.red-ui-palette-node-small {
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
button.red-ui-button {
|
||||
button.red-ui-button.red-ui-projects-dialog-button {
|
||||
width: calc(50% - 80px);
|
||||
margin: 20px;
|
||||
height: auto;
|
||||
|
||||
@@ -42,7 +42,11 @@
|
||||
background: $secondary-background;
|
||||
border: 2px solid $primary-border-color;
|
||||
text-align: center;
|
||||
line-height:50px
|
||||
line-height:50px;
|
||||
|
||||
&.selected {
|
||||
background: $workspace-button-background-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.red-ui-editor-radial-menu-opt-disabled {
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
button.red-ui-toggleButton.toggle {
|
||||
text-align: center;
|
||||
i {
|
||||
min-width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.red-ui-sidebar-context-property {
|
||||
|
||||
@@ -321,15 +321,12 @@ div.red-ui-info-table {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
.red-ui-treeList-label {
|
||||
font-size: 13px;
|
||||
padding: 2px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.red-ui-info-outline-project {
|
||||
border-bottom: 1px solid $secondary-border-color;
|
||||
}
|
||||
|
||||
}
|
||||
.red-ui-info-outline,.red-ui-sidebar-help-toc, #red-ui-clipboard-dialog-import-conflicts-list {
|
||||
.red-ui-info-outline-item {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
@@ -366,6 +363,12 @@ div.red-ui-info-table {
|
||||
}
|
||||
}
|
||||
|
||||
.red-ui-treeList-label {
|
||||
font-size: 13px;
|
||||
padding: 2px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.red-ui-search-result-node {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
@@ -387,6 +390,7 @@ div.red-ui-info-table {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.red-ui-info-outline-item-control-spacer {
|
||||
display: inline-block;
|
||||
width: 23px;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
color: $secondary-text-color;
|
||||
cursor: pointer;
|
||||
input {
|
||||
display:none;
|
||||
display:none !important;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
border: 1px solid $form-input-border-color;
|
||||
border-radius: 4px;
|
||||
height: 34px;
|
||||
line-height: 14px;
|
||||
display: inline-flex;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -25,6 +26,14 @@
|
||||
box-sizing: border-box;
|
||||
overflow:visible;
|
||||
position: relative;
|
||||
&[disabled] {
|
||||
input, button {
|
||||
background: $secondary-background-inactive;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.red-ui-typedInput-input-wrap {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
.uneditable-input, input, textarea {
|
||||
.uneditable-input, input[type="text"],input[type="password"], textarea {
|
||||
width: calc(100% - 150px);
|
||||
}
|
||||
textarea {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|then|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|foreign|not|references|default|null|inner|cross|natural|database|drop|grant",t="true|false",n="avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",r="int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|money|real|number|integer",i=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t,"storage.type":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"string",regex:"`.*?`"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql"}.call(o.prototype),t.Mode=o}); (function() {
|
||||
ace.define("ace/mode/sql_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){var e="select|insert|update|delete|from|where|and|or|group|by|order|limit|offset|having|as|case|when|then|else|end|type|left|right|join|on|outer|desc|asc|union|create|table|primary|key|if|foreign|not|references|default|null|inner|cross|natural|database|drop|grant",t="true|false",n="avg|count|first|last|max|min|sum|ucase|lcase|mid|len|round|rank|now|format|coalesce|ifnull|isnull|nvl",r="int|numeric|decimal|date|varchar|char|bigint|float|double|bit|binary|text|set|timestamp|money|real|number|integer",i=this.createKeywordMapper({"support.function":n,keyword:e,"constant.language":t,"storage.type":r},"identifier",!0);this.$rules={start:[{token:"comment",regex:"--.*$"},{token:"comment",start:"/\\*",end:"\\*/"},{token:"string",regex:'".*?"'},{token:"string",regex:"'.*?'"},{token:"string",regex:"`.*?`"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:i,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|="},{token:"paren.lparen",regex:"[\\(]"},{token:"paren.rparen",regex:"[\\)]"},{token:"text",regex:"\\s+"}]},this.normalizeRules()};r.inherits(s,i),t.SqlHighlightRules=s}),ace.define("ace/mode/sql",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sql_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./sql_highlight_rules").SqlHighlightRules,o=function(){this.HighlightRules=s,this.$behaviour=this.$defaultBehaviour};r.inherits(o,i),function(){this.lineCommentStart="--",this.$id="ace/mode/sql",this.snippetFileId="ace/snippets/sql"}.call(o.prototype),t.Mode=o}); (function() {
|
||||
ace.require(["ace/mode/sql"], function(m) {
|
||||
if (typeof module == "object" && typeof exports == "object" && module) {
|
||||
module.exports = m;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d\s]*$/,onMatch:function(e,t,n,r){var i=/^\s*/.exec(r)[0];return n.length<1?n.push(this.next):n[0]="mlString",n.length<2?n.push(i.length):n[1]=i.length,this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlString:[{token:"indent",regex:/^\s*$/},{token:"indent",regex:/^\s*/,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart=["#"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/yaml"}.call(a.prototype),t.Mode=a}); (function() {
|
||||
ace.define("ace/mode/yaml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment",regex:"#.*$"},{token:"list.markup",regex:/^(?:-{3}|\.{3})\s*(?=#|$)/},{token:"list.markup",regex:/^\s*[\-?](?:$|\s)/},{token:"constant",regex:"!![\\w//]+"},{token:"constant.language",regex:"[&\\*][a-zA-Z0-9-_]+"},{token:["meta.tag","keyword"],regex:/^(\s*\w.*?)(:(?=\s|$))/},{token:["meta.tag","keyword"],regex:/(\w+?)(\s*:(?=\s|$))/},{token:"keyword.operator",regex:"<<\\w*:\\w*"},{token:"keyword.operator",regex:"-\\s*(?=[{])"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:/[|>][-+\d]*(?:$|\s+(?:$|#))/,onMatch:function(e,t,n,r){r=r.replace(/ #.*/,"");var i=/^ *((:\s*)?-(\s*[^|>])?)?/.exec(r)[0].replace(/\S\s*$/,"").length,s=parseInt(/\d+[\s+-]*$/.exec(r));return s?(i+=s-1,this.next="mlString"):this.next="mlStringPre",n.length?(n[0]=this.next,n[1]=i):(n.push(this.next),n.push(i)),this.token},next:"mlString"},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:"constant.numeric",regex:/(\b|[+\-\.])[\d_]+(?:(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)(?=[^\d-\w]|$)/},{token:"constant.numeric",regex:/[+\-]?\.inf\b|NaN\b|0x[\dA-Fa-f_]+|0b[10_]+/},{token:"constant.language.boolean",regex:"\\b(?:true|false|TRUE|FALSE|True|False|yes|no)\\b"},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:/[^\s,:\[\]\{\}]+/}],mlStringPre:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.shift(),n.shift()):(n[1]=e.length-1,this.next=n[0]="mlString"),this.token},next:"mlString"},{defaultToken:"string"}],mlString:[{token:"indent",regex:/^ *$/},{token:"indent",regex:/^ */,onMatch:function(e,t,n){var r=n[1];return r>=e.length?(this.next="start",n.splice(0)):this.next="mlString",this.token},next:"mlString"},{token:"string",regex:".+"}]},this.normalizeRules()};r.inherits(s,i),t.YamlHighlightRules=s}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/folding/coffee",["require","exports","module","ace/lib/oop","ace/mode/folding/fold_mode","ace/range"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("./fold_mode").FoldMode,s=e("../../range").Range,o=t.FoldMode=function(){};r.inherits(o,i),function(){this.getFoldWidgetRange=function(e,t,n){var r=this.indentationBlock(e,n);if(r)return r;var i=/\S/,o=e.getLine(n),u=o.search(i);if(u==-1||o[u]!="#")return;var a=o.length,f=e.getLength(),l=n,c=n;while(++n<f){o=e.getLine(n);var h=o.search(i);if(h==-1)continue;if(o[h]!="#")break;c=n}if(c>l){var p=e.getLine(c).length;return new s(l,a,c,p)}},this.getFoldWidget=function(e,t,n){var r=e.getLine(n),i=r.search(/\S/),s=e.getLine(n+1),o=e.getLine(n-1),u=o.search(/\S/),a=s.search(/\S/);if(i==-1)return e.foldWidgets[n-1]=u!=-1&&u<a?"start":"","";if(u==-1){if(i==a&&r[i]=="#"&&s[i]=="#")return e.foldWidgets[n-1]="",e.foldWidgets[n+1]="","start"}else if(u==i&&r[i]=="#"&&o[i]=="#"&&e.getLine(n-2).search(/\S/)==-1)return e.foldWidgets[n-1]="start",e.foldWidgets[n+1]="","";return u!=-1&&u<i?e.foldWidgets[n-1]="start":e.foldWidgets[n-1]="",i<a?"start":""}}.call(o.prototype)}),ace.define("ace/mode/yaml",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/yaml_highlight_rules","ace/mode/matching_brace_outdent","ace/mode/folding/coffee"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./yaml_highlight_rules").YamlHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("./folding/coffee").FoldMode,a=function(){this.HighlightRules=s,this.$outdent=new o,this.foldingRules=new u,this.$behaviour=this.$defaultBehaviour};r.inherits(a,i),function(){this.lineCommentStart=["#"],this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t);if(e=="start"){var i=t.match(/^.*[\{\(\[]\s*$/);i&&(r+=n)}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.$id="ace/mode/yaml"}.call(a.prototype),t.Mode=a}); (function() {
|
||||
ace.require(["ace/mode/yaml"], function(m) {
|
||||
if (typeof module == "object" && typeof exports == "object" && module) {
|
||||
module.exports = m;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ace.define("ace/snippets/handlebars",["require","exports","module"],function(e,t,n){"use strict";t.snippetText=undefined,t.scope="handlebars"}); (function() {
|
||||
; (function() {
|
||||
ace.require(["ace/snippets/handlebars"], function(m) {
|
||||
if (typeof module == "object" && typeof exports == "object" && module) {
|
||||
module.exports = m;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user