Compare commits

..

No commits in common. "master" and "0.18.1" have entirely different histories.

1795 changed files with 65031 additions and 314435 deletions

1
.gitattributes vendored
View File

@ -1 +0,0 @@
/packages/node_modules/** linguist-generated=false

30
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,30 @@
## 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 [mailing list](https://groups.google.com/forum/#!forum/node-red), [slack team](https://nodered.org/slack) or ask 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:

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,3 @@
<!--
## Before you hit that Submit button.... ## Before you hit that Submit button....
Please read our [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) Please read our [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
@ -7,28 +6,25 @@ before submitting a pull-request.
## Types of changes ## Types of changes
What types of changes does your code introduce? What types of changes does your code introduce?
Put an `x` in the boxes that apply _Put an `x` in the boxes that apply_
-->
- [ ] Bugfix (non-breaking change which fixes an issue) - [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality) - [ ] New feature (non-breaking change which adds functionality)
<!--
If you want to raise a pull-request with a new feature, or a refactoring If you want to raise a pull-request with a new feature, or a refactoring
of existing code, it **may well get rejected** if it hasn't been discussed on of existing code, it **may well get rejected** if it hasn't been discussed on
the [forum](https://discourse.nodered.org) or the [mailing list](https://groups.google.com/forum/#!forum/node-red) or
[slack team](https://nodered.org/slack) first. [slack team](https://nodered.org/slack) first.
-->
## Proposed changes ## Proposed changes
<!-- Describe the nature of this change. What problem does it address? --> Describe the nature of this change. What problem does it address?
## Checklist ## Checklist
<!-- Put an `x` in the boxes that apply --> _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) - [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team. - [ ] For non-bugfix PRs, I have discussed this change on the mailing list/slack team.
- [ ] I have run `npm run test` to verify the unit tests pass - [ ] I have run `grunt` to verify the unit tests pass
- [ ] I have added suitable unit tests to cover the new/changed functionality - [ ] I have added suitable unit tests to cover the new/changed functionality

View File

@ -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:
- "*"

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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
View File

@ -7,9 +7,7 @@
.sessions.json .sessions.json
.settings .settings
.tern-project .tern-project
.i18n-editor-metadata
*.backup *.backup
*.bak
*_cred* *_cred*
coverage coverage
credentials.json credentials.json
@ -19,13 +17,3 @@ node_modules
public public
locales/zz-ZZ locales/zz-ZZ
nodes/core/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

View File

@ -15,5 +15,5 @@
"shadow": true, // allow variable shadowing (re-use of names...) "shadow": true, // allow variable shadowing (re-use of names...)
"sub": true, // don't warn that foo['bar'] should be written as foo.bar "sub": true, // don't warn that foo['bar'] should be written as foo.bar
"proto": true, // allow setting of __proto__ in node < v0.12, "proto": true, // allow setting of __proto__ in node < v0.12,
"esversion": 11 // allow es11(ES2020) "esversion": 6 // allow es6
} }

5
.nodemonignore Normal file
View File

@ -0,0 +1,5 @@
/Gruntfile.js
/.git/*
/lib/*
*.backup
/public/*

7
.npmignore Normal file
View File

@ -0,0 +1,7 @@
.settings
.jshintignore
.jshintrc
.project
.tern-project
.travis.yml
.git

20
.travis.yml Normal file
View File

@ -0,0 +1,20 @@
sudo: false
language: node_js
env:
- CXX="g++-4.8"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- gcc-4.8
node_js:
- "8"
- "6"
- "4"
script:
- istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage
before_script:
- npm install -g istanbul
- npm install coveralls

19
API.md
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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 Please raise any bug reports on the relevant project's issue tracker. Be sure to
search the list to see if your issue has already been raised. search the list to see if your issue has already been raised.
If your issue is more of a question on how to do something with Node-RED, please
consider using the [community forum](https://discourse.nodered.org/).
A good bug report is one that make it easy for us to understand what you were A good bug report is one that make it easy for us to understand what you were
trying to do and what went wrong. trying to do and what went wrong.
@ -29,34 +26,25 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog.
At a minimum, please include: 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-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 ## Feature requests
For feature requests, please raise them on the [forum](https://discourse.nodered.org). For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red).
## Pull-Requests ## Pull-Requests
If you want to raise a pull-request with a new feature, or a refactoring If you want to raise a pull-request with a new feature, or a refactoring
of existing code, please come and discuss it with us first. We prefer to of existing code, it may well get rejected if you haven't discussed it on
do it that way to make sure your time and effort is well spent on something the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
that fits with our goals.
If you've got a bug-fix or similar for us, then you are most welcome to All contributors need to sign the JS Foundation's Contributor License Agreement.
get it raised - just make sure you link back to the issue it's fixing and It is an online process and quick to do. You can read the details of the agreement
try to include some tests! here: https://cla.js.foundation/node-red/node-red.
All contributors need to sign the OpenJS Foundation's Contributor License Agreement. If you raise a pull-request without having signed the CLA, you will be prompted
It is an online process and quick to do. If you raise a pull-request without to do so automatically.
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 ### Coding standards

View File

@ -15,35 +15,17 @@
**/ **/
var path = require("path"); var path = require("path");
var fs = require("fs-extra");
var sass = require("sass");
module.exports = function(grunt) { module.exports = function(grunt) {
var nodemonArgs = ["-V"]; var nodemonArgs = ["-v"];
var flowFile = grunt.option('flowFile'); var flowFile = grunt.option('flowFile');
if (flowFile) { if (flowFile) {
nodemonArgs.push(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;
}
const pkg = grunt.file.readJSON('package.json');
process.env.NODE_RED_PACKAGE_VERSION = pkg.version;
grunt.initConfig({ grunt.initConfig({
pkg: pkg, pkg: grunt.file.readJSON('package.json'),
paths: { paths: {
dist: ".dist" dist: ".dist"
}, },
@ -55,8 +37,8 @@ module.exports = function(grunt) {
ui: 'bdd', ui: 'bdd',
reporter: 'spec' reporter: 'spec'
}, },
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] }, all: { src: ['test/**/*_spec.js'] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]}, core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]} nodes: { src: ["test/nodes/**/*_spec.js"]}
}, },
webdriver: { webdriver: {
@ -64,19 +46,18 @@ module.exports = function(grunt) {
configFile: 'test/editor/wdio.conf.js' configFile: 'test/editor/wdio.conf.js'
} }
}, },
nyc: { mocha_istanbul: {
options: { options: {
cwd: '.', globals: ['expect'],
include: ['packages/node_modules/**'], timeout: 3000,
excludeNodeModules: false, ignoreLeaks: false,
exclude: ['packages/node_modules/@node-red/editor-client/**'], ui: 'bdd',
reporter: ['lcov', 'html','text-summary'], reportFormats: ['lcov','html'],
reportDir: 'coverage', print: 'both'
all: true
}, },
all: { cmd: false, args: ['grunt', 'simplemocha:all'] }, all: { src: ['test/**/*_spec.js'] },
core: { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] }, core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] } nodes: { src: ["test/nodes/**/*_spec.js"]}
}, },
jshint: { jshint: {
options: { options: {
@ -92,20 +73,22 @@ module.exports = function(grunt) {
//"loopfunc": true, // allow functions to be defined in loops //"loopfunc": true, // allow functions to be defined in loops
//"sub": true // don't warn that foo['bar'] should be written as foo.bar //"sub": true // don't warn that foo['bar'] should be written as foo.bar
}, },
// all: [ all: [
// 'Gruntfile.js', 'Gruntfile.js',
// 'red.js', 'red.js',
// 'packages/**/*.js' 'red/**/*.js',
// ], 'nodes/core/*/*.js',
// core: { 'editor/js/**/*.js'
// files: { ],
// src: [ core: {
// 'Gruntfile.js', files: {
// 'red.js', src: [
// 'packages/**/*.js', 'Gruntfile.js',
// ] 'red.js',
// } 'red/**/*.js'
// }, ]
}
},
nodes: { nodes: {
files: { files: {
src: [ 'nodes/core/*/*.js' ] src: [ 'nodes/core/*/*.js' ]
@ -113,7 +96,7 @@ module.exports = function(grunt) {
}, },
editor: { editor: {
files: { files: {
src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ] src: [ 'editor/js/**/*.js' ]
} }
}, },
tests: { tests: {
@ -133,188 +116,142 @@ module.exports = function(grunt) {
src: [ src: [
// Ensure editor source files are concatenated in // Ensure editor source files are concatenated in
// the right order // the right order
"packages/node_modules/@node-red/editor-client/src/js/polyfills.js", "editor/js/red.js",
"packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js", "editor/js/events.js",
"packages/node_modules/@node-red/editor-client/src/js/red.js", "editor/js/i18n.js",
"packages/node_modules/@node-red/editor-client/src/js/events.js", "editor/js/settings.js",
"packages/node_modules/@node-red/editor-client/src/js/hooks.js", "editor/js/user.js",
"packages/node_modules/@node-red/editor-client/src/js/i18n.js", "editor/js/comms.js",
"packages/node_modules/@node-red/editor-client/src/js/settings.js", "editor/js/text/bidi.js",
"packages/node_modules/@node-red/editor-client/src/js/user.js", "editor/js/text/format.js",
"packages/node_modules/@node-red/editor-client/src/js/comms.js", "editor/js/ui/state.js",
"packages/node_modules/@node-red/editor-client/src/js/runtime.js", "editor/js/nodes.js",
"packages/node_modules/@node-red/editor-client/src/js/multiplayer.js", "editor/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js", "editor/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/text/format.js", "editor/js/ui/utils.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js", "editor/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/plugins.js", "editor/js/ui/common/checkboxSet.js",
"packages/node_modules/@node-red/editor-client/src/js/nodes.js", "editor/js/ui/common/menu.js",
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js", "editor/js/ui/common/panels.js",
"packages/node_modules/@node-red/editor-client/src/js/history.js", "editor/js/ui/common/popover.js",
"packages/node_modules/@node-red/editor-client/src/js/validators.js", "editor/js/ui/common/searchBox.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js", "editor/js/ui/common/tabs.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js", "editor/js/ui/common/stack.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", "editor/js/ui/common/typedInput.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js", "editor/js/ui/actions.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js", "editor/js/ui/deploy.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js", "editor/js/ui/diff.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js", "editor/js/ui/keyboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js", "editor/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js", "editor/js/ui/view.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", "editor/js/ui/sidebar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", "editor/js/ui/palette.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", "editor/js/ui/tab-info.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", "editor/js/ui/tab-config.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", "editor/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", "editor/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", "editor/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", "editor/js/ui/clipboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", "editor/js/ui/library.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js", "editor/js/ui/notifications.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", "editor/js/ui/search.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", "editor/js/ui/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js", "editor/js/ui/subflow.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js", "editor/js/ui/userSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js", "editor/js/ui/projects/projects.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js", "editor/js/ui/projects/projectSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", "editor/js/ui/projects/projectUserSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", "editor/js/ui/projects/tab-versionControl.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", "editor/js/ui/touch/radialMenu.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"
], ],
nonull: true, dest: "public/red/red.js"
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
}, },
vendor: { vendor: {
files: [ files: {
{ "public/vendor/vendor.js": [
src: [ "editor/vendor/jquery/js/jquery-1.11.3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js", "editor/vendor/bootstrap/js/bootstrap.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js", "editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js", "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js", "editor/vendor/marked/marked.min.js",
"node_modules/marked/marked.min.js", "editor/vendor/d3/d3.v3.min.js",
"node_modules/dompurify/dist/purify.min.js", "editor/vendor/i18next/i18next.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, "public/vendor/vendor.css": [
dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js" // TODO: resolve relative resource paths in
}, // bootstrap/FA/jquery
// {
// 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, "public/vendor/jsonata/jsonata.min.js": [
dest: "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js", "node_modules/jsonata/jsonata-es5.min.js",
}, "editor/vendor/jsonata/formatter.js"
{ ],
src: "node_modules/mermaid/dist/mermaid.min.js", "public/vendor/ace/worker-jsonata.js": [
nonull: true, "node_modules/jsonata/jsonata-es5.min.js",
dest: "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js", "editor/vendor/jsonata/worker-jsonata.js"
},
] ]
} }
}
}, },
uglify: { uglify: {
build: { build: {
files: { files: {
'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js', 'public/red/red.min.js': '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', 'public/red/main.min.js': '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', 'public/vendor/ace/mode-jsonata.js': 'editor/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/vendor/ace/snippets/jsonata.js': 'editor/vendor/jsonata/snippets-jsonata.js'
} }
} }
}, },
sass: { sass: {
build: { build: {
options: { options: {
implementation: sass,
outputStyle: 'compressed' outputStyle: 'compressed'
}, },
files: [{ files: [{
dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css', dest: 'public/red/style.min.css',
src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss' src: 'editor/sass/style.scss'
},
{
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
src: 'editor/vendor/bootstrap/css/bootstrap.css'
}] }]
} }
}, },
jsonlint: { jsonlint: {
messages: { messages: {
src: [ src: [
'packages/node_modules/@node-red/nodes/locales/**/*.json', 'nodes/core/locales/en-US/messages.json',
'packages/node_modules/@node-red/editor-client/locales/**/*.json', 'red/api/locales/en-US/editor.json',
'packages/node_modules/@node-red/runtime/locales/**/*.json' 'red/runtime/locales/en-US/runtime.json'
] ]
}, },
keymaps: { keymaps: {
src: [ src: [
'packages/node_modules/@node-red/editor-client/src/js/keymap.json' 'editor/js/keymap.json'
] ]
} }
}, },
attachCopyright: { attachCopyright: {
js: { js: {
src: [ src: [
'packages/node_modules/@node-red/editor-client/public/red/red.min.js', 'public/red/red.min.js',
'packages/node_modules/@node-red/editor-client/public/red/main.min.js' 'public/red/main.min.js'
] ]
}, },
css: { css: {
src: [ src: [
'packages/node_modules/@node-red/editor-client/public/red/style.min.css' 'public/red/style.min.css'
] ]
} }
}, },
clean: { clean: {
build: { build: {
src: [ src: [
"packages/node_modules/@node-red/editor-client/public/red", "public/red",
"packages/node_modules/@node-red/editor-client/public/index.html", "public/index.html",
"packages/node_modules/@node-red/editor-client/public/favicon.ico", "public/favicon.ico",
"packages/node_modules/@node-red/editor-client/public/icons", "public/icons",
"packages/node_modules/@node-red/editor-client/public/vendor", "public/vendor"
"packages/node_modules/@node-red/editor-client/public/types/node",
"packages/node_modules/@node-red/editor-client/public/types/node-red",
] ]
}, },
release: { release: {
@ -326,36 +263,30 @@ module.exports = function(grunt) {
watch: { watch: {
js: { js: {
files: [ 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: { sass: {
files: [ files: [
'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss' 'editor/sass/**/*.scss'
], ],
tasks: ['sass','attachCopyright:css'] tasks: ['sass','attachCopyright:css']
}, },
json: { json: {
files: [ files: [
'packages/node_modules/@node-red/nodes/locales/**/*.json', 'nodes/core/locales/en-US/messages.json',
'packages/node_modules/@node-red/editor-client/locales/**/*.json', 'red/api/locales/en-US/editor.json',
'packages/node_modules/@node-red/runtime/locales/**/*.json' 'red/runtime/locales/en-US/runtime.json'
], ],
tasks: ['jsonlint:messages'] tasks: ['jsonlint:messages']
}, },
keymaps: { keymaps: {
files: [ files: [
'packages/node_modules/@node-red/editor-client/src/js/keymap.json' 'editor/js/keymap.json'
], ],
tasks: ['jsonlint:keymaps','copy:build'] tasks: ['jsonlint:keymaps','copy:build']
}, },
tours: {
files: [
'packages/node_modules/@node-red/editor-client/src/tours/**/*.js'
],
tasks: ['copy:build']
},
misc: { misc: {
files: [ files: [
'CHANGELOG.md' 'CHANGELOG.md'
@ -367,13 +298,12 @@ module.exports = function(grunt) {
nodemon: { nodemon: {
/* uses .nodemonignore */ /* uses .nodemonignore */
dev: { dev: {
script: 'packages/node_modules/node-red/red.js', script: 'red.js',
options: { options: {
args: nodemonArgs, args: nodemonArgs,
ext: 'js,html,json', ext: 'js,html,json',
watch: [ watch: [
'packages/node_modules', 'red','nodes'
'!packages/node_modules/@node-red/editor-client'
] ]
} }
} }
@ -392,75 +322,69 @@ module.exports = function(grunt) {
build: { build: {
files:[ files:[
{ {
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js', src: 'editor/js/main.js',
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js' dest: 'public/red/main.js'
}, },
{ {
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json', src: 'editor/js/keymap.json',
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json' dest: 'public/red/keymap.json'
}, },
{ {
cwd: 'packages/node_modules/@node-red/editor-client/src/images', cwd: 'editor/images',
src: '**', src: '**',
expand: true, 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: [ src: [
'ace/**', 'ace/**',
'jquery/css/base/**', //'bootstrap/css/**',
'font-awesome/**', 'bootstrap/img/**',
'monaco/dist/**', 'jquery/css/**',
'monaco/types/extraLibs.js', 'font-awesome/**'
'monaco/style.css',
'monaco/monaco-bootstrap.js'
], ],
expand: true, expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' dest: 'public/vendor/'
}, },
{ {
cwd: 'packages/node_modules/@node-red/editor-client/src', cwd: 'editor/icons',
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',
src: '**', src: '**',
expand: true, expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/icons/' dest: 'public/icons/'
}, },
{ {
expand: true, expand: true,
src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'], src: ['editor/index.html','editor/favicon.ico'],
dest: 'packages/node_modules/@node-red/editor-client/public/', dest: 'public/',
flatten: true flatten: true
}, },
{ {
src: 'CHANGELOG.md', src: 'CHANGELOG.md',
dest: 'packages/node_modules/@node-red/editor-client/public/red/about' dest: '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/'
} }
] ]
},
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: { chmod: {
@ -468,96 +392,20 @@ module.exports = function(grunt) {
mode: '755' mode: '755'
}, },
release: { release: {
// Target-specific file/dir lists and/or options go here.
src: [ src: [
"packages/node_modules/@node-red/nodes/core/hardware/nrgpio", path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*')
"packages/node_modules/@node-red/runtime/lib/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: { compress: {
release: { release: {
options: { options: {
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip' archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
}, },
expand: true, expand: true,
cwd: 'packages/node_modules/', cwd: '<%= paths.dist %>/',
src: [ src: ['node-red-<%= pkg.version %>/**']
'**',
'!@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'
} }
} }
}); });
@ -570,42 +418,18 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-concurrent'); grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-sass'); grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-contrib-compress'); grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-chmod'); grunt.loadNpmTasks('grunt-chmod');
grunt.loadNpmTasks('grunt-jsonlint'); grunt.loadNpmTasks('grunt-jsonlint');
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { grunt.loadNpmTasks('grunt-mocha-istanbul');
grunt.loadNpmTasks('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.registerMultiTask('attachCopyright', function() { grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src; var files = this.data.src;
var copyright = "/**\n"+ var copyright = "/**\n"+
" * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+ " * Copyright JS Foundation and other contributors, http://js.foundation\n"+
" *\n"+ " *\n"+
" * Licensed under the Apache License, Version 2.0 (the \"License\");\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"+ " * you may not use this file except in compliance with the License.\n"+
@ -642,38 +466,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', grunt.registerTask('setDevEnv',
'Sets NODE_ENV=development so non-minified assets are used', 'Sets NODE_ENV=development so non-minified assets are used',
function () { function () {
@ -682,61 +474,37 @@ module.exports = function(grunt) {
grunt.registerTask('default', grunt.registerTask('default',
'Builds editor content then runs code style checks and unit tests on all components', 'Builds editor content then runs code style checks and unit tests on all components',
['build','verifyPackageDependencies','jshint:editor','nyc:all']); ['build','test-core','test-editor','test-nodes']);
grunt.registerTask('no-coverage',
'Builds editor content then runs code style checks and unit tests on all components without code coverage',
['build','verifyPackageDependencies','jshint:editor','simplemocha:all']);
grunt.registerTask('test-core', grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code', 'Runs code style check and unit tests on core runtime code',
['build','nyc:core']); ['build','mocha_istanbul:core']);
grunt.registerTask('test-editor', grunt.registerTask('test-editor',
'Runs code style check on editor code', 'Runs code style check on editor code',
['jshint:editor']); ['jshint:editor']);
if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
grunt.registerTask('test-ui', grunt.registerTask('test-ui',
'Builds editor content then runs unit tests on editor ui', 'Builds editor content then runs unit tests on editor ui',
['verifyUiTestDependencies']); ['build','jshint:editor','webdriver:all']);
} else {
grunt.registerTask('test-ui',
'Builds editor content then runs unit tests on editor ui',
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
}
grunt.registerTask('test-nodes', grunt.registerTask('test-nodes',
'Runs unit tests on core nodes', 'Runs unit tests on core nodes',
['build','nyc:nodes']); ['build','mocha_istanbul:nodes']);
grunt.registerTask('build', grunt.registerTask('build',
'Builds editor content', 'Builds editor content',
['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']); ['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', grunt.registerTask('dev',
'Developer mode: run node-red, watch for source changes and build/restart', 'Developer mode: run node-red, watch for source changes and build/restart',
['build','setDevEnv','concurrent:dev']); ['build','setDevEnv','concurrent:dev']);
grunt.registerTask('release', grunt.registerTask('release',
'Create distribution zip file', 'Create distribution zip file',
['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules','generatePublishScript']); ['build','clean:release','copy:release','chmod:release','compress:release']);
grunt.registerTask('pack-modules',
'Create module pack files for release',
['mkdir:release','npm-command']);
grunt.registerTask('coverage', grunt.registerTask('coverage',
'Run Istanbul code test coverage task', 'Run Istanbul code test coverage task',
['build','nyc:all']); ['build','mocha_istanbul:all']);
grunt.registerTask('docs',
'Generates API documentation',
['jsdoc']);
}; };

View File

@ -1,4 +1,4 @@
Copyright OpenJS Foundation and other contributors, https://openjsf.org/ Copyright JS Foundation and other contributors, http://js.foundation
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004

View File

@ -1,16 +1,17 @@
# Node-RED # Node-RED
https://nodered.org http://nodered.org
[![Build Status](https://github.com/node-red/node-red/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/node-red/node-red/actions?query=branch%3Amaster) [![Build Status](https://travis-ci.org/node-red/node-red.svg)](https://travis-ci.org/node-red/node-red)
[![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master)
Low-code programming for event-driven applications. A visual tool for wiring the Internet of Things.
![Node-RED: Low-code programming for event-driven applications](https://nodered.org/images/node-red-screenshot.png) ![Node-RED: A visual tool for wiring the Internet of Things](http://nodered.org/images/node-red-screenshot.png)
## Quick Start ## 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. started.
1. `sudo npm install -g --unsafe-perm node-red` 1. `sudo npm install -g --unsafe-perm node-red`
@ -19,9 +20,10 @@ started.
## Getting Help ## 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). For further help, or general discussion, please use the
[mailing list](https://groups.google.com/forum/#!forum/node-red).
## Developers ## Developers
@ -43,6 +45,9 @@ If you want to run the latest code from git, here's how to get started:
4. Run 4. Run
npm start npm start
or
node red.js
## Contributing ## Contributing
@ -55,15 +60,15 @@ This project adheres to the [Contributor Covenant 1.4](http://contributor-covena
## Authors ## 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) * Nick O'Leary [@knolleary](http://twitter.com/knolleary)
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
* And many others...
## Copyright and license ## 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).

View File

@ -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.

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# #
# Copyright JS Foundation and other contributors, http://js.foundation # Copyright JS Foundation and other contributors, http://js.foundation
# #
@ -29,16 +29,15 @@ do
done done
# Find the real location of this script # Find the real location of this script
CURRENT_PATH=$(pwd) CURRENT_PATH=`pwd`
SCRIPT_PATH=$(readlink -f "$0") SCRIPT_PATH="${BASH_SOURCE[0]}";
while [ -h "${SCRIPT_PATH}" ]; do while([ -h "${SCRIPT_PATH}" ]); do
cd "$(dirname "${SCRIPT_PATH}")" || exit 1 cd "`dirname "${SCRIPT_PATH}"`"
P=$(basename "${SCRIPT_PATH}") SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
SCRIPT_PATH=$(readlink "${P}")
done done
cd "$(dirname "${SCRIPT_PATH}")" > /dev/null || exit 1 cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH=$(pwd) SCRIPT_PATH="`pwd`";
cd "$CURRENT_PATH" || exit 1 cd $CURRENT_PATH
# Run Node-RED # 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

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

BIN
editor/icons/alert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

BIN
editor/icons/arduino.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

View File

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

BIN
editor/icons/batch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

BIN
editor/icons/bluetooth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

BIN
editor/icons/bridge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

BIN
editor/icons/cog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

BIN
editor/icons/comment.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

BIN
editor/icons/db.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

BIN
editor/icons/debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

BIN
editor/icons/envelope.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

BIN
editor/icons/feed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

BIN
editor/icons/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

BIN
editor/icons/function.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

BIN
editor/icons/hash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

BIN
editor/icons/inject.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

BIN
editor/icons/join.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
editor/icons/leveldb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
editor/icons/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

BIN
editor/icons/link-out.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

BIN
editor/icons/mongodb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
editor/icons/mouse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

BIN
editor/icons/node-error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

BIN
editor/icons/parser-csv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

BIN
editor/icons/parser-xml.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

BIN
editor/icons/range.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

BIN
editor/icons/redis.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

BIN
editor/icons/rpi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

BIN
editor/icons/serial.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

BIN
editor/icons/sort.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

BIN
editor/icons/split.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

BIN
editor/icons/subflow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

BIN
editor/icons/swap.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

BIN
editor/icons/switch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

BIN
editor/icons/template.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

BIN
editor/icons/timer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
editor/icons/trigger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

BIN
editor/icons/twitter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

BIN
editor/icons/watch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

BIN
editor/images/grip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
editor/images/node-red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

View File

@ -26,26 +26,8 @@ RED.comms = (function() {
var reconnectAttempts = 0; var reconnectAttempts = 0;
var active = false; 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() { function connectWS() {
active = true; 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 path = location.hostname;
var port = location.port; var port = location.port;
if (port.length !== 0) { if (port.length !== 0) {
@ -53,8 +35,7 @@ RED.comms = (function() {
} }
path = path+document.location.pathname; path = path+document.location.pathname;
path = path+(path.slice(-1) == "/"?"":"/")+"comms"; path = path+(path.slice(-1) == "/"?"":"/")+"comms";
wspath = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path; path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
}
var auth_tokens = RED.settings.get("auth-tokens"); var auth_tokens = RED.settings.get("auth-tokens");
pendingAuth = (auth_tokens!=null); pendingAuth = (auth_tokens!=null);
@ -65,10 +46,9 @@ RED.comms = (function() {
ws.send(JSON.stringify({subscribe:t})); ws.send(JSON.stringify({subscribe:t}));
} }
} }
emit('connect')
} }
ws = new WebSocket(wspath); ws = new WebSocket(path);
ws.onopen = function() { ws.onopen = function() {
reconnectAttempts = 0; reconnectAttempts = 0;
if (errornotification) { if (errornotification) {
@ -85,30 +65,21 @@ RED.comms = (function() {
} }
ws.onmessage = function(event) { ws.onmessage = function(event) {
var message = JSON.parse(event.data); var message = JSON.parse(event.data);
if (message.auth) { for (var m = 0; m < message.length; m++) {
if (pendingAuth) { var msg = message[m];
if (message.auth === "ok") { if (pendingAuth && msg.auth) {
if (msg.auth === "ok") {
pendingAuth = false; pendingAuth = false;
completeConnection(); completeConnection();
} else if (message.auth === "fail") { } else if (msg.auth === "fail") {
// anything else is an error... // anything else is an error...
active = false; active = false;
RED.user.login({updateMenu:true},function() { RED.user.login({updateMenu:true},function() {
connectWS(); connectWS();
}) })
} }
} else if (message.auth === "fail") {
// Our current session has expired
active = false;
RED.user.login({updateMenu:true},function() {
connectWS();
})
} }
} else { else if (msg.topic) {
// 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) { for (var t in subscriptions) {
if (subscriptions.hasOwnProperty(t)) { if (subscriptions.hasOwnProperty(t)) {
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
@ -124,7 +95,6 @@ RED.comms = (function() {
} }
} }
} }
}
}; };
ws.onclose = function() { ws.onclose = function() {
if (!active) { if (!active) {
@ -152,10 +122,10 @@ RED.comms = (function() {
connectWS(); connectWS();
} else { } else {
var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>'; var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>';
errornotification.update(msg,{silent:true}); errornotification.update(msg);
$(errornotification).find("a").on("click", function(e) { $(errornotification).find("a").click(function(e) {
e.preventDefault(); e.preventDefault();
errornotification.update(RED._("notification.errors.lostConnection"),{silent:true}); errornotification.update(RED._("notification.errors.lostConnection"));
clearInterval(connectCountdownTimer); clearInterval(connectCountdownTimer);
connectWS(); 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 { return {
connect: connectWS, connect: connectWS,
subscribe: subscribe, subscribe: subscribe,
unsubscribe:unsubscribe, unsubscribe:unsubscribe
on,
off,
send
} }
})(); })();

View File

@ -32,23 +32,17 @@
} }
} }
} }
function emit() { function emit(evt,arg) {
var evt = arguments[0]
var args = Array.prototype.slice.call(arguments,1);
if (RED.events.DEBUG) {
console.warn(evt,args);
}
if (handlers[evt]) { if (handlers[evt]) {
let cpyHandlers = [...handlers[evt]]; for (var i=0;i<handlers[evt].length;i++) {
for (var i=0;i<cpyHandlers.length;i++) {
try { try {
cpyHandlers[i].apply(null, args); handlers[evt][i](arg);
} catch(err) { } catch(err) {
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString())); console.log("RED.events.emit error: ["+evt+"] "+(err.toString()));
console.warn(err); console.log(err);
} }
} }
} }
} }
return { return {

330
editor/js/history.js Normal file
View File

@ -0,0 +1,330 @@
/**
* 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[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.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);
});
} 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
View 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();
}
}
});
})
}
}
})();

43
editor/js/keymap.json Normal file
View File

@ -0,0 +1,43 @@
{
"*": {
"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-e": "core:show-export-dialog",
"ctrl-i": "core:show-import-dialog",
"ctrl-space": "core:toggle-sidebar",
"ctrl-,": "core:show-user-settings",
"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"
}
}

437
editor/js/main.js Normal file
View File

@ -0,0 +1,437 @@
/**
* 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 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) {
$("body").append(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);
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":"Change to local branch '"+project.git.branches.local+"'",
"abort-merge":"Git merge aborted",
"loaded":"Project '"+msg.project+"' loaded",
"updated":"Project '"+msg.project+"' updated",
"pull":"Project '"+msg.project+"' reloaded",
"revert": "Project '"+msg.project+"' reloaded"
}[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: "Manage project dependencies",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.settings.show('deps');
}
}
]
// } else if (RED.settings.theme('palette.editable') !== false) {
} else {
options.buttons = [
{
text: "Close",
click: function() {
persistentNotifications[notificationId].hideNotification();
}
}
]
}
} else if (msg.error === "credentials_load_failed") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: "Setup credentials",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showCredentialsPrompt();
}
}
]
}
} else if (msg.error === "missing_flow_file") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: "Setup project files",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.showFilesPrompt();
}
}
]
}
} else if (msg.error === "project_empty") {
if (RED.user.hasPermission("projects.write")) {
options.buttons = [
{
text: "No thanks",
click: function() {
persistentNotifications[notificationId].hideNotification();
}
},
{
text: "Create default project files",
click: function() {
persistentNotifications[notificationId].hideNotification();
RED.projects.createDefaultFileSet();
}
}
]
}
}
}
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) {
$("body").append(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) {
$("body").append(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:"Projects",options:[
{id:"menu-item-projects-new",label:"New...",disabled:false,onselect:"core:new-project"},
{id:"menu-item-projects-open",label:"Open...",disabled:false,onselect:"core:open-project"}
]});
}
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"),disabled:true,options:[
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,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);
})
});
})();

1385
editor/js/nodes.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,5 +13,4 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
**/ **/
var RED = {};
$header-height: 48px;

View File

@ -19,6 +19,7 @@ RED.settings = (function () {
var loadedSettings = {}; var loadedSettings = {};
var userSettings = {}; var userSettings = {};
var settingsDirty = false;
var pendingSave; var pendingSave;
var hasLocalStorage = function () { var hasLocalStorage = function () {
@ -33,10 +34,10 @@ RED.settings = (function () {
if (!hasLocalStorage()) { if (!hasLocalStorage()) {
return; return;
} }
if (key.startsWith("auth-tokens")) { if (key === "auth-tokens") {
localStorage.setItem(key+this.authTokensSuffix, JSON.stringify(value)); localStorage.setItem(key, JSON.stringify(value));
} else { } else {
RED.utils.setMessageProperty(userSettings,key,value); userSettings[key] = value;
saveUserSettings(); saveUserSettings();
} }
}; };
@ -45,25 +46,16 @@ RED.settings = (function () {
* If the key is not set in the localStorage it returns <i>undefined</i> * If the key is not set in the localStorage it returns <i>undefined</i>
* Else return the JSON parsed value * Else return the JSON parsed value
* @param key * @param key
* @param defaultIfUndefined
* @returns {*} * @returns {*}
*/ */
var get = function (key,defaultIfUndefined) { var get = function (key) {
if (!hasLocalStorage()) { if (!hasLocalStorage()) {
return undefined; return undefined;
} }
if (key.startsWith("auth-tokens")) { if (key === "auth-tokens") {
return JSON.parse(localStorage.getItem(key+this.authTokensSuffix)); return JSON.parse(localStorage.getItem(key));
} else { } else {
var v; return userSettings[key];
try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {}
if (v === undefined) {
try { v = RED.utils.getMessageProperty(RED.settings,key); } catch(err) {}
}
if (v === undefined) {
v = defaultIfUndefined;
}
return v;
} }
}; };
@ -71,8 +63,8 @@ RED.settings = (function () {
if (!hasLocalStorage()) { if (!hasLocalStorage()) {
return; return;
} }
if (key.startsWith("auth-tokens")) { if (key === "auth-tokens") {
localStorage.removeItem(key+this.authTokensSuffix); localStorage.removeItem(key);
} else { } else {
delete userSettings[key]; delete userSettings[key];
saveUserSettings(); saveUserSettings();
@ -97,24 +89,18 @@ RED.settings = (function () {
userSettings = data; userSettings = data;
} }
var init = function (options, done) { var init = function (done) {
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search); var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
var path=window.location.pathname.slice(0,-1);
RED.settings.authTokensSuffix=path.replace(/\//g, '-');
if (accessTokenMatch) { if (accessTokenMatch) {
var accessToken = accessTokenMatch[1]; var accessToken = accessTokenMatch[1];
RED.settings.set("auth-tokens",{access_token: accessToken}); RED.settings.set("auth-tokens",{access_token: accessToken});
window.location.search = ""; window.location.search = "";
} }
RED.settings.apiRootUrl = options.apiRootUrl;
$.ajaxSetup({ $.ajaxSetup({
beforeSend: function(jqXHR,settings) { beforeSend: function(jqXHR,settings) {
// Only attach auth header for requests to relative paths // Only attach auth header for requests to relative paths
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) { if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
if (options.apiRootUrl) {
settings.url = options.apiRootUrl+settings.url;
}
var auth_tokens = RED.settings.get("auth-tokens"); var auth_tokens = RED.settings.get("auth-tokens");
if (auth_tokens) { if (auth_tokens) {
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token); jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);
@ -127,7 +113,7 @@ RED.settings = (function () {
load(done); load(done);
} }
var refreshSettings = function(done) { var load = function(done) {
$.ajax({ $.ajax({
headers: { headers: {
"Accept": "application/json" "Accept": "application/json"
@ -137,37 +123,23 @@ RED.settings = (function () {
url: 'settings', url: 'settings',
success: function (data) { success: function (data) {
setProperties(data); setProperties(data);
done(null, data); if (!RED.settings.user || RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
console.log("Node-RED: " + data.version);
loadUserSettings(done);
}, },
error: function(jqXHR,textStatus,errorThrown) { error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) { if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) { if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = ""; window.location.search = "";
} }
RED.user.login(function() { refreshSettings(done); }); RED.user.login(function() { load(done); });
} else { } else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus); console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
} }
} }
}); });
}
var load = function(done) {
refreshSettings(function(err, data) {
if (!err) {
if (!RED.settings.user || RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
console.log("Node-RED: " + data.version);
console.groupCollapsed("Versions");
console.log("jQuery",$().jquery)
console.log("jQuery UI",$.ui.version);
if(window.ace) { console.log("ACE",ace.version); }
if(window.monaco) { console.log("MONACO",monaco.version || "unknown"); }
console.log("D3",d3.version);
console.groupEnd();
loadUserSettings(done);
}
})
}; };
function loadUserSettings(done) { function loadUserSettings(done) {
@ -228,28 +200,14 @@ RED.settings = (function () {
return defaultValue; return defaultValue;
} }
} }
function getLocal(key) {
return localStorage.getItem(key)
}
function setLocal(key, value) {
localStorage.setItem(key, value);
}
function removeLocal(key) {
localStorage.removeItem(key)
}
return { return {
init: init, init: init,
load: load, load: load,
loadUserSettings: loadUserSettings, loadUserSettings: loadUserSettings,
refreshSettings: refreshSettings,
set: set, set: set,
get: get, get: get,
remove: remove, remove: remove,
theme: theme, theme: theme
setLocal: setLocal,
getLocal: getLocal,
removeLocal: removeLocal
} }
})(); })();

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