Compare commits
No commits in common. "master" and "0.19.5" have entirely different histories.
1
.gitattributes
vendored
@ -1 +0,0 @@
|
||||
/packages/node_modules/** linguist-generated=false
|
34
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
## Before you hit that Submit button....
|
||||
|
||||
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
|
||||
|
||||
If your issue is:
|
||||
- a general 'how-to' type question,
|
||||
- a feature request or suggestion for a change,
|
||||
- or problems with 3rd party (`node-red-contrib-`) nodes
|
||||
|
||||
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
|
||||
|
||||
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
|
||||
|
||||
That way the whole Node-RED user community can help, rather than rely on the core development team.
|
||||
|
||||
## So you have a real issue to raise...
|
||||
|
||||
To help us understand the issue, please fill-in as much of the following information as you can:
|
||||
-->
|
||||
|
||||
### What are the steps to reproduce?
|
||||
|
||||
### What happens?
|
||||
|
||||
### What do you expect to happen?
|
||||
|
||||
### Please tell us about your environment:
|
||||
|
||||
- [ ] Node-RED version:
|
||||
- [ ] node.js version:
|
||||
- [ ] npm version:
|
||||
- [ ] Platform/OS:
|
||||
- [ ] Browser:
|
61
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,61 +0,0 @@
|
||||
name: 🐞 Report a bug
|
||||
description: File a bug/issue on the core of Node-RED
|
||||
labels: [needs-triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
|
||||
|
||||
If your issue is:
|
||||
- a general 'how-to' type question,
|
||||
- a feature request or suggestion for a change,
|
||||
- or problems with 3rd party (`node-red-contrib-`) nodes
|
||||
|
||||
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
|
||||
|
||||
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
|
||||
|
||||
That way the whole Node-RED user community can help, rather than rely on the core development team.
|
||||
|
||||
To help us understand the issue, please fill-in as much of the following information as you can:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A clear & concise description of what you're experiencing.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear & concise description of what you expected to happen.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Example flow
|
||||
description: If you have a minimal example flow that demonstrates the issue, share it here.
|
||||
value: |
|
||||
```
|
||||
paste your flow here
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Please tell us about your environment. Include any relevant information on how you are running Node-RED.
|
||||
value: |
|
||||
- Node-RED version:
|
||||
- Node.js version:
|
||||
- npm version:
|
||||
- Platform/OS:
|
||||
- Browser:
|
||||
validations:
|
||||
required: false
|
14
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,14 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: ❓ Questions
|
||||
url: https://discourse.nodered.org
|
||||
about: Ask your question on the Node-RED forum
|
||||
- name: ⭐️ Feature Request
|
||||
url: https://discourse.nodered.org/c/development/feature-requests
|
||||
about: Discuss your request with the community
|
||||
- name: 🗂 Documentation
|
||||
url: https://nodered.org/docs
|
||||
about: Go straight to the documentation
|
||||
- name: 💬 Slack
|
||||
url: https://nodered.org/slack
|
||||
about: Chat about the project on our slack team
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -29,6 +29,6 @@ the [forum](https://discourse.nodered.org) or
|
||||
<!-- Put an `x` in the boxes that apply -->
|
||||
|
||||
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
|
||||
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
|
||||
- [ ] I have run `npm run test` to verify the unit tests pass
|
||||
- [ ] For non-bugfix PRs, I have discussed this change on the mailing list/slack team.
|
||||
- [ ] I have run `grunt` to verify the unit tests pass
|
||||
- [ ] I have added suitable unit tests to cover the new/changed functionality
|
||||
|
15
.github/dependabot.yml
vendored
@ -1,15 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
29
.github/scripts/update-node-red-docker.js
vendored
@ -1,29 +0,0 @@
|
||||
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
@ -1,18 +0,0 @@
|
||||
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);
|
62
.github/workflows/release.yml
vendored
@ -1,62 +0,0 @@
|
||||
name: Publish Release
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
name: 'Update node-red-docker image'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out node-red repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'node-red'
|
||||
- name: Check out node-red-docker repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'node-red/node-red-docker'
|
||||
path: 'node-red-docker'
|
||||
- name: Check out node-red.github.io repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'node-red/node-red.github.io'
|
||||
path: 'node-red.github.io'
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: node ./node-red/.github/scripts/update-node-red-docker.js
|
||||
- name: Create Docker Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
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@v6
|
||||
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
|
30
.github/workflows/tests.yml
vendored
@ -1,30 +0,0 @@
|
||||
name: Run tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18, 20, 22]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run test
|
12
.gitignore
vendored
@ -7,9 +7,7 @@
|
||||
.sessions.json
|
||||
.settings
|
||||
.tern-project
|
||||
.i18n-editor-metadata
|
||||
*.backup
|
||||
*.bak
|
||||
*_cred*
|
||||
coverage
|
||||
credentials.json
|
||||
@ -19,13 +17,3 @@ node_modules
|
||||
public
|
||||
locales/zz-ZZ
|
||||
nodes/core/locales/zz-ZZ
|
||||
!packages/node_modules
|
||||
packages/node_modules/@node-red/editor-client/public
|
||||
!test/**/node_modules
|
||||
docs
|
||||
!packages/node_modules/**/docs
|
||||
.vscode
|
||||
.nyc_output
|
||||
sync.ffs_db
|
||||
package-lock.json
|
||||
.editorconfig
|
||||
|
@ -15,5 +15,5 @@
|
||||
"shadow": true, // allow variable shadowing (re-use of names...)
|
||||
"sub": true, // don't warn that foo['bar'] should be written as foo.bar
|
||||
"proto": true, // allow setting of __proto__ in node < v0.12,
|
||||
"esversion": 11 // allow es11(ES2020)
|
||||
"esversion": 6 // allow es6
|
||||
}
|
||||
|
5
.nodemonignore
Normal file
@ -0,0 +1,5 @@
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
7
.npmignore
Normal file
@ -0,0 +1,7 @@
|
||||
.settings
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.project
|
||||
.tern-project
|
||||
.travis.yml
|
||||
.git
|
12
.travis.yml
Normal file
@ -0,0 +1,12 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "10"
|
||||
script:
|
||||
- ./node_modules/.bin/grunt && istanbul report text && ( cat coverage/lcov.info | $(npm get prefix)/bin/coveralls || true ) && rm -rf coverage
|
||||
before_script:
|
||||
- npm install -g istanbul coveralls
|
||||
- node_js: "8"
|
||||
- node_js: "6"
|
||||
- node_js: "4"
|
19
API.md
@ -1,19 +0,0 @@
|
||||
Node-RED consists of 6 node modules under the `@node-red` scope, which are pulled together
|
||||
by the top-level `node-red` module. The typical scenario is where you are embedding Node-RED into your
|
||||
own application, in which case you would use the `node-red` module rather than any of the
|
||||
internal modules directly.
|
||||
|
||||
```javascript
|
||||
let RED = require("node-red");
|
||||
```
|
||||
|
||||
|
||||
Module | Description
|
||||
-------|-------
|
||||
[node-red](node-red.html) | the main module that pulls together all of the internal modules and provides the executable version of Node-RED
|
||||
[@node-red/editor-api](@node-red_editor-api.html) | an Express application that serves the Node-RED editor and provides the Admin HTTP API
|
||||
[@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED
|
||||
[@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules
|
||||
[@node-red/registry](@node-red_registry.html) | the internal node registry
|
||||
@node-red/nodes | the default set of core nodes. This module only contains the Node-RED nodes - it does not expose any APIs.
|
||||
@node-red/editor-client | the client-side resources of the Node-RED editor application
|
1549
CHANGELOG.md
@ -1,7 +0,0 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
title: "Node-RED"
|
||||
authors:
|
||||
- family-names: "OpenJS Foundation"
|
||||
- family-names: "Contributors"
|
||||
url: "https://nodered.org"
|
@ -16,9 +16,6 @@ behavior to the project's core team at team@nodered.org.
|
||||
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
||||
search the list to see if your issue has already been raised.
|
||||
|
||||
If your issue is more of a question on how to do something with Node-RED, please
|
||||
consider using the [community forum](https://discourse.nodered.org/).
|
||||
|
||||
A good bug report is one that make it easy for us to understand what you were
|
||||
trying to do and what went wrong.
|
||||
|
||||
@ -29,7 +26,7 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog.
|
||||
At a minimum, please include:
|
||||
|
||||
- Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly.
|
||||
- Version of Node.js - what does `node -v` say?
|
||||
- Version of node.js - what does `node -v` say?
|
||||
|
||||
## Feature requests
|
||||
|
||||
@ -38,25 +35,16 @@ For feature requests, please raise them on the [forum](https://discourse.nodered
|
||||
## Pull-Requests
|
||||
|
||||
If you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, please come and discuss it with us first. We prefer to
|
||||
do it that way to make sure your time and effort is well spent on something
|
||||
that fits with our goals.
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [forum](https://discourse.nodered.org) first.
|
||||
|
||||
If you've got a bug-fix or similar for us, then you are most welcome to
|
||||
get it raised - just make sure you link back to the issue it's fixing and
|
||||
try to include some tests!
|
||||
All contributors need to sign the JS Foundation's Contributor License Agreement.
|
||||
It is an online process and quick to do. You can read the details of the agreement
|
||||
here: https://cla.js.foundation/node-red/node-red.
|
||||
|
||||
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
|
||||
It is an online process and quick to do. If you raise a pull-request without
|
||||
having signed the CLA, you will be prompted to do so automatically.
|
||||
If you raise a pull-request without having signed the CLA, you will be prompted
|
||||
to do so automatically.
|
||||
|
||||
### Code Branches
|
||||
|
||||
When raising a PR for a fix or a new feature, it is important to target the right branch.
|
||||
|
||||
- `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch.
|
||||
- `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream.
|
||||
- `dev` - this is the branch for new feature development targeting the next milestone release.
|
||||
|
||||
### Coding standards
|
||||
|
||||
|
607
Gruntfile.js
@ -15,35 +15,21 @@
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs-extra");
|
||||
var sass = require("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');
|
||||
if (browserstack) {
|
||||
process.env.BROWSERSTACK = true;
|
||||
}
|
||||
var nonHeadless = grunt.option('non-headless');
|
||||
if (nonHeadless) {
|
||||
process.env.NODE_RED_NON_HEADLESS = true;
|
||||
process.env.NODE_RED_NON_HEADLESS = 'true';
|
||||
}
|
||||
const pkg = grunt.file.readJSON('package.json');
|
||||
process.env.NODE_RED_PACKAGE_VERSION = pkg.version;
|
||||
grunt.initConfig({
|
||||
pkg: pkg,
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
dist: ".dist"
|
||||
},
|
||||
@ -55,8 +41,8 @@ module.exports = function(grunt) {
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
|
||||
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
webdriver: {
|
||||
@ -64,19 +50,18 @@ module.exports = function(grunt) {
|
||||
configFile: 'test/editor/wdio.conf.js'
|
||||
}
|
||||
},
|
||||
nyc: {
|
||||
mocha_istanbul: {
|
||||
options: {
|
||||
cwd: '.',
|
||||
include: ['packages/node_modules/**'],
|
||||
excludeNodeModules: false,
|
||||
exclude: ['packages/node_modules/@node-red/editor-client/**'],
|
||||
reporter: ['lcov', 'html','text-summary'],
|
||||
reportDir: 'coverage',
|
||||
all: true
|
||||
globals: ['expect'],
|
||||
timeout: 3000,
|
||||
ignoreLeaks: false,
|
||||
ui: 'bdd',
|
||||
reportFormats: ['lcov','html'],
|
||||
print: 'both'
|
||||
},
|
||||
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'] }
|
||||
all: { src: ["test/_spec.js","test/red/**/*_spec.js","test/nodes/**/*_spec.js"] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
@ -92,20 +77,22 @@ module.exports = function(grunt) {
|
||||
//"loopfunc": true, // allow functions to be defined in loops
|
||||
//"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
// all: [
|
||||
// 'Gruntfile.js',
|
||||
// 'red.js',
|
||||
// 'packages/**/*.js'
|
||||
// ],
|
||||
// core: {
|
||||
// files: {
|
||||
// src: [
|
||||
// 'Gruntfile.js',
|
||||
// 'red.js',
|
||||
// 'packages/**/*.js',
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/core/*/*.js',
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/core/*/*.js' ]
|
||||
@ -113,7 +100,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ]
|
||||
src: [ 'editor/js/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
@ -133,188 +120,145 @@ module.exports = function(grunt) {
|
||||
src: [
|
||||
// Ensure editor source files are concatenated in
|
||||
// the right order
|
||||
"packages/node_modules/@node-red/editor-client/src/js/polyfills.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/red.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/events.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/hooks.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/i18n.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/settings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/user.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/comms.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/runtime.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/multiplayer.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/text/format.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/plugins.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/nodes.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/history.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/search.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/group.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
|
||||
"editor/js/red.js",
|
||||
"editor/js/events.js",
|
||||
"editor/js/i18n.js",
|
||||
"editor/js/settings.js",
|
||||
"editor/js/user.js",
|
||||
"editor/js/comms.js",
|
||||
"editor/js/text/bidi.js",
|
||||
"editor/js/text/format.js",
|
||||
"editor/js/ui/state.js",
|
||||
"editor/js/nodes.js",
|
||||
"editor/js/history.js",
|
||||
"editor/js/validators.js",
|
||||
"editor/js/ui/utils.js",
|
||||
"editor/js/ui/common/editableList.js",
|
||||
"editor/js/ui/common/checkboxSet.js",
|
||||
"editor/js/ui/common/menu.js",
|
||||
"editor/js/ui/common/panels.js",
|
||||
"editor/js/ui/common/popover.js",
|
||||
"editor/js/ui/common/searchBox.js",
|
||||
"editor/js/ui/common/tabs.js",
|
||||
"editor/js/ui/common/stack.js",
|
||||
"editor/js/ui/common/typedInput.js",
|
||||
"editor/js/ui/actions.js",
|
||||
"editor/js/ui/deploy.js",
|
||||
"editor/js/ui/diff.js",
|
||||
"editor/js/ui/keyboard.js",
|
||||
"editor/js/ui/workspaces.js",
|
||||
"editor/js/ui/view.js",
|
||||
"editor/js/ui/view-navigator.js",
|
||||
"editor/js/ui/sidebar.js",
|
||||
"editor/js/ui/palette.js",
|
||||
"editor/js/ui/tab-info.js",
|
||||
"editor/js/ui/tab-config.js",
|
||||
"editor/js/ui/tab-context.js",
|
||||
"editor/js/ui/palette-editor.js",
|
||||
"editor/js/ui/editor.js",
|
||||
"editor/js/ui/editors/*.js",
|
||||
"editor/js/ui/tray.js",
|
||||
"editor/js/ui/clipboard.js",
|
||||
"editor/js/ui/library.js",
|
||||
"editor/js/ui/notifications.js",
|
||||
"editor/js/ui/search.js",
|
||||
"editor/js/ui/typeSearch.js",
|
||||
"editor/js/ui/subflow.js",
|
||||
"editor/js/ui/userSettings.js",
|
||||
"editor/js/ui/projects/projects.js",
|
||||
"editor/js/ui/projects/projectSettings.js",
|
||||
"editor/js/ui/projects/projectUserSettings.js",
|
||||
"editor/js/ui/projects/tab-versionControl.js",
|
||||
"editor/js/ui/touch/radialMenu.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
|
||||
dest: "public/red/red.js"
|
||||
},
|
||||
vendor: {
|
||||
files: [
|
||||
{
|
||||
src: [
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"node_modules/marked/marked.min.js",
|
||||
"node_modules/dompurify/dist/purify.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
|
||||
"node_modules/i18next/i18next.min.js",
|
||||
"node_modules/i18next-http-backend/i18nextHttpBackend.min.js",
|
||||
"node_modules/jquery-i18next/jquery-i18next.min.js",
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js"
|
||||
},
|
||||
// {
|
||||
// src: [
|
||||
// // TODO: resolve relative resource paths in
|
||||
// // bootstrap/FA/jquery
|
||||
// ],
|
||||
// dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css"
|
||||
// },
|
||||
{
|
||||
src: [
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js",
|
||||
},
|
||||
{
|
||||
src: "node_modules/mermaid/dist/mermaid.min.js",
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js",
|
||||
},
|
||||
]
|
||||
files: {
|
||||
"public/vendor/vendor.js": [
|
||||
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
|
||||
"editor/vendor/bootstrap/js/bootstrap.min.js",
|
||||
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
|
||||
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"editor/vendor/marked/marked.min.js",
|
||||
"editor/vendor/d3/d3.v3.min.js",
|
||||
"editor/vendor/i18next/i18next.min.js"
|
||||
],
|
||||
"public/vendor/vendor.css": [
|
||||
// TODO: resolve relative resource paths in
|
||||
// bootstrap/FA/jquery
|
||||
],
|
||||
"public/vendor/jsonata/jsonata.min.js": [
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"editor/vendor/jsonata/formatter.js"
|
||||
],
|
||||
"public/vendor/ace/worker-jsonata.js": [
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"editor/vendor/jsonata/worker-jsonata.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
build: {
|
||||
files: {
|
||||
'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js'
|
||||
'public/red/red.min.js': 'public/red/red.js',
|
||||
'public/red/main.min.js': 'public/red/main.js',
|
||||
'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js',
|
||||
'public/vendor/ace/snippets/jsonata.js': 'editor/vendor/jsonata/snippets-jsonata.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
build: {
|
||||
options: {
|
||||
implementation: sass,
|
||||
outputStyle: 'compressed'
|
||||
},
|
||||
files: [{
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css',
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss'
|
||||
dest: 'public/red/style.min.css',
|
||||
src: 'editor/sass/style.scss'
|
||||
},
|
||||
{
|
||||
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
|
||||
src: 'editor/vendor/bootstrap/css/bootstrap.css'
|
||||
}]
|
||||
}
|
||||
},
|
||||
jsonlint: {
|
||||
messages: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/nodes/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/editor-client/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/runtime/locales/**/*.json'
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
]
|
||||
},
|
||||
keymaps: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
|
||||
'editor/js/keymap.json'
|
||||
]
|
||||
}
|
||||
},
|
||||
attachCopyright: {
|
||||
js: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/public/red/red.min.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/red/main.min.js'
|
||||
'public/red/red.min.js',
|
||||
'public/red/main.min.js'
|
||||
]
|
||||
},
|
||||
css: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/public/red/style.min.css'
|
||||
'public/red/style.min.css'
|
||||
]
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
build: {
|
||||
src: [
|
||||
"packages/node_modules/@node-red/editor-client/public/red",
|
||||
"packages/node_modules/@node-red/editor-client/public/index.html",
|
||||
"packages/node_modules/@node-red/editor-client/public/favicon.ico",
|
||||
"packages/node_modules/@node-red/editor-client/public/icons",
|
||||
"packages/node_modules/@node-red/editor-client/public/vendor",
|
||||
"packages/node_modules/@node-red/editor-client/public/types/node",
|
||||
"packages/node_modules/@node-red/editor-client/public/types/node-red",
|
||||
"public/red",
|
||||
"public/index.html",
|
||||
"public/favicon.ico",
|
||||
"public/icons",
|
||||
"public/vendor"
|
||||
]
|
||||
},
|
||||
release: {
|
||||
@ -326,36 +270,30 @@ module.exports = function(grunt) {
|
||||
watch: {
|
||||
js: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/**/*.js'
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
tasks: ['copy:build','concat',/*'uglify',*/ 'attachCopyright:js']
|
||||
tasks: ['copy:build','concat','uglify','attachCopyright:js']
|
||||
},
|
||||
sass: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss'
|
||||
'editor/sass/**/*.scss'
|
||||
],
|
||||
tasks: ['sass','attachCopyright:css']
|
||||
},
|
||||
json: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/nodes/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/editor-client/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/runtime/locales/**/*.json'
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
],
|
||||
tasks: ['jsonlint:messages']
|
||||
},
|
||||
keymaps: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
|
||||
'editor/js/keymap.json'
|
||||
],
|
||||
tasks: ['jsonlint:keymaps','copy:build']
|
||||
},
|
||||
tours: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/tours/**/*.js'
|
||||
],
|
||||
tasks: ['copy:build']
|
||||
},
|
||||
misc: {
|
||||
files: [
|
||||
'CHANGELOG.md'
|
||||
@ -367,13 +305,12 @@ module.exports = function(grunt) {
|
||||
nodemon: {
|
||||
/* uses .nodemonignore */
|
||||
dev: {
|
||||
script: 'packages/node_modules/node-red/red.js',
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'packages/node_modules',
|
||||
'!packages/node_modules/@node-red/editor-client'
|
||||
'red','nodes'
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -392,75 +329,69 @@ module.exports = function(grunt) {
|
||||
build: {
|
||||
files:[
|
||||
{
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js',
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js'
|
||||
src: 'editor/js/main.js',
|
||||
dest: 'public/red/main.js'
|
||||
},
|
||||
{
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json',
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json'
|
||||
src: 'editor/js/keymap.json',
|
||||
dest: 'public/red/keymap.json'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/images',
|
||||
cwd: 'editor/images',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/images/'
|
||||
dest: 'public/red/images/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/vendor',
|
||||
cwd: 'editor/vendor',
|
||||
src: [
|
||||
'ace/**',
|
||||
'jquery/css/base/**',
|
||||
'font-awesome/**',
|
||||
'monaco/dist/**',
|
||||
'monaco/types/extraLibs.js',
|
||||
'monaco/style.css',
|
||||
'monaco/monaco-bootstrap.js'
|
||||
//'bootstrap/css/**',
|
||||
'bootstrap/img/**',
|
||||
'jquery/css/**',
|
||||
'font-awesome/**'
|
||||
],
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/'
|
||||
dest: 'public/vendor/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src',
|
||||
src: [
|
||||
'types/node/**/*.ts',
|
||||
'types/node-red/*.ts',
|
||||
],
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/icons',
|
||||
cwd: 'editor/icons',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/icons/'
|
||||
dest: 'public/icons/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'],
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/',
|
||||
src: ['editor/index.html','editor/favicon.ico'],
|
||||
dest: 'public/',
|
||||
flatten: true
|
||||
},
|
||||
{
|
||||
src: 'CHANGELOG.md',
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/about'
|
||||
},
|
||||
{
|
||||
src: 'CHANGELOG.md',
|
||||
dest: 'packages/node_modules/node-red/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/tours',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/'
|
||||
dest: 'public/red/about'
|
||||
}
|
||||
]
|
||||
},
|
||||
release: {
|
||||
files: [{
|
||||
mode: true,
|
||||
expand: true,
|
||||
src: [
|
||||
'*.md',
|
||||
'LICENSE',
|
||||
'package.json',
|
||||
'settings.js',
|
||||
'red.js',
|
||||
'lib/.gitignore',
|
||||
'nodes/*.demo',
|
||||
'nodes/core/**',
|
||||
'red/**',
|
||||
'public/**',
|
||||
'editor/templates/**',
|
||||
'bin/**'
|
||||
],
|
||||
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
|
||||
}]
|
||||
}
|
||||
},
|
||||
chmod: {
|
||||
@ -469,95 +400,19 @@ module.exports = function(grunt) {
|
||||
},
|
||||
release: {
|
||||
src: [
|
||||
"packages/node_modules/@node-red/nodes/core/hardware/nrgpio",
|
||||
"packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/node-red-*sh"
|
||||
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*'),
|
||||
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/red/runtime/storage/localfilesystem/projects/git/node-red-*sh')
|
||||
]
|
||||
}
|
||||
},
|
||||
'npm-command': {
|
||||
options: {
|
||||
cmd: "pack",
|
||||
cwd: "<%= paths.dist %>/modules"
|
||||
},
|
||||
'node-red': { options: { args: [__dirname+'/packages/node_modules/node-red'] } },
|
||||
'@node-red/editor-api': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-api'] } },
|
||||
'@node-red/editor-client': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-client'] } },
|
||||
'@node-red/nodes': { options: { args: [__dirname+'/packages/node_modules/@node-red/nodes'] } },
|
||||
'@node-red/registry': { options: { args: [__dirname+'/packages/node_modules/@node-red/registry'] } },
|
||||
'@node-red/runtime': { options: { args: [__dirname+'/packages/node_modules/@node-red/runtime'] } },
|
||||
'@node-red/util': { options: { args: [__dirname+'/packages/node_modules/@node-red/util'] } }
|
||||
|
||||
|
||||
},
|
||||
mkdir: {
|
||||
release: {
|
||||
options: {
|
||||
create: ['<%= paths.dist %>/modules']
|
||||
},
|
||||
},
|
||||
},
|
||||
compress: {
|
||||
release: {
|
||||
options: {
|
||||
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
|
||||
},
|
||||
expand: true,
|
||||
cwd: 'packages/node_modules/',
|
||||
src: [
|
||||
'**',
|
||||
'!@node-red/editor-client/src/**'
|
||||
]
|
||||
}
|
||||
},
|
||||
jsdoc : {
|
||||
modules: {
|
||||
src: [
|
||||
'API.md',
|
||||
'packages/node_modules/node-red/lib/red.js',
|
||||
'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',
|
||||
'packages/node_modules/@node-red/registry/lib/index.js'
|
||||
],
|
||||
options: {
|
||||
destination: 'docs',
|
||||
configure: './jsdoc.json',
|
||||
fred: "hi there"
|
||||
}
|
||||
},
|
||||
_editor: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js'
|
||||
],
|
||||
options: {
|
||||
destination: 'packages/node_modules/@node-red/editor-client/docs',
|
||||
configure: './jsdoc.json'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
jsdoc2md: {
|
||||
runtimeAPI: {
|
||||
options: {
|
||||
separators: true
|
||||
},
|
||||
src: [
|
||||
'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'
|
||||
],
|
||||
dest: 'packages/node_modules/@node-red/runtime/docs/api.md'
|
||||
},
|
||||
nodeREDUtil: {
|
||||
options: {
|
||||
separators: true
|
||||
},
|
||||
src: 'packages/node_modules/@node-red/util/**/*.js',
|
||||
dest: 'packages/node_modules/@node-red/util/docs/api.md'
|
||||
cwd: '<%= paths.dist %>/',
|
||||
src: ['node-red-<%= pkg.version %>/**']
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -570,42 +425,18 @@ 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');
|
||||
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.loadNpmTasks('grunt-webdriver');
|
||||
}
|
||||
grunt.loadNpmTasks('grunt-jsdoc');
|
||||
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
|
||||
grunt.loadNpmTasks('grunt-npm-command');
|
||||
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.loadNpmTasks('grunt-mocha-istanbul');
|
||||
grunt.loadNpmTasks('grunt-webdriver');
|
||||
|
||||
grunt.registerMultiTask('attachCopyright', function() {
|
||||
var files = this.data.src;
|
||||
var copyright = "/**\n"+
|
||||
" * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+
|
||||
" * Copyright JS Foundation and other contributors, http://js.foundation\n"+
|
||||
" *\n"+
|
||||
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+
|
||||
" * you may not use this file except in compliance with the License.\n"+
|
||||
@ -642,38 +473,6 @@ module.exports = function(grunt) {
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('verifyPackageDependencies', function() {
|
||||
var done = this.async();
|
||||
var verifyDependencies = require("./scripts/verify-package-dependencies.js");
|
||||
verifyDependencies().then(function(failures) {
|
||||
if (failures.length > 0) {
|
||||
failures.forEach(f => grunt.log.error(f));
|
||||
grunt.fail.fatal("Failed to verify package dependencies");
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('verifyUiTestDependencies', function() {
|
||||
if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.fail.fatal('You need to install the UI test dependencies first.\nUse the script in "scripts/install-ui-test-dependencies.sh"');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
grunt.registerTask('generatePublishScript',
|
||||
'Generates a script to publish build output to npm',
|
||||
function () {
|
||||
const done = this.async();
|
||||
const generatePublishScript = require("./scripts/generate-publish-script.js");
|
||||
generatePublishScript().then(function(output) {
|
||||
grunt.log.writeln(output);
|
||||
|
||||
const filePath = path.join(grunt.config.get('paths.dist'),"modules","publish.sh");
|
||||
grunt.file.write(filePath,output);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('setDevEnv',
|
||||
'Sets NODE_ENV=development so non-minified assets are used',
|
||||
function () {
|
||||
@ -682,61 +481,37 @@ 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','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']);
|
||||
|
||||
['build','jshint:editor','mocha_istanbul:all']);
|
||||
|
||||
grunt.registerTask('test-core',
|
||||
'Runs code style check and unit tests on core runtime code',
|
||||
['build','nyc:core']);
|
||||
['build','mocha_istanbul:core']);
|
||||
|
||||
grunt.registerTask('test-editor',
|
||||
'Runs code style check on editor code',
|
||||
['jshint:editor']);
|
||||
|
||||
if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.registerTask('test-ui',
|
||||
'Builds editor content then runs unit tests on editor ui',
|
||||
['verifyUiTestDependencies']);
|
||||
} else {
|
||||
grunt.registerTask('test-ui',
|
||||
'Builds editor content then runs unit tests on editor ui',
|
||||
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
|
||||
}
|
||||
grunt.registerTask('test-ui',
|
||||
'Builds editor content then runs unit tests on editor ui',
|
||||
['build','jshint:editor','webdriver:all']);
|
||||
|
||||
grunt.registerTask('test-nodes',
|
||||
'Runs unit tests on core nodes',
|
||||
['build','nyc:nodes']);
|
||||
['build','mocha_istanbul: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']);
|
||||
|
||||
grunt.registerTask('dev',
|
||||
'Developer mode: run node-red, watch for source changes and build/restart',
|
||||
['build','setDevEnv','concurrent:dev']);
|
||||
|
||||
grunt.registerTask('release',
|
||||
'Create distribution zip file',
|
||||
['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules','generatePublishScript']);
|
||||
|
||||
grunt.registerTask('pack-modules',
|
||||
'Create module pack files for release',
|
||||
['mkdir:release','npm-command']);
|
||||
|
||||
['build','clean:release','copy:release','chmod:release','compress:release']);
|
||||
|
||||
grunt.registerTask('coverage',
|
||||
'Run Istanbul code test coverage task',
|
||||
['build','nyc:all']);
|
||||
|
||||
grunt.registerTask('docs',
|
||||
'Generates API documentation',
|
||||
['jsdoc']);
|
||||
['build','mocha_istanbul:all']);
|
||||
};
|
||||
|
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
Copyright JS Foundation and other contributors, http://js.foundation
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
28
README.md
@ -1,16 +1,17 @@
|
||||
# Node-RED
|
||||
|
||||
https://nodered.org
|
||||
http://nodered.org
|
||||
|
||||
[](https://github.com/node-red/node-red/actions?query=branch%3Amaster)
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
Low-code programming for event-driven applications.
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||

|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
Check out https://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
started.
|
||||
|
||||
1. `sudo npm install -g --unsafe-perm node-red`
|
||||
@ -19,7 +20,7 @@ started.
|
||||
|
||||
## Getting Help
|
||||
|
||||
More documentation can be found [here](https://nodered.org/docs).
|
||||
More documentation can be found [here](http://nodered.org/docs).
|
||||
|
||||
For further help, or general discussion, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
|
||||
|
||||
@ -43,6 +44,9 @@ If you want to run the latest code from git, here's how to get started:
|
||||
4. Run
|
||||
|
||||
npm start
|
||||
or
|
||||
|
||||
node red.js
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -55,15 +59,15 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena
|
||||
|
||||
## Authors
|
||||
|
||||
Node-RED is a project of the [OpenJS Foundation](http://openjsf.org).
|
||||
Node-RED is a project of the [JS Foundation](http://js.foundation).
|
||||
|
||||
It is maintained by:
|
||||
It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
|
||||
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
* And many others...
|
||||
|
||||
|
||||
## Copyright and license
|
||||
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE).
|
||||
Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE).
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly.
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright JS Foundation and other contributors, http://js.foundation
|
||||
#
|
||||
@ -29,16 +29,15 @@ do
|
||||
done
|
||||
|
||||
# Find the real location of this script
|
||||
CURRENT_PATH=$(pwd)
|
||||
SCRIPT_PATH=$(readlink -f "$0")
|
||||
CURRENT_PATH=`pwd`
|
||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
||||
while [ -h "${SCRIPT_PATH}" ]; do
|
||||
cd "$(dirname "${SCRIPT_PATH}")" || exit 1
|
||||
P=$(basename "${SCRIPT_PATH}")
|
||||
SCRIPT_PATH=$(readlink "${P}")
|
||||
cd "`dirname "${SCRIPT_PATH}"`"
|
||||
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
||||
done
|
||||
cd "$(dirname "${SCRIPT_PATH}")" > /dev/null || exit 1
|
||||
SCRIPT_PATH=$(pwd)
|
||||
cd "$CURRENT_PATH" || exit 1
|
||||
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
|
||||
SCRIPT_PATH="`pwd`";
|
||||
cd $CURRENT_PATH
|
||||
|
||||
# Run Node-RED
|
||||
exec /usr/bin/env node ${OPTIONS} "${SCRIPT_PATH}"/../red.js ${ARGS}
|
||||
exec /usr/bin/env node $OPTIONS $SCRIPT_PATH/../red.js $ARGS
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
BIN
editor/icons/alert.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
editor/icons/arduino.png
Normal file
After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
BIN
editor/icons/batch.png
Normal file
After Width: | Height: | Size: 712 B |
BIN
editor/icons/bluetooth.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
editor/icons/bridge-dash.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
editor/icons/bridge.png
Normal file
After Width: | Height: | Size: 575 B |
BIN
editor/icons/cog.png
Normal file
After Width: | Height: | Size: 493 B |
BIN
editor/icons/comment.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
editor/icons/db.png
Normal file
After Width: | Height: | Size: 459 B |
BIN
editor/icons/debug.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
editor/icons/envelope.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
editor/icons/feed.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
editor/icons/file-in.png
Normal file
After Width: | Height: | Size: 542 B |
BIN
editor/icons/file-out.png
Normal file
After Width: | Height: | Size: 503 B |
BIN
editor/icons/file.png
Normal file
After Width: | Height: | Size: 255 B |
BIN
editor/icons/function.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
editor/icons/hash.png
Normal file
After Width: | Height: | Size: 502 B |
BIN
editor/icons/inject.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
editor/icons/join.png
Normal file
After Width: | Height: | Size: 253 B |
BIN
editor/icons/leveldb.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
editor/icons/light.png
Normal file
After Width: | Height: | Size: 639 B |
BIN
editor/icons/link-out.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
editor/icons/mongodb.png
Normal file
After Width: | Height: | Size: 414 B |
BIN
editor/icons/mouse.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
editor/icons/node-changed.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/icons/node-error.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/icons/parser-csv.png
Normal file
After Width: | Height: | Size: 413 B |
BIN
editor/icons/parser-html.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
editor/icons/parser-json.png
Normal file
After Width: | Height: | Size: 467 B |
BIN
editor/icons/parser-xml.png
Normal file
After Width: | Height: | Size: 393 B |
BIN
editor/icons/parser-yaml.png
Normal file
After Width: | Height: | Size: 423 B |
BIN
editor/icons/range.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
editor/icons/redis.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
editor/icons/rpi.png
Normal file
After Width: | Height: | Size: 482 B |
BIN
editor/icons/serial.png
Normal file
After Width: | Height: | Size: 273 B |
BIN
editor/icons/sort.png
Normal file
After Width: | Height: | Size: 793 B |
BIN
editor/icons/split.png
Normal file
After Width: | Height: | Size: 256 B |
BIN
editor/icons/subflow.png
Normal file
After Width: | Height: | Size: 439 B |
BIN
editor/icons/swap.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
editor/icons/switch.png
Normal file
After Width: | Height: | Size: 509 B |
BIN
editor/icons/template.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
editor/icons/timer.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
editor/icons/trigger.png
Normal file
After Width: | Height: | Size: 258 B |
BIN
editor/icons/twitter.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
editor/icons/watch.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
editor/icons/white-globe.png
Normal file
After Width: | Height: | Size: 707 B |
BIN
editor/images/deploy-flows-o.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
editor/images/deploy-flows.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/images/deploy-full-o.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
editor/images/deploy-full.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
editor/images/deploy-nodes-o.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
editor/images/deploy-nodes.png
Normal file
After Width: | Height: | Size: 392 B |
BIN
editor/images/grip.png
Normal file
After Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
BIN
editor/images/node-red.png
Normal file
After Width: | Height: | Size: 1019 B |
BIN
editor/images/pw_maze_white.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
editor/images/subflow_tab.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
editor/images/typedInput/09.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/az.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
editor/images/typedInput/bin.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/bool.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
editor/images/typedInput/env.png
Normal file
After Width: | Height: | Size: 809 B |
BIN
editor/images/typedInput/expr.png
Normal file
After Width: | Height: | Size: 563 B |
BIN
editor/images/typedInput/json.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
editor/images/typedInput/re.png
Normal file
After Width: | Height: | Size: 502 B |
@ -26,35 +26,16 @@ RED.comms = (function() {
|
||||
var reconnectAttempts = 0;
|
||||
var active = false;
|
||||
|
||||
RED.events.on('login', function(username) {
|
||||
// User has logged in
|
||||
// Need to upgrade the connection to be authenticated
|
||||
if (ws && ws.readyState == 1) {
|
||||
const auth_tokens = RED.settings.get("auth-tokens");
|
||||
ws.send(JSON.stringify({auth:auth_tokens.access_token}))
|
||||
}
|
||||
})
|
||||
|
||||
function connectWS() {
|
||||
active = true;
|
||||
var wspath;
|
||||
|
||||
if (RED.settings.apiRootUrl) {
|
||||
var m = /^(https?):\/\/(.*)$/.exec(RED.settings.apiRootUrl);
|
||||
if (m) {
|
||||
console.log(m);
|
||||
wspath = "ws"+(m[1]==="https"?"s":"")+"://"+m[2]+"comms";
|
||||
}
|
||||
} else {
|
||||
var path = location.hostname;
|
||||
var port = location.port;
|
||||
if (port.length !== 0) {
|
||||
path = path+":"+port;
|
||||
}
|
||||
path = path+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
|
||||
wspath = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
var path = location.hostname;
|
||||
var port = location.port;
|
||||
if (port.length !== 0) {
|
||||
path = path+":"+port;
|
||||
}
|
||||
path = path+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
|
||||
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
pendingAuth = (auth_tokens!=null);
|
||||
@ -65,10 +46,9 @@ RED.comms = (function() {
|
||||
ws.send(JSON.stringify({subscribe:t}));
|
||||
}
|
||||
}
|
||||
emit('connect')
|
||||
}
|
||||
|
||||
ws = new WebSocket(wspath);
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
reconnectAttempts = 0;
|
||||
if (errornotification) {
|
||||
@ -85,39 +65,29 @@ RED.comms = (function() {
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var message = JSON.parse(event.data);
|
||||
if (message.auth) {
|
||||
if (pendingAuth) {
|
||||
if (message.auth === "ok") {
|
||||
for (var m = 0; m < message.length; m++) {
|
||||
var msg = message[m];
|
||||
if (pendingAuth && msg.auth) {
|
||||
if (msg.auth === "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
} else if (message.auth === "fail") {
|
||||
} else if (msg.auth === "fail") {
|
||||
// anything else is an error...
|
||||
active = false;
|
||||
RED.user.login({updateMenu:true},function() {
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
} else if (message.auth === "fail") {
|
||||
// Our current session has expired
|
||||
active = false;
|
||||
RED.user.login({updateMenu:true},function() {
|
||||
connectWS();
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Otherwise, 'message' is an array of actual comms messages
|
||||
for (var m = 0; m < message.length; m++) {
|
||||
var msg = message[m];
|
||||
if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
}
|
||||
else if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,10 +122,10 @@ RED.comms = (function() {
|
||||
connectWS();
|
||||
} else {
|
||||
var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>';
|
||||
errornotification.update(msg,{silent:true});
|
||||
$(errornotification).find("a").on("click", function(e) {
|
||||
errornotification.update(msg);
|
||||
$(errornotification).find("a").click(function(e) {
|
||||
e.preventDefault();
|
||||
errornotification.update(RED._("notification.errors.lostConnection"),{silent:true});
|
||||
errornotification.update(RED._("notification.errors.lostConnection"));
|
||||
clearInterval(connectCountdownTimer);
|
||||
connectWS();
|
||||
})
|
||||
@ -190,53 +160,9 @@ RED.comms = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function send(topic, msg) {
|
||||
if (ws && ws.readyState == 1) {
|
||||
ws.send(JSON.stringify({
|
||||
topic,
|
||||
data: msg
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const eventHandlers = {};
|
||||
function on(evt,func) {
|
||||
eventHandlers[evt] = eventHandlers[evt]||[];
|
||||
eventHandlers[evt].push(func);
|
||||
}
|
||||
function off(evt,func) {
|
||||
const handler = eventHandlers[evt];
|
||||
if (handler) {
|
||||
for (let i=0;i<handler.length;i++) {
|
||||
if (handler[i] === func) {
|
||||
handler.splice(i,1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function emit() {
|
||||
const evt = arguments[0]
|
||||
const args = Array.prototype.slice.call(arguments,1);
|
||||
if (eventHandlers[evt]) {
|
||||
let cpyHandlers = [...eventHandlers[evt]];
|
||||
for (let i=0;i<cpyHandlers.length;i++) {
|
||||
try {
|
||||
cpyHandlers[i].apply(null, args);
|
||||
} catch(err) {
|
||||
console.warn("RED.comms.emit error: ["+evt+"] "+(err.toString()));
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect: connectWS,
|
||||
subscribe: subscribe,
|
||||
unsubscribe:unsubscribe,
|
||||
on,
|
||||
off,
|
||||
send
|
||||
unsubscribe:unsubscribe
|
||||
}
|
||||
})();
|
@ -32,23 +32,17 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
function emit() {
|
||||
var evt = arguments[0]
|
||||
var args = Array.prototype.slice.call(arguments,1);
|
||||
if (RED.events.DEBUG) {
|
||||
console.warn(evt,args);
|
||||
}
|
||||
function emit(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
let cpyHandlers = [...handlers[evt]];
|
||||
|
||||
for (var i=0;i<cpyHandlers.length;i++) {
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
try {
|
||||
cpyHandlers[i].apply(null, args);
|
||||
handlers[evt][i](arg);
|
||||
} catch(err) {
|
||||
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
||||
console.warn(err);
|
||||
console.log("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return {
|
332
editor/js/history.js
Normal file
@ -0,0 +1,332 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
RED.history = (function() {
|
||||
var undo_history = [];
|
||||
|
||||
function undoEvent(ev) {
|
||||
var i;
|
||||
var len;
|
||||
var node;
|
||||
var subflow;
|
||||
var modifiedTabs = {};
|
||||
if (ev) {
|
||||
if (ev.t == 'multi') {
|
||||
len = ev.events.length;
|
||||
for (i=len-1;i>=0;i--) {
|
||||
undoEvent(ev.events[i]);
|
||||
}
|
||||
} else if (ev.t == 'replace') {
|
||||
RED.nodes.clear();
|
||||
var imported = RED.nodes.import(ev.config);
|
||||
imported[0].forEach(function(n) {
|
||||
if (ev.changed[n.id]) {
|
||||
n.changed = true;
|
||||
}
|
||||
})
|
||||
|
||||
RED.nodes.version(ev.rev);
|
||||
} else if (ev.t == 'add') {
|
||||
if (ev.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
node = RED.nodes.node(ev.nodes[i]);
|
||||
if (node.z) {
|
||||
modifiedTabs[node.z] = true;
|
||||
}
|
||||
RED.nodes.remove(ev.nodes[i]);
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.removeWorkspace(ev.workspaces[i].id);
|
||||
RED.workspaces.remove(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflows) {
|
||||
for (i=0;i<ev.subflows.length;i++) {
|
||||
RED.nodes.removeSubflow(ev.subflows[i]);
|
||||
RED.workspaces.remove(ev.subflows[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.instances) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('changed')) {
|
||||
subflow = RED.nodes.subflow(ev.subflow.id);
|
||||
if (subflow) {
|
||||
subflow.changed = ev.subflow.changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ev.t == "delete") {
|
||||
if (ev.workspaces) {
|
||||
for (i=0;i<ev.workspaces.length;i++) {
|
||||
RED.nodes.addWorkspace(ev.workspaces[i]);
|
||||
RED.workspaces.add(ev.workspaces[i]);
|
||||
}
|
||||
}
|
||||
if (ev.subflow && ev.subflow.subflow) {
|
||||
RED.nodes.addSubflow(ev.subflow.subflow);
|
||||
}
|
||||
if (ev.subflowInputs && ev.subflowInputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowInputs[0].z);
|
||||
subflow.in.push(ev.subflowInputs[0]);
|
||||
subflow.in[0].dirty = true;
|
||||
}
|
||||
if (ev.subflowOutputs && ev.subflowOutputs.length > 0) {
|
||||
subflow = RED.nodes.subflow(ev.subflowOutputs[0].z);
|
||||
ev.subflowOutputs.sort(function(a,b) { return a.i-b.i});
|
||||
for (i=0;i<ev.subflowOutputs.length;i++) {
|
||||
var output = ev.subflowOutputs[i];
|
||||
subflow.out.splice(output.i,0,output);
|
||||
for (var j=output.i+1;j<subflow.out.length;j++) {
|
||||
subflow.out[j].i++;
|
||||
subflow.out[j].dirty = true;
|
||||
}
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow:"+subflow.id) {
|
||||
if (l.sourcePort >= output.i) {
|
||||
l.sourcePort++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (subflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
|
||||
n.inputs = subflow.in.length;
|
||||
n.outputs = subflow.out.length;
|
||||
while (n.outputs > n.ports.length) {
|
||||
n.ports.push(n.ports.length);
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
});
|
||||
}
|
||||
if (ev.nodes) {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.add(ev.nodes[i]);
|
||||
modifiedTabs[ev.nodes[i].z] = true;
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.changes) {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
node = RED.nodes.node(i);
|
||||
if (node) {
|
||||
for (var d in ev.changes[i]) {
|
||||
if (ev.changes[i].hasOwnProperty(d)) {
|
||||
node[d] = ev.changes[i][d];
|
||||
}
|
||||
}
|
||||
node.dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (ev.t == "move") {
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
var n = ev.nodes[i];
|
||||
n.n.x = n.ox;
|
||||
n.n.y = n.oy;
|
||||
n.n.dirty = true;
|
||||
n.n.moved = n.moved;
|
||||
}
|
||||
// A move could have caused a link splice
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "edit") {
|
||||
for (i in ev.changes) {
|
||||
if (ev.changes.hasOwnProperty(i)) {
|
||||
if (ev.node._def.defaults && ev.node._def.defaults[i] && ev.node._def.defaults[i].type) {
|
||||
// This is a config node property
|
||||
var currentConfigNode = RED.nodes.node(ev.node[i]);
|
||||
if (currentConfigNode) {
|
||||
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
|
||||
}
|
||||
var newConfigNode = RED.nodes.node(ev.changes[i]);
|
||||
if (newConfigNode) {
|
||||
newConfigNode.users.push(ev.node);
|
||||
}
|
||||
}
|
||||
ev.node[i] = ev.changes[i];
|
||||
}
|
||||
}
|
||||
if (ev.subflow) {
|
||||
if (ev.subflow.hasOwnProperty('inputCount')) {
|
||||
if (ev.node.in.length > ev.subflow.inputCount) {
|
||||
ev.node.in.splice(ev.subflow.inputCount);
|
||||
} else if (ev.subflow.inputs.length > 0) {
|
||||
ev.node.in = ev.node.in.concat(ev.subflow.inputs);
|
||||
}
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('outputCount')) {
|
||||
if (ev.node.out.length > ev.subflow.outputCount) {
|
||||
ev.node.out.splice(ev.subflow.outputCount);
|
||||
} else if (ev.subflow.outputs.length > 0) {
|
||||
ev.node.out = ev.node.out.concat(ev.subflow.outputs);
|
||||
}
|
||||
}
|
||||
if (ev.subflow.hasOwnProperty('instances')) {
|
||||
ev.subflow.instances.forEach(function(n) {
|
||||
var node = RED.nodes.node(n.id);
|
||||
if (node) {
|
||||
node.changed = n.changed;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
RED.editor.validateNode(ev.node);
|
||||
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
|
||||
n.inputs = ev.node.in.length;
|
||||
n.outputs = ev.node.out.length;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
RED.editor.validateNode(n);
|
||||
});
|
||||
} else {
|
||||
var outputMap;
|
||||
if (ev.outputMap) {
|
||||
outputMap = {};
|
||||
for (var port in ev.outputMap) {
|
||||
if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== "-1") {
|
||||
outputMap[ev.outputMap[port]] = port;
|
||||
}
|
||||
}
|
||||
}
|
||||
RED.editor.updateNodeProperties(ev.node,outputMap);
|
||||
RED.editor.validateNode(ev.node);
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.addLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
ev.node.dirty = true;
|
||||
ev.node.changed = ev.changed;
|
||||
} else if (ev.t == "createSubflow") {
|
||||
if (ev.nodes) {
|
||||
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
|
||||
n.z = ev.activeWorkspace;
|
||||
n.dirty = true;
|
||||
});
|
||||
for (i=0;i<ev.nodes.length;i++) {
|
||||
RED.nodes.remove(ev.nodes[i]);
|
||||
}
|
||||
}
|
||||
if (ev.links) {
|
||||
for (i=0;i<ev.links.length;i++) {
|
||||
RED.nodes.removeLink(ev.links[i]);
|
||||
}
|
||||
}
|
||||
|
||||
RED.nodes.removeSubflow(ev.subflow.subflow);
|
||||
RED.workspaces.remove(ev.subflow.subflow);
|
||||
|
||||
if (ev.removedLinks) {
|
||||
for (i=0;i<ev.removedLinks.length;i++) {
|
||||
RED.nodes.addLink(ev.removedLinks[i]);
|
||||
}
|
||||
}
|
||||
} else if (ev.t == "reorder") {
|
||||
if (ev.order) {
|
||||
RED.workspaces.order(ev.order);
|
||||
}
|
||||
}
|
||||
Object.keys(modifiedTabs).forEach(function(id) {
|
||||
var subflow = RED.nodes.subflow(id);
|
||||
if (subflow) {
|
||||
RED.editor.validateNode(subflow);
|
||||
}
|
||||
});
|
||||
|
||||
RED.nodes.dirty(ev.dirty);
|
||||
RED.view.redraw(true);
|
||||
RED.palette.refresh();
|
||||
RED.workspaces.refresh();
|
||||
RED.sidebar.config.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
//TODO: this function is a placeholder until there is a 'save' event that can be listened to
|
||||
markAllDirty: function() {
|
||||
for (var i=0;i<undo_history.length;i++) {
|
||||
undo_history[i].dirty = true;
|
||||
}
|
||||
},
|
||||
list: function() {
|
||||
return undo_history
|
||||
},
|
||||
depth: function() {
|
||||
return undo_history.length;
|
||||
},
|
||||
push: function(ev) {
|
||||
undo_history.push(ev);
|
||||
},
|
||||
pop: function() {
|
||||
var ev = undo_history.pop();
|
||||
undoEvent(ev);
|
||||
},
|
||||
peek: function() {
|
||||
return undo_history[undo_history.length-1];
|
||||
},
|
||||
clear: function() {
|
||||
undo_history = [];
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
86
editor/js/i18n.js
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
|
||||
RED.i18n = (function() {
|
||||
|
||||
return {
|
||||
init: function(done) {
|
||||
i18n.init({
|
||||
resGetPath: 'locales/__ns__?lng=__lng__',
|
||||
dynamicLoad: false,
|
||||
load:'current',
|
||||
ns: {
|
||||
namespaces: ["editor","node-red","jsonata","infotips"],
|
||||
defaultNs: "editor"
|
||||
},
|
||||
fallbackLng: ['en-US'],
|
||||
useCookie: false
|
||||
},function() {
|
||||
done();
|
||||
});
|
||||
RED["_"] = function() {
|
||||
return i18n.t.apply(null,arguments);
|
||||
}
|
||||
|
||||
},
|
||||
loadCatalog: function(namespace,done) {
|
||||
var languageList = i18n.functions.toLanguages(i18n.detectLanguage());
|
||||
var toLoad = languageList.length;
|
||||
languageList.forEach(function(lang) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'locales/'+namespace+'?lng='+lang,
|
||||
success: function(data) {
|
||||
i18n.addResourceBundle(lang,namespace,data);
|
||||
toLoad--;
|
||||
if (toLoad === 0) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
loadNodeCatalogs: function(done) {
|
||||
var languageList = i18n.functions.toLanguages(i18n.detectLanguage());
|
||||
var toLoad = languageList.length;
|
||||
|
||||
languageList.forEach(function(lang) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'locales/nodes?lng='+lang,
|
||||
success: function(data) {
|
||||
var namespaces = Object.keys(data);
|
||||
namespaces.forEach(function(ns) {
|
||||
i18n.addResourceBundle(lang,ns,data[ns]);
|
||||
});
|
||||
toLoad--;
|
||||
if (toLoad === 0) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
})();
|
44
editor/js/keymap.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"*": {
|
||||
"ctrl-shift-p":"core:manage-palette",
|
||||
"ctrl-f": "core:search",
|
||||
"ctrl-=": "core:zoom-in",
|
||||
"ctrl--": "core:zoom-out",
|
||||
"ctrl-0": "core:zoom-reset",
|
||||
"ctrl-enter": "core:confirm-edit-tray",
|
||||
"ctrl-escape": "core:cancel-edit-tray",
|
||||
"ctrl-g i": "core:show-info-tab",
|
||||
"ctrl-g d": "core:show-debug-tab",
|
||||
"ctrl-g c": "core:show-config-tab",
|
||||
"ctrl-g x": "core:show-context-tab",
|
||||
"ctrl-e": "core:show-export-dialog",
|
||||
"ctrl-i": "core:show-import-dialog",
|
||||
"ctrl-space": "core:toggle-sidebar",
|
||||
"ctrl-,": "core:show-user-settings",
|
||||
"ctrl-alt-r": "core:show-remote-diff",
|
||||
"ctrl-alt-n": "core:new-project",
|
||||
"ctrl-alt-o": "core:open-project",
|
||||
"ctrl-g v": "core:show-version-control-tab"
|
||||
},
|
||||
"workspace": {
|
||||
"backspace": "core:delete-selection",
|
||||
"delete": "core:delete-selection",
|
||||
"enter": "core:edit-selected-node",
|
||||
"ctrl-c": "core:copy-selection-to-internal-clipboard",
|
||||
"ctrl-x": "core:cut-selection-to-internal-clipboard",
|
||||
"ctrl-v": "core:paste-from-internal-clipboard",
|
||||
"ctrl-z": "core:undo",
|
||||
"ctrl-a": "core:select-all-nodes",
|
||||
"shift-?": "core:show-help",
|
||||
"up": "core:move-selection-up",
|
||||
"right": "core:move-selection-right",
|
||||
"down": "core:move-selection-down",
|
||||
"left": "core:move-selection-left",
|
||||
"shift-up": "core:step-selection-up",
|
||||
"shift-right": "core:step-selection-right",
|
||||
"shift-down": "core:step-selection-down",
|
||||
"shift-left": "core:step-selection-left",
|
||||
"ctrl-shift-j": "core:show-previous-tab",
|
||||
"ctrl-shift-k": "core:show-next-tab"
|
||||
}
|
||||
}
|
501
editor/js/main.js
Normal file
@ -0,0 +1,501 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
(function() {
|
||||
|
||||
function appendNodeConfig(nodeConfig) {
|
||||
var m = /<!-- --- \[red-module:(\S+)\] --- -->/.exec(nodeConfig.trim());
|
||||
var moduleId;
|
||||
if (m) {
|
||||
moduleId = m[1];
|
||||
} else {
|
||||
moduleId = "unknown";
|
||||
}
|
||||
try {
|
||||
$("body").append(nodeConfig);
|
||||
} catch(err) {
|
||||
RED.notify(RED._("notification.errors.failedToAppendNode",{module:moduleId, error:err.toString()}),{
|
||||
type: "error",
|
||||
timeout: 10000
|
||||
});
|
||||
console.log("["+moduleId+"] "+err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
function loadNodeList() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
RED.nodes.setNodeList(data);
|
||||
RED.i18n.loadNodeCatalogs(function() {
|
||||
loadIconList(loadNodes);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadIconList(done) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'icons',
|
||||
success: function(data) {
|
||||
RED.nodes.setIconSets(data);
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadNodes() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"text/html"
|
||||
},
|
||||
cache: false,
|
||||
url: 'nodes',
|
||||
success: function(data) {
|
||||
var configs = data.trim().split(/(?=<!-- --- \[red-module:\S+\] --- -->)/);
|
||||
configs.forEach(function(data) {
|
||||
appendNodeConfig(data);
|
||||
});
|
||||
|
||||
$("body").i18n();
|
||||
$("#palette > .palette-spinner").hide();
|
||||
$(".palette-scroll").removeClass("hide");
|
||||
$("#palette-search").removeClass("hide");
|
||||
loadFlows(function() {
|
||||
if (RED.settings.theme("projects.enabled",false)) {
|
||||
RED.projects.refresh(function(activeProject) {
|
||||
RED.sidebar.info.refresh()
|
||||
if (!activeProject) {
|
||||
// Projects enabled but no active project
|
||||
RED.menu.setDisabled('menu-item-projects-open',true);
|
||||
RED.menu.setDisabled('menu-item-projects-settings',true);
|
||||
if (activeProject === false) {
|
||||
// User previously decline the migration to projects.
|
||||
} else { // null/undefined
|
||||
RED.projects.showStartup();
|
||||
}
|
||||
}
|
||||
completeLoad();
|
||||
});
|
||||
} else {
|
||||
// Projects disabled by the user
|
||||
RED.sidebar.info.refresh()
|
||||
completeLoad();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadFlows(done) {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json",
|
||||
},
|
||||
cache: false,
|
||||
url: 'flows',
|
||||
success: function(nodes) {
|
||||
if (nodes) {
|
||||
var currentHash = window.location.hash;
|
||||
RED.nodes.version(nodes.rev);
|
||||
RED.nodes.import(nodes.flows);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
if (/^#flow\/.+$/.test(currentHash)) {
|
||||
RED.workspaces.show(currentHash.substring(6));
|
||||
}
|
||||
}
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function completeLoad() {
|
||||
var persistentNotifications = {};
|
||||
RED.comms.subscribe("notification/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var notificationId = parts[1];
|
||||
if (notificationId === "runtime-deploy") {
|
||||
// handled in ui/deploy.js
|
||||
return;
|
||||
}
|
||||
if (notificationId === "node") {
|
||||
// handled below
|
||||
return;
|
||||
}
|
||||
if (notificationId === "project-update") {
|
||||
RED.nodes.clear();
|
||||
RED.history.clear();
|
||||
RED.view.redraw(true);
|
||||
RED.projects.refresh(function() {
|
||||
loadFlows(function() {
|
||||
var project = RED.projects.getActiveProject();
|
||||
var message = {
|
||||
"change-branch": RED._("notification.project.change-branch", {project: project.git.branches.local}),
|
||||
"merge-abort": RED._("notification.project.merge-abort"),
|
||||
"loaded": RED._("notification.project.loaded", {project: msg.project}),
|
||||
"updated": RED._("notification.project.updated", {project: msg.project}),
|
||||
"pull": RED._("notification.project.pull", {project: msg.project}),
|
||||
"revert": RED._("notification.project.revert", {project: msg.project}),
|
||||
"merge-complete": RED._("notification.project.merge-complete")
|
||||
}[msg.action];
|
||||
RED.notify("<p>"+message+"</p>");
|
||||
RED.sidebar.info.refresh()
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.text) {
|
||||
msg.default = msg.text;
|
||||
var text = RED._(msg.text,msg);
|
||||
var options = {
|
||||
type: msg.type,
|
||||
fixed: msg.timeout === undefined,
|
||||
timeout: msg.timeout,
|
||||
id: notificationId
|
||||
}
|
||||
if (notificationId === "runtime-state") {
|
||||
if (msg.error === "missing-types") {
|
||||
text+="<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
if (!!RED.projects.getActiveProject()) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.manage-project-dep"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.projects.settings.show('deps');
|
||||
}
|
||||
}
|
||||
]
|
||||
// } else if (RED.settings.theme('palette.editable') !== false) {
|
||||
} else {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (msg.error === "credentials_load_failed") {
|
||||
if (RED.settings.theme("projects.enabled",false)) {
|
||||
// projects enabled
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.setup-cred"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.projects.showCredentialsPrompt();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (msg.error === "missing_flow_file") {
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.setup-project"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.projects.showFilesPrompt();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (msg.error === "missing_package_file") {
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.create-default-package"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.projects.createDefaultPackageFile();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (msg.error === "project_empty") {
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.no-thanks"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("notification.label.create-default-project"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.projects.createDefaultFileSet();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
} else if (msg.error === "git_merge_conflict") {
|
||||
RED.nodes.clear();
|
||||
RED.sidebar.versionControl.refresh(true);
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
{
|
||||
text: RED._("notification.label.show-merge-conflicts"),
|
||||
click: function() {
|
||||
persistentNotifications[notificationId].hideNotification();
|
||||
RED.sidebar.versionControl.showLocalChanges();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
||||
persistentNotifications[notificationId] = RED.notify(text,options);
|
||||
} else {
|
||||
persistentNotifications[notificationId].update(text,options);
|
||||
}
|
||||
} else if (persistentNotifications.hasOwnProperty(notificationId)) {
|
||||
persistentNotifications[notificationId].close();
|
||||
delete persistentNotifications[notificationId];
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("status/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var node = RED.nodes.node(parts[1]);
|
||||
if (node) {
|
||||
if (msg.hasOwnProperty("text")) {
|
||||
if (msg.text[0] !== ".") {
|
||||
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
|
||||
}
|
||||
}
|
||||
node.status = msg;
|
||||
node.dirty = true;
|
||||
RED.view.redraw();
|
||||
}
|
||||
});
|
||||
RED.comms.subscribe("notification/node/#",function(topic,msg) {
|
||||
var i,m;
|
||||
var typeList;
|
||||
var info;
|
||||
if (topic == "notification/node/added") {
|
||||
var addedTypes = [];
|
||||
msg.forEach(function(m) {
|
||||
var id = m.id;
|
||||
RED.nodes.addNodeSet(m);
|
||||
addedTypes = addedTypes.concat(m.types);
|
||||
RED.i18n.loadCatalog(id, function() {
|
||||
$.get('nodes/'+id, function(data) {
|
||||
appendNodeConfig(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
if (addedTypes.length) {
|
||||
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
|
||||
}
|
||||
loadIconList();
|
||||
} else if (topic == "notification/node/removed") {
|
||||
for (i=0;i<msg.length;i++) {
|
||||
m = msg[i];
|
||||
info = RED.nodes.removeNodeSet(m.id);
|
||||
if (info.added) {
|
||||
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
|
||||
}
|
||||
}
|
||||
loadIconList();
|
||||
} else if (topic == "notification/node/enabled") {
|
||||
if (msg.types) {
|
||||
info = RED.nodes.getNodeSet(msg.id);
|
||||
if (info.added) {
|
||||
RED.nodes.enableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
|
||||
} else {
|
||||
$.get('nodes/'+msg.id, function(data) {
|
||||
appendNodeConfig(data);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (topic == "notification/node/disabled") {
|
||||
if (msg.types) {
|
||||
RED.nodes.disableNodeSet(msg.id);
|
||||
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
|
||||
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
|
||||
}
|
||||
} else if (topic == "node/upgraded") {
|
||||
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
|
||||
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
|
||||
}
|
||||
// Refresh flow library to ensure any examples are updated
|
||||
RED.library.loadFlowLibrary();
|
||||
});
|
||||
}
|
||||
|
||||
function showAbout() {
|
||||
$.get('red/about', function(data) {
|
||||
var aboutHeader = '<div style="text-align:center;">'+
|
||||
'<img width="50px" src="red/images/node-red-icon.svg" />'+
|
||||
'</div>';
|
||||
|
||||
RED.sidebar.info.set(aboutHeader+marked(data));
|
||||
RED.sidebar.info.show();
|
||||
});
|
||||
}
|
||||
|
||||
function loadEditor() {
|
||||
var menuOptions = [];
|
||||
if (RED.settings.theme("projects.enabled",false)) {
|
||||
menuOptions.push({id:"menu-item-projects-menu",label:RED._("menu.label.projects"),options:[
|
||||
{id:"menu-item-projects-new",label:RED._("menu.label.projects-new"),disabled:false,onselect:"core:new-project"},
|
||||
{id:"menu-item-projects-open",label:RED._("menu.label.projects-open"),disabled:false,onselect:"core:open-project"},
|
||||
{id:"menu-item-projects-settings",label:RED._("menu.label.projects-settings"),disabled:false,onselect:"core:show-project-settings"}
|
||||
]});
|
||||
}
|
||||
|
||||
|
||||
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
|
||||
// {id:"menu-item-view-show-grid",setting:"view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"},
|
||||
// {id:"menu-item-view-snap-grid",setting:"view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"},
|
||||
// {id:"menu-item-status",setting:"node-show-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true},
|
||||
//null,
|
||||
// {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[
|
||||
// {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}},
|
||||
// {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("ltr")}}},
|
||||
// {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("rtl")}}},
|
||||
// {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}}
|
||||
// ]},
|
||||
// null,
|
||||
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true},
|
||||
null
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[
|
||||
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-import-dialog"},
|
||||
{id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
|
||||
]});
|
||||
menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),options:[
|
||||
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:show-export-dialog"},
|
||||
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"}
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"});
|
||||
menuOptions.push(null);
|
||||
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"});
|
||||
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
|
||||
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"},
|
||||
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"},
|
||||
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"}
|
||||
]});
|
||||
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
|
||||
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"},
|
||||
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"},
|
||||
]});
|
||||
menuOptions.push(null);
|
||||
if (RED.settings.theme('palette.editable') !== false) {
|
||||
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"});
|
||||
menuOptions.push(null);
|
||||
}
|
||||
|
||||
menuOptions.push({id:"menu-item-user-settings",label:RED._("menu.label.settings"),onselect:"core:show-user-settings"});
|
||||
menuOptions.push(null);
|
||||
|
||||
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"});
|
||||
menuOptions.push({id:"menu-item-help",
|
||||
label: RED.settings.theme("menu.menu-item-help.label",RED._("menu.label.help")),
|
||||
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
|
||||
});
|
||||
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" });
|
||||
|
||||
|
||||
RED.view.init();
|
||||
RED.userSettings.init();
|
||||
RED.user.init();
|
||||
RED.library.init();
|
||||
RED.keyboard.init();
|
||||
RED.palette.init();
|
||||
if (RED.settings.theme('palette.editable') !== false) {
|
||||
RED.palette.editor.init();
|
||||
} else {
|
||||
console.log("Palette editor disabled");
|
||||
}
|
||||
|
||||
RED.sidebar.init();
|
||||
|
||||
if (RED.settings.theme("projects.enabled",false)) {
|
||||
RED.projects.init();
|
||||
} else {
|
||||
console.log("Projects disabled");
|
||||
}
|
||||
|
||||
RED.subflow.init();
|
||||
RED.workspaces.init();
|
||||
RED.clipboard.init();
|
||||
RED.search.init();
|
||||
RED.editor.init();
|
||||
RED.diff.init();
|
||||
|
||||
RED.menu.init({id:"btn-sidemenu",options: menuOptions});
|
||||
|
||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||
RED.notifications.init();
|
||||
|
||||
RED.actions.add("core:show-about", showAbout);
|
||||
RED.nodes.init();
|
||||
RED.comms.connect();
|
||||
|
||||
$("#main-container").show();
|
||||
$(".header-toolbar").show();
|
||||
|
||||
loadNodeList();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
|
||||
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
||||
document.title = document.title+" : "+window.location.hostname;
|
||||
}
|
||||
|
||||
ace.require("ace/ext/language_tools");
|
||||
|
||||
RED.i18n.init(function() {
|
||||
RED.settings.init(loadEditor);
|
||||
})
|
||||
});
|
||||
})();
|