Compare commits

..

4 Commits

Author SHA1 Message Date
Nick O'Leary
841438793d Tidy up merge of #1026
Closes #1026
2017-02-10 21:51:42 +00:00
Nick O'Leary
e0ac565bab Merge branch 'ashrafg-bidi' into bidi 2016-12-05 14:11:19 +00:00
samarsultan
e5511ea86d Rework bidi file structure
Closes #1026,#983,#982,#978

PR #1026 contains the full history of the changes made herein.
Due to the volume of commits with no meaningful comment, they
have been squashed down to this one.

Adding bidi Files

Tuning code style

Adding rest of bidi files and its dependencies

Tuning code style

editing numeric shaping value

Tuning Code style

adding bidi support

Adding Bidi Support for the rest of files

Adding Bidi support

Editing Bidi menu

adding mirroring enablement

handeling code style

Addinng Bidi Support

Adding Bidi Support

Adding locale settings to national calendar support

Adding Numeric Shaping Support

scss files after adding rtl direction part 1

adding rtl direction at scss files part2

adding right directionality at comman typeInput

correcting some comments

editing spaces

applying code style

editing code style

editing code style

editing code style

editing code style

adding right directionality

adding right directionality

adding style in case of right directionality

adding style in case of right directionality

adding style in case of right directionality

create a global variable for ui direction to call it once need

Update main.js

Update typedInput.js

Update 10-switch.html

manage palette mirroring

adding RTL directionality to Tabs and Manage palette

Style Editing

Adding mirroring to subflow workspace

handle mirroring defects at manage palette

Handle Mirroring defects at sidebar seperator

Numeric Shaping Updates

Editing code style

Handling mirroring defects

Handling mirroring defects

Fixing mirroring defects

editing code style

fixing mirroring defects at deploy dialog

editing code style

Updating Bidi Support

handling some reviewing comments

handling chicks

Update base-text-dir.js

Update bidi-util.js

Update bidi-util.js

Handling reviewing comment

Fixing Popover mirroring defect

Handling namespace structure for bidi features

reflecting new namespace structure for bidi features at editorfiles

Handling comments that related to css

moving bidi.js under js/bidi folder
2016-12-05 14:10:31 +00:00
wajnberg
b67df2c0ab Fixing issue 1024 (#1041)
Signed-off-by: Moshe Wajnberg <wajnberg@il.ibm.com>
2016-11-16 14:11:27 +00:00
1698 changed files with 53079 additions and 290540 deletions

1
.gitattributes vendored
View File

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

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,34 +0,0 @@
<!--
## Before you hit that Submit button....
Please read our [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
before submitting a pull-request.
## Types of changes
What types of changes does your code introduce?
Put an `x` in the boxes that apply
-->
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
<!--
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
the [forum](https://discourse.nodered.org) or
[slack team](https://nodered.org/slack) first.
-->
## Proposed changes
<!-- Describe the nature of this change. What problem does it address? -->
## Checklist
<!-- Put an `x` in the boxes that apply -->
- [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md)
- [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team.
- [ ] I have run `npm run test` to verify the unit tests pass
- [ ] 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@v3
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@v2
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
path: 'node-red-docker'
commit-message: 'Bump to ${{ env.newVersion }}'
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
body: |
Updates the Node-RED Docker repo for the ${{ env.newVersion }} release.
Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`.
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
- run: node ./node-red/.github/scripts/update-node-red-website.js
- name: Create Website Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.NR_REPO_TOKEN }}
committer: GitHub <noreply@github.com>
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
path: 'node-red.github.io'
commit-message: 'Bump to ${{ env.newVersion }}'
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
body: |
Updates the Node-RED Website repo for the ${{ env.newVersion }} release.
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary

View File

@@ -1,36 +0,0 @@
name: Run tests
on:
push:
branches: [ master, dev ]
pull_request:
branches: [ master, dev ]
permissions:
contents: read
jobs:
build:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm install
- name: Run tests
run: |
npm run test
# - name: Publish to coveralls.io
# if: ${{ matrix.node-version == 16 }}
# uses: coverallsapp/github-action@v1.1.2
# with:
# github-token: ${{ github.token }}

11
.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,12 +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

View File

@@ -2,9 +2,6 @@
"asi": true, // allow missing semicolons "asi": true, // allow missing semicolons
"curly": true, // require braces "curly": true, // require braces
"eqnull": true, // ignore ==null "eqnull": true, // ignore ==null
//"eqeqeq": true, // enforce ===
"freeze": true, // don't allow override
"indent": 4, // default indent of 4
"forin": true, // require property filtering in "for in" loops "forin": true, // require property filtering in "for in" loops
"immed": true, // require immediate functions to be wrapped in ( ) "immed": true, // require immediate functions to be wrapped in ( )
"nonbsp": true, // warn on unexpected whitespace breaking chars "nonbsp": true, // warn on unexpected whitespace breaking chars
@@ -12,8 +9,6 @@
//"unused": true, // Check for unused functions and variables //"unused": true, // Check for unused functions and variables
"loopfunc": true, // allow functions to be defined in loops "loopfunc": true, // allow functions to be defined in loops
//"expr": true, // allow ternery operator syntax... //"expr": true, // allow ternery operator syntax...
"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)
} }

View File

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

7
.npmignore Normal file
View File

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

24
.travis.yml Normal file
View File

@@ -0,0 +1,24 @@
sudo: false
language: node_js
env:
- CXX="g++-4.8"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- gcc-4.8
matrix:
allow_failures:
- node_js: "7"
node_js:
- "7"
- "6"
- "4"
- "0.10"
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

View File

@@ -1,501 +1,498 @@
#### 3.1.0: Milestone Release #### 0.15.1: Maintenance Release
Editor - Update default palette catalogue to use https
- Disable palette editor if npm not found - and fix for Windows
- Searching package catalogue should be case-insensitive Fixes #1010
- contenteditable fields not handled in config nodes Fixes #1011
- Change html link refs from `_new` to `_blank` to be standards compliant
- Default filter to All Catalogues and show nodes for small lists (#4318) @knolleary #### 0.15.0: Milestone Release
- Better distinguish between ctrl and meta keys on mac (#4310) @knolleary
- Ensure junction appears when filtering quick-add list (#4297) @knolleary
- Update message catalogs for JSONata Expression editor (#4287) @kazuhitoyokoi
- Add tooltip to relevance sort button in user settings UI (#4288) @kazuhitoyokoi
- Capture workspace dirty state when quick-adding junction (#4283) @knolleary
- Add docs for $clone function (#4284) @knolleary
Runtime Runtime
- Dependency updates (#4317) @knolleary - Increase default apiMaxLength to 5mb and add to default settings Closes #1001
- Ensure storage/util.writeFile handles concurrent write attempts (#4316) @knolleary - Add v2 /flows api and deploy-overwrite protection
- Migrate http -> https for nodered.org (#4313) @Rotzbua - Encrypt credentials by default
- Add Node 20 to GH Action test matrix (#4305) @Rotzbua - Ensure errors thrown by RED.events handlers don't percolate up
- Handle group-scoped nodes inside subflow (#4301) @knolleary
- Handle non-url-safe chars in context api (#4298) @knolleary
- Fix git pull operation in project feature (#4290) @kazuhitoyokoi
- Change linefeed codes in Korean message catalogs (#4286) @kazuhitoyokoi
- Fix file permissions of message catalogs (#4285) @kazuhitoyokoi
- Update tour (#4278) @knolleary
Nodes
- File: Fix handling in file nodes when number is specified as file name (#4267) @kazuhitoyokoi
- Function: Adding function timeout to settings file (#4265) (#4309) @knolleary
- Function: Fix function setup tab layout (#4299) @knolleary
- HTTP Request: Handle 204 in httprequest JSON (#4262) @sammachin
- JSON: Fix test cases of JSON node (#4275) @kazuhitoyokoi
- MQTT: Remove unnecessary check for clientid if autoUnsub set (#4302) @knolleary
##### 3.1.0-beta.4: Beta Release
Editor
- Add Japanese translation for 3.1.0 (#4252) @kazuhitoyokoi
- Improve Catalogue visibility (#4248) @Steve-Mcl
- Add support for wiring and moving junctions on touch device (#4244) @Steve-Mcl
- Show errors and statuses of config nodes in the sidebar when no catch node is available (#4231) @bvmensvoort
- Improve wiring for horizontally aligned nodes (#4232) @knolleary
- French translation of Welcome Tours (#4200) @GogoVega
- French translation of v3.1.0-beta.3 changes (#4199) @GogoVega
- add Japanese message for 3.1.0 beta 3 (#4209) @HiroyasuNishiyama
- Dont clone the group nodes `node` array when saving edits (#4208) @Steve-Mcl
Runtime
- Add NR_SUBFLOW_NAME/ID/PATH env vars (#4250) @knolleary
- Evaluate all env vars as part of async flow start (#4230) @knolleary
- Add support for httpStatic middleware (#4229) @knolleary
Nodes
- Fix JSONata in file nodes (#4246) @kazuhitoyokoi
- Fix timeout icon in function and link call nodes (#4253) @kazuhitoyokoi
- Fix connection keep-alive in http request node (#4228) @knolleary
- adding timeout attribute to function node (#4177) @k1ln
- Fix manual mode join when multiple sequences being handled (#4143) @BitCaesar
- Fix delay node flush issue (#4203) @dceejay
- Update status and catch node labels in group mode (#4207) @Steve-Mcl
##### 3.1.0-beta.3: Beta Release
Editor Editor
- Select the item that is specified in a deep link URL (#4113) @Steve-Mcl - Mark nodes as changed when they are moved
- Update to Monaco 0.38.0 (#4189) @Steve-Mcl - Added parent containment option for draggable. (#1006)
- Place subflow outputs/inputs relative to current view (#4183) @knolleary - Ignore bidi event handling on non-existent and non-Input elements Closes #999
- Enable RED.view.select to select group by id (#4184) @knolleary - Remove list of flows from menu
- Combine existing env vars when merging groups (#4182) @knolleary - Allow nodes to be imported with their credentials
- Avoid creating empty global-config node if not needed (#4153) @knolleary - Add workspace search option
- Fix group selection when using lasso (#4108) @knolleary - Add scrollOnAdd option to editableList
- Use editor path in generating localStorage keys (#4151) @mw75 - Add swift markup to editor for open whisk node
- Ensure no node credentials are included when exporting to clipboard (#4112) @knolleary - Scrollable tabs 👍
- Fix jsonata expression test ui (#4097) @knolleary - Allow linking to individual flow via url hash
- Fix search button in palette popover (#4096) @knolleary - Avoid duplicating existing subflows on import
- Add import-to-new-tab option
- Add new options to export-nodes dialog
- Stop nodes being added beyond the outer bounds of the workspace
- Default config nodes to global scope unless in a subflow Closes #972
- Bidi support for Text Direction and Structured Text (#961)
- Fix jQuery selector, selecting more than one help pane/popover and displaying incorrectly. (#970)
- Fixes removeItem not passing row data to callback. (#965)
- Move common components and add searchBox
- Add initial palette sidebar
Nodes
- Inject node label - show topic for timestamp mode if short
- Let change node set type if total match
- Clean up status on close for several core nodes.
- Change node: re-parse JSON set value each time to avoid pass-by-ref
- Better handle HTTP Request header capitalisation
- Enable ES6 parsing in Function editor by default Fixes #985
- Update debug sidebar to use RED.view.reveal to show debug nodes
- Add full path tip to file node, And tidy up Pi node tips
- Remove WebSocket node maxlistener warning
- Update mqtt-broker node to use fully name-space qualified status messages
- Let UDP node better share same port instance if required
- Add number of units to the delay node (rate) (#994)
- Allow http middleware to skip rawBodyParser
- Let change node move property to sub-property.
- Add info to exec warning about buffered output if using python
- TCP node: pass on latest input msg properties
- Make sure MQTT broker is really set
- Fix escape character catch in TCPGet + support 0x?? sequences
- Fix split character in TCP Request node
- Add CSS highlighting to the template node (#950)
- Only update switch previous value after all rules are run
Other
- Add npm build/test scripts Closes #946 #660
- Move travis to node 6 and 7 - drop 5 and 0.12
#### 0.14.6: Maintenance Release
Fixes
- Tell ace about Function node globals. Closes #927
- Tidy up mqtt nodes - linting and done handling. Closes #935
- Fix invalid html in TCP and HTML node edit templates
- Add proper help text to link nodes
- Handle importing old mqtt-broker configs that lack properties
- Update ace to 1.2.4
- Allow config nodes to provide a sort function for their select list
- Add log warning if node module required version cannot be satisfied
- Handle empty credentials file. Closes #937
- Add RPi.GPIO lib test for ArchLinux
#### 0.14.5: Maintenance Release
Fixes
- Cannot clear cookies with http nodes
- let HTML parse node allow msg.select set select
- Validate nodes on import after any references have been remapped
- Debug node handles objects without constructor property Fixes #933
- Ensure 'false' property values are displayed in info panel Fixes #940
- Fix node enable/disable over restart - load configs after settings init
#### 0.14.4: Maintenance Release
Nodes
- Update trigger node ui to use typedInputs
- Better handling of quotes in CSV node
- Clarify the MQTT node sends msg.payload - closes #929
- Inject node should reuse the message it is triggered with Closes #914
- Stop trigger node re-using old message
- Allow node.status text to be 'falsey' values
Fixes
- Handle DOMException when embedded in an iframe of different origin Fixes #932
- Fix double firing of menu actions
- Fix select box handling in Safari - fixes #928
- Clear context in node test helper Fixes #858
- Allow node properties to be same as existing object functions Fixes #880
- Handle comms link closing whilst completing the initial connect
- Protect against node type names that clash with Object property names Fixes #917
- Clone default node properties to avoid reference leakage
- Strip tab node definition when exporting
- Check for null config properties in editor before over-writing them
- Add hasUsers flag to config nodes
Editor
- Add sql mode to ace editor
- Keyboard shortcuts dialog update (#923)
- Ensure importing link nodes to a subflow doesn't add outbound links Fixes #921
- Add updateConfigNodeUsers function to editor
- Scroll to bottom when item added to editableList
- Form input widths behave more consistently when resizing Fixes #919 #920
#### 0.14.3: Maintenance Release
Fixes
- Create default setting.js in user-specified directory. Fixes #908
- MQTT In subscription qos not defaulting properly
- Let exec node handle 0 as well as "0"
#### 0.14.2: Maintenance Release
Fixes
- Cannot add new twitter credentials. Fixes #913
- Support array references in Debug property field
#### 0.14.1: Maintenance Release
Fixes
- Handle undefined property that led to missing wires in the editor
- Remove duplicate 'Delete' entry in keyboard shortcut window. Closes #911
- Add 'exec' to node-red-pi launch script. Closes #910
#### 0.14.0: Milestone Release
Editor
- Replace edit dialog with edit tray
- Enable shift-drag detach of just the selected link
- Allow workspace tabs to be re-ordered
- Scope keyboard shortcuts to dom elements
- Ensure parent nodes marked as changed due to child config node changes
- Validate all edit dialog inputs when one changes
- Add editableList widget and update Switch/Change nodes to use it
- Add option to filter Debug sidebar by flow and highlight subflow-emitting nodes
- Back off comms reconnect attempts after prolonged failures
- Prompt for login if comms reconnect fails authentication
- Change style of nodes in subflow template view
- Add CHANGELOG.md and make it accessible from menu
Runtime Runtime
- Allow options object on each httpStatic configuration (#4109) @kevinGodell - Always log node warnings on start without requiring -v
- Ensure non-zero exit codes for errors (#4181) @knolleary - Add support for loading scoped node modules. Closes #885
- Ensure external modules are installed synchronously (#4180) @knolleary - Add process.env.PORT to settings.js
- Update dependecies include got (#4155) @knolleary - Clear node context on deploy. Closes #870
- Add Japanese translations for v3.1 beta.2 (#4158) @kazuhitoyokoi - Enable finer grained permissions in adminAuth
- Ensure express server options are applied consistently (#4178) @knolleary
- Remove version info from theme endpoint (#4179) @knolleary
- Add Japanese translations for welcome tour of 3.1.0 beta.2 (#4145) @kazuhitoyokoi
- Added SHA-256 and SHA-512-256 digest authentication (#4100) @sroebert
- Add "timers" types to known types (#4103) @Steve-Mcl
Nodes Nodes
- Allow Catch/Status nodes to be scoped to their group (#4185) @NetHans - Enable config nodes to reference other config nodes
- MQTT: Option to disable MQTT topic unsubscribe on disconnect (#4078) @flying7eleven - Add Split/Join nodes
- Add Link nodes
- Add support to HTTP In node for PATCH requests. Closes #904
##### 3.1.0-beta.2: Beta Release - Add cookie handling to HTTP In and HTTP Response nodes
- Add repeat indicator to inject node label. Closes #887
Editor - Add javascript highlighter to template node
- Add optional timeout to exec node
- NEW: Add change icon to tabs (#4068) @knolleary - Add TLS node and update MQTT/HTTP nodes to use it
- NEW: Complete overhaul of Group UX (#4079) @knolleary - Let trigger node also send last payload to arrive
- NEW: Add link to node help in node edit dialog footer (#4065) @knolleary - Add timestamp as a default typedInput and update Inject and change nodes to match,
- NEW: Added editor feature for connecting multiple nodes to single node (#4051) @sonntam - Add QoS option to MQTT In node
- NEW: Increase workspace size to 8000x8000 (#4094) @knolleary - Add status to exec spawn mode
- Ensure node buttons are redrawn when flow lock state is changed (#4091) @knolleary - Add Move capability to Change node
- Prevent loops being created with junction nodes (#4087) @knolleary - Update Serial node to support custom baud rates
- Prevent opening locked node's edit dialog (#4069) @knolleary - Add support for array-syntax in typedInput msg properties
- Reverse direction of tab scroll to expected direction (#4064) @knolleary - Add RED.util to Function node sandbox
- Add cancel operation to editableList (#4077) @HiroyasuNishiyama - Capture error stack on node.error. Closes #879
- Apply Mermaid diagram for project settings UI (#4054) @kazuhitoyokoi
- Add tooltip for show/hide button on info sidebar (#4050) @kazuhitoyokoi
- Fix align nodes on locked tab (#4072) @HiroyasuNishiyama Fixes
- Fix importing connected link nodes into a subflow (#4082) @knolleary
- Fix to add empty marker to empty group (#4060) @HiroyasuNishiyama - Add error handling to all node definition api calls
- Fix image URLs for v3.0 tour (#4053) @kazuhitoyokoi - Handle null return from Function node in array of messages
- Show scrollbar in notification dialog only when needed (#4048) @kazuhitoyokoi - Defer loading of token sessions until they are accessed. Fixes #895
- Update-monaco-and-typings (#4089) @Steve-Mcl - set pi gpio pin status correctly if set on start
- Update jquery UI (#4088) @knolleary - Prevent parent window scrolling when view is focused. Fixes #635
- Support i18n of lock/unlock buttons in flow property UI (#4049) @kazuhitoyokoi - Handle missing tab nodes in a loaded flow config
- Translation kr (#3895) @hae-iotplatform - Ensure typedInput dropdown doesn't fall off the page
- Translation zhcn (请懂中文的帮忙review) (#3952) @cliyr - Protect against node types with reserved names such as toString. Fixes #880
- Add French translation of nodes (#3964) @GogoVega - Do not rely on the HTML file to identify where nodes are registered from
- Add French translation (#3962) @GogoVega - Preserve node properties on import
- Portuguese Brazilian (pt-BR) translation (#3804) @FabsMuller - Fix regression in delay node. topic based queue was emptying all the time instead of spreading out messages.
- Throw an error if a Function node adds an input event listener
- Fix hang on partial deploy with disconnected mqtt node
Runtime - TypedInput: preload type icons to ensure width calc correct
- Ensure tcp node creates a buffer of size 1 at least
- NEW: Generate stable ids for subflow instance internal nodes (#4093) @knolleary - Return editorTheme default if value is undefined
- NEW: Change default file name to flows.json in project feature (#4073) @kazuhitoyokoi - Fix RED.util.compareObjects for Function created objects and Buffers
- NEW: Deprecate synchronous access to jsonata (#4090) @knolleary - Ensure default settings copied to command-line specified userDir
- Add Node 18 to test matrix (#4084) @knolleary
- Bump minimum nodejs version supported to match documented value (#4086) @knolleary
- Update monaco docs link in settings.js (#4075) @Steve-Mcl #### 0.13.4: Maintenance Release
- Remove duplicated messages in the message catalog (#4066) @kazuhitoyokoi
- Ensure errors in preDeliver callback are handled (#3911) @knolleary - Add timed release mode to delay node
- Fix "EADDRINUSE" error (#4046) @bggbr - Enable link splicing for when import_dragging nodes. Closes #811
- Fix uncaught exception on deploy whilst node sending messages
Nodes - Deprecate old mqtt client and connection pool modules
- Change node: add bool/num types to change mode Closes #835
- Link Call: Clear link-call timeouts when node is closed (#4085) @knolleary - Validate fields that are `$(env-vars)` Closes #825
- Join: ensure inflight status is cleared when in auto mode (#4083) @knolleary - Handle missing config nodes when validating node properties
- File Out: Fix extra newline append for multipart file write (#3915) @dceejay - Pi node - don't try to send data if closing
- Add validators for complete and link call nodes (#4056) @kazuhitoyokoi - Load node message catalog when added dynamically
- Split palette labels on spaces and hyphens when laying out
##### 3.1.0-beta.1: Beta Release - Warn if editor routes are accessed but runtime not started Closes #816
- Better handling of zero-length flow files Closes #819
Editor - Allow runtime calls to RED._ to specify other namespace
- Better right alignment of numerics in delay and trigger nodes
- NEW: Locking Flows (#3938) @knolleary - Allow node modules to include example flows
- NEW: Improve UX around hiding flows via context menu (#3930) @knolleary - Create node_modules in userDir
- NEW: Add support for inline image in markdown editor by drag and drop of an image file (#4006) @HiroyasuNishiyama - Ensure errors in node def functions don't break view rendering Fixes #815
- NEW: Add support for mermaid diagram to markdown editor (#4007) @HiroyasuNishiyama - Updated Inject node info with instructions for flow and global options
- NEW: Support uri fragments for nodes and groups including edit support (#3870) @knolleary
- NEW: Add global environment variable feature (#3941) @HiroyasuNishiyama
- Remember compact/pretty flow export user choice (#3974) @Steve-Mcl #### 0.13.3: Maintenance Release
- fix .red-ui-notification class (#4035) @xiaobinqt
- Fix border radius on Modules list header (#4038) @bonanitech - Fix crash on repeated inject of invalid json payload
- fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama - Add binary mode to tail node
- Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama - Revert Cheerio to somewhat smaller version
- Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama - Add os/platform info to default debug
- fix hide subflow tooltip (#4033) @HiroyasuNishiyama
- Fix disabled menu items in project feature (#4027) @kazuhitoyokoi
- Let themes change radialMenu text colors (#3995) @bonanitech
- Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi #### 0.13.2: Maintenance Release
- Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi
- Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi - Don't force reconnect mqtt client if message arrives (fixes the MQTT connect/disconnect endless cycle)
- Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi - Add -p/--port option to override listening port
- Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary - Invert config node filter toggle button colours so state is more obvious
- Handle replacing unknown node inside group or subflow (#3921) @knolleary - Add timeout to httprequest node
- Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo - Tidy up of all node info content - make style consistent
- i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama - Make jquery spinner element css consistent with other inputs
- add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama - tcp node add reply (to all) capability
- Fix autocomplete entry for responseUrl (#3884) @knolleary - Allow the template node to be treated as plain text
- Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama - Validate MQTT In topics Fixes #792
- Fix search type with spaces (#3841) @Steve-Mcl - httpNodeAuth should not block http options requests Fixes #793
- Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama - Disable perMessageDeflate on WS servers - fixes 'zlib binding closed' error
- Add button type to the adding SSH key button (#3866) @kazuhitoyokoi - Clear trigger status icon on re-deploy
- Check radio button as default in project dialog (#3879) @kazuhitoyokoi - Don't default inject payload to blank string
- Add $clone as supported function (#3874) @HiroyasuNishiyama - Trigger node, add configurable reset
- Env var jsonata (#3807) @HiroyasuNishiyama - Allow function properties in settings Fixes #790 - fixes use of httpNodeMiddleware
- Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi - Fix order of config dialog calls to save/creds/validate
- Add debounce to Pi GPIO node
Runtime
- Force IPv4 name resolution to have priority (#4019) @dceejay
- Fix async loading of modules containing both nodes and plugins (#3999) @knolleary #### 0.13.1: Maintenance Release
- Use main branch as default in project feature (#4036) @kazuhitoyokoi
- Rename package var to avoid strict mode error (#4020) @knolleary - Revert wrapping of http request object
- Fix typos in settings.js (#4013) @ypid
- Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary
- Ignore commit error in project feature (#3987) @kazuhitoyokoi
- Update dependencies (#3969) @knolleary #### 0.13.0: Milestone Release
- Add check that node sends object rather than primitive type (#3909) @knolleary
- Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary - Add 'previous value' option to Switch node
- Fix nodesDir scan when node package has js/html in sub dir to package.json (#3867) @Steve-Mcl - Allow existing nodes to splice into links on drag
- Fix file permissions (#3917) @kazuhitoyokoi - CORS not properly configured on multiple http routes Fixes #783
- ci: add minimum GitHub token permissions for workflows (#3907) @boahc077 - Restore shift-drag to snap/unsnap to grid
- Moving nodes with keyboard should flag workspace dirty
Nodes - Notifications flagged as fixed should not be click-closable
- Rework config sidebar and deploy warning
- Catch: fix typo in catch.html (#3965) @we11adam - Wrap http request object to match http response object
- Change: Fix change node overwriting msg with itself (#3899) @dceejay - Add 'view' menu and reorganise a few things
- Comment node: Clarify where the text will appear (#4004) @dirkjanfaber - Allow shift-click to detach existing wires
- CSV: change replace to replaceAll (#3990) @dceejay - Splice nodes dragged from palette into links
- CSV node: check header properties for ' and " (#3920) @dceejay - try to trim imported/dragged flows to [ ]
- CSV: Fix for CSV undefined property (#3906) @dceejay - Move version number as title of NR logo
- Delay: let delay node handle both flush then reset (#3898) @dceejay - Moving nodes mark workspace as dirty
- Function: Limit number of ports in function node (#3886) @kazuhitoyokoi - Ok/Cancel edit dialogs with Ctrl-Enter/Escape
- Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi - Handle OSX Meta key when selecting nodes
- Function: add function node monaco types util and promisify (#3868) @Steve-Mcl - Add grid-alignment options
- HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary - Add oneditresize function definition
- HTTP Request: Support form-data arrays (#3991) @hardillb - Rename propertySelect to typedInput and add boolean opt
- HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary - Add propertySelect to switch node
- HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb - Add propertySelect support to Change node
- HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi - Add context/flow/global support to Function node
- HTTP Response: Ensure statusCode is a number (#3894) @hardillb - Add node context/flow/global
- Inject: Allow Inject node to work with async context stores (#4021) @knolleary - Add propertySelect jquery widget
- Join/Batch: Add count to join and batch node labels (#4028) @dceejay - Add add/update/delete flow apis
- MQTT: Fix birth topic handling in MQTT node (#3905) @Steve-Mcl - Allow core nodes dir to be provided to runtime via settings
- MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi - Tidy up API passed to node modules
- MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl - Move locale files under api/runtime components
- MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl - Add flow reload admin api
- MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary
- MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl
- Range: Add drop mode to range node (#3935) @dceejay
- Remove done from describe (#3873) @HiroyasuNishiyama #### 0.12.5: Maintenance Release
- Split node: avoid duplicate done call for buffer split (#4000) @knolleary
- Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi - Add attribute capability to HTML parser node
- TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay - Add Pi Keyboard code node
- Template: Add information about environment variable to template node (#3882) @kazuhitoyokoi - Fix for MQTT client connection cycling on partial deploy
- Trigger: Hide trigger node repeat send option if sending nothing (#4023) @dceejay - Fix for tcp node properly closing connections
- Watch: fix watch node test on MacOS/ARM (#3942) @HiroyasuNishiyama - Update sentiment node dependencies
- Fix for file node handling of UTF8 extended characters
#### 3.0.2: Maintenance Release
Editor
#### 0.12.4: Maintenance Release
- Fix workspace chart bottom property (#3812) @bonanitech
- Update german translation (#3802) @Dennis14e - Add readOnly setting to prevent file writes in localfilesystem storage
- Support color reset to the default in subflow and group (#3801) @kazuhitoyokoi - Support bcrypt for httpNodeAuth
- Allow generateNodeNames to handle names containing regex control chars (#3817) @knolleary - Pi no longer needs root workaround to access gpio
- Hide scrollbars until they're needed (#3808) @bonanitech - Fix: Input File node will not retain the file name
- Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary
- remove console.log (#3820) @Steve-Mcl
Runtime #### 0.12.3: Maintenance Release
- Register subflow module instance node with parent flow (#3818) @knolleary - Fixes for TCP Get node reconnect handling
- Clear delay node status on re-deploy
Nodes - Update Font-Awesome to v4.5
- Fix trigger to block properly until reset
- HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb - Update example auth properties in settings.js
- Ensure httpNodeAuth doesn't get applied to admin routes
#### 3.0.1: Maintenance Release - TCP Get node not passing on existing msg properties
Editor
- Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl #### 0.12.2: Maintenance Release
- Sys info (diagnostics report) amendments (#3793) @Steve-Mcl
- Allow `mode` and `title` to be omitted in `options` argument for `createEditor` (#3791) @Steve-Mcl - Enable touch-menu for links so they can be deleted
- Fix focus issues (#3789) @Steve-Mcl - Allow nodes to be installed by path name
- Ensure all typedInput buttons have button type set (#3788) @knolleary - Fix basic authentication on httpNode/Admin/Static
- Do not flag hasUsers=false nodes as unused in search (#3787) @knolleary - Handle errors thrown in Function node setTimeout/Interval
- Properly position quick-add dialog in all cases (#3786) @knolleary - Fix mqtt node lifecycle with partial deployments
- Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary - Update tcp node status on reconnect after timeout
- Remove use of Object.hasOwn (#3784) @knolleary - Debug node not handling null messages
- Kill processes run with exec node when flows redeployed
#### 3.0.0: Milestone Release - Inject time spinner incrementing value incorrectly
Editor
- Use theme page and header values if settings.js values are not present (#3767) @Steve-Mcl #### 0.12.1: Maintenance Release
- Focus editor for undo after some actions in menu (#3759) @kazuhitoyokoi
- Ensure node icon shade has properly rounded corners (#3763) @knolleary - Enable touch-menu for links so they can be deleted
- Fix storing subflow credential type when input has multiple types (#3762) @knolleary - Allow nodes to be installed by path name
- Ensure global-config and flow-config have info in the hierarchy popover (#3752) @Steve-Mcl - Fix basic authentication on httpNode/Admin/Static
- Include dirty state in history event (#3748) @Steve-Mcl
- Fix display direction of context sub-menu (#3746) @knolleary
- Fix clear pinned paths of debug sidebar menu (#3745) @HiroyasuNishiyama
- prevent exception generating tooltip for deleted nodes (#3742) @Steve-Mcl #### 0.12.0: Milestone Release
- Fix context menu issues ready for v3 beta.5 (#3741) @Steve-Mcl
- Do not generate new node-ids when pasting a cut flow (#3729) @knolleary - Change/Switch rules now resize with dialog width
- Fix to prevent node from moving out of workspace (#3731) @HiroyasuNishiyama - Support for node 4.x
- Don't let themes change disabled config node background color (#3736) @bonanitech - Move to Express 4.x
- Move colors left behind in #3692 to CSS variables (#3737) @bonanitech - Copy default settings file to user dir on start up
- Fix handling of global debug message (#3733) @HiroyasuNishiyama - Config nodes can be scoped to a particular subflow/tab
- Fix label overflow @ config-node palette (#3730) @ralphwetzel - Comms link tolerates <5 second breaks in connection before notifying user
- Fix defaulting to monaco if settings does not contain codeEditor (#3732) @knolleary - MQTT node overhaul - add will/tls/birth message support
- Disable keyboard shortcut mapping when showing Edit[..]Dialog (#3700) @ralphwetzel - Status node - to report status events from other nodes
- Update add-junction menu to work in more cases (#3727) @knolleary - Error node can be targeted to specific other nodes
- Ensure importMap is not null when using import UI (#3723) @Steve-Mcl - JSON node can encode Array types
- Add Japanese translations for v3.0-beta.4 (#3724) @kazuhitoyokoi - Switch node regular expression rule can now be set to be case-insensitive
- Fix "split with" on virtual links (#3766) @Steve-Mcl - HTTP In node can accept non-UTF8 payloads - will return a Buffer when appropriate
- Exec node configuration consistent regardless of the spawn option
Runtime - Function node can now display status icon/text
- CSV node can now handle arrays
- Do not remove unknown credentials of Subflow Modules (#3728) @knolleary - setInterval/clearInterval add to Function node
- Add missing entries from beta.4 changelog (#3721) @knolleary - Function node automatically clears all timers (setInterval/setTimeout) when the node is stopped
Nodes
- Change: Fix change node, not handling from field properly when using context (#3754) @Fadoli #### 0.11.2: Maintenance Release
- Link Call: Fix linkcall registry bugs (#3751) @Steve-Mcl
- WebSocket: Fix close timeout of websocket node (#3734) @HiroyasuNishiyama - Allow XML parser options be set on the message
- Add 'mobile' category to the palette (no core nodes included)
#### 3.0.0-beta.4: Beta Release - Allow a message catalog provide a partial translation
- Fix HTTP Node nls message id
Editor - Remove delay spinner upper limit
- Update debug node output to include length of payload
- Move all colours to CSS variables (#3692) @bonanitech
- Fix clicking on node in workspace to hide context menu (#3696) @knolleary
- Fix credential type input item of subflow template (#3703) @HiroyasuNishiyama
- Add option flag `reimport` to `importNodes` (#3718) @Steve-Mcl
- Update german translation (#3691) @Dennis14e #### 0.11.1: Maintenance Release
- List welcome tours in help sidebar (#3717) @knolleary
- Ensure 'hidden flow' count doesn't include subflows (#3715) @knolleary - Fix exclusive config node check when type not registered (prevented HTTP In node from being editable unless the swagger node was also installed)
- Fix Chinese translate (#3706) @hotlong
- Fix use default button for node icon (#3714) @kazuhitoyokoi
- Fix select boxes vertical alignment (#3698) @bonanitech
- Ensure workspace clean after undoing dropped node (#3708) @Steve-Mcl #### 0.11.0: Milestone Release
- Use solid colour as config node icon background to hide text overflow (#3710) @Steve-Mcl
- Increase quick-add height to reveal 2 most recent entries (#3711) @Steve-Mcl - Add Node 0.12 support
- Set default editor to monaco in absence of user preference (#3702) @knolleary - Internationalization support
- Add Japanese translations for v3.0-beta.3 (#3688) @kazuhitoyokoi - Editor UI refresh
- Fix handling of spacebar inside JSON visual editor (#3687) @knolleary - Add RBE node
- Fix menu padding to handle both icons and submenus (#3686) @knolleary - File node optionally creates path to file
- Include scroll offset when positioning quick-add dialog (#3685) @knolleary - Function node can access `clearTimeout`
- Fix: Unable to login with 'read' permission
Runtime
- Allow flows to be stopped and started manually (#3719) @knolleary
- Import default export if node is a transpiled es module (#3669) @dschmidt #### 0.10.10: Maintenance Release
- Leave Monaco theme commented out by default (#3704) @bonanitech
- Fix permissions issue with packaged nrgpio script
Nodes - Add better help message if deprecated node missing
- CSV: Fix CSV node to handle when outputting text fields (#3716) @dceejay
- Delay: Fix delay rate limit last timing when empty (#3709) @dceejay
- Link: Ensure link-call cache is updated when link-in is modified (#3695) @Steve-Mcl #### 0.10.9: Maintenance Release
- Join: Join node in reduce mode doesn't keep existing msg properties (#3670) @dceejay
- Template: Add support for evalulating {{env.<var>}} within a template node (#3690) @cow0w Fix packaging of bin scripts
#### 3.0.0-beta.3: Beta Release
Editor #### 0.10.8: Maintenance Release
- Add Right-Click content menu (#3678) @knolleary - Nodes moved out of core
- Fix disable junction (#3671) @HiroyasuNishiyama - still included as a dependency: twitter, serial, email, feedparser
- Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi - no longer included: mongo, arduino, irc, redis
- Reset mouse state when switching tabs (#3643) @knolleary - node icon defn can be a function
- Fix uncorrect fix of junction to subflow conversion (#3666) @HiroyasuNishiyama - http_proxy support
- Fix undoing junction to subflow (#3653) @HiroyasuNishiyama - httpNodeMiddleware setting
- Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama - Trigger node ui refresh
- Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama - editorTheme setting
- Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi - Warn on deploy of unused config nodes
- Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama - catch node prevents error loops
- Fix to sanitize tab name (#3646) @HiroyasuNishiyama
- Fix selector placement (#3644) @bonanitech
- Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi
- Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama #### 0.10.6: Maintenance Release
- Fix layer of palette node (#3638) @HiroyasuNishiyama
- Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama Changes:
- Fix typo in CSS (#3628) @bonanitech - Performance improvements in editor
- Use the correct variable for the gutter text color (#3615) @bonanitech - Palette appearance update
- Warn on navigation with undeployed changes
- Disable undeployed node action buttons
Runtime - Disable subflow node action buttons
- Add Catch node
- Support loading node modules from `nodesdir` (#3676) @Steve-Mcl - Add logging functions to Function node
- fix buffer parse error message of evaluateNodeProperty (#3624) @HiroyasuNishiyama - Add send function to Function node
- Update Change node to support multiple rules
Nodes
- File: Further simplify file node filename entry UX (v3) (#3677) @Steve-Mcl
- Function: Fix initial cursor position of init/finalize tab of function node (#3674) @HiroyasuNishiyama #### 0.10.4: Maintenance Release
- Function: Fix ESM module loading in Function node (#3645) @knolleary
- Inject: Fix JSONata evaluation of inject button (#3632) @HiroyasuNishiyama Changes:
- TCP: Dont delete TCP socket twice (#3630) @Steve-Mcl
- MQTT Node: define noproxy variable (#3626) @Steve-Mcl - http request node passes on request url as msg.url
- Debug: i18n debug sidebar node label (#3623) @HiroyasuNishiyama - handle config nodes appearing out of order in flow file - don't assume they are always at the start
- move subflow palette category to the top, to make it more obvious
#### 3.0.0-beta.2: Beta Release - fix labelling of Raspberry Pi pins
- allow email node to mark mail as read
**Migration from 2.x** - fix saving library content
- add node-red and node-red-pi start scripts
- The 'slice wires' action has changed from Ctrl-RightMouseButton to Alt-LeftMouseButton - use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir)
Editor
- Rework Junctions to be more node like in their event handling (#3607) @knolleary #### 0.10.3: Maintenance Release
- Change slicing / slice-junction operations over to mouse button 0 (Left Mouse Button) (#3609) @Steve-Mcl
- Do not slice-junction link node wires (#3608) @knolleary Fixes:
- Handle many-to-one slicing of wires (#3604) @knolleary
- Ensure ACE worker options are set (#3611) @Steve-Mcl - httpAdminAuth was too aggressively deprecated (ie removed); restoring with a console warning when used
- Remove duplicate history add of ungroup event (#3605) @knolleary - adds reporting of node.js version on start-up
- use text width instead of number of characters for deciding select fi… (#3603) @HiroyasuNishiyama - mongo node skip/limit options can be strings or numbers
- Update Japanese info of link call node reflecting update of English info (#3600) @HiroyasuNishiyama - CSV parser passes through provided message object
- Fix typedInput label not visible on themes (#3580) @bonanitech
- Fix project switching when junctions are present (#3595) @Steve-Mcl
- Fix junction: when wiring from a regular nodes INPUT, backwards to a junction (#3591) @Steve-Mcl
- Fix error initialising flow tab editor (#3585) @Steve-Mcl #### 0.10.2: Maintenance Release
- Add Japanese translations for v3.0-beta.1 (#3576) @kazuhitoyokoi
- Fix image paths where `red/image/typedInput/XXXX.png` should be `red/image/typedInput/XXXX.svg` (#3592) @kazuhitoyokoi Fixes:
- Fix browser console error Uncaught TypeError when searching certain terms (#3584) @Steve-Mcl - subflow info sidebar more useful
- adds missing font-awesome file
Runtime - inject node day selection defaulted to invalid selection
- loading a flow with no tabs failed to add nodes to default tab
- fix error on system-info action (#3589) @HiroyasuNishiyama
Nodes
- I18n switch rule selector (#3602) @HiroyasuNishiyama
- Handle removal of event handlers to allow mqtt client.end() to work (#3594) @PhilDay-CT
- update link-call node info according to current behavior (#3597) @HiroyasuNishiyama
#### 3.0.0-beta.1: Beta Release
**Migration from 2.x**
- Node-RED now requires Node.js 14.x or later.
- New installs of Node-RED will default to the monaco editor.
Editor
- Add Junctions (#3462) @knolleary
- Allow node name to be auto-generated when added (#3478, #3538) @knolleary
- Set monaco as default code editor as of v3.x (#3543) @Steve-Mcl
- Update Monaco to V0.33.0 (#3522) @Steve-Mcl
- Auto-complete Improvements (#3521) @Steve-Mcl
- Add a tooltip to debug sidebar messages to reveal full path to node (#3503) @knolleary
- Fix down arrow triggering menu in search box (#3507) @Steve-Mcl
- Add Japanese translations for v3.0 (#3512) @kazuhitoyokoi
- Add feature: Continuous search tools (search previous, search next) (#3405) @Steve-Mcl
- Add feature: split-wire-to-links (#3399, #3476) @Steve-Mcl
- Add copy button to node properties tables (#3390) @knolleary
- Add info-tab search options dropdown to the regular search (#3395) @Steve-Mcl
- New Feature: Add ability to find modified nodes/flows. (#3392) @Steve-Mcl
- Code editor ux improvements around remembering state of each code editor in a flow (#3553) @Steve-Mcl
- Make it easier to apply themes on SVG icons (#3515) @bonanitech
- Add support of property validation message (#3438) @HiroyasuNishiyama
- Ensure node validation tooltip is closed when field becomes valid (#3570) @knolleary
- Add "search for" buttons to notifications (#3567) @Steve-Mcl
- Don't let themes change node config colors (#3564) @bonanitech
- Fix gap between typedInput containers borders (#3560) @bonanitech
- Fix recording removed links in edit history (#3547) @knolleary
- Remove unused SASS vars (#3536) @bonanitech
- Add custom style for jQuery widgets borders (#3537) @bonanitech
- fix out of scope reference of hasUnusedConfig variable (#3535) @HiroyasuNishiyama
- correct "non string" check parenthesis (#3524) @Steve-Mcl
- Ensure i18n of scoped package name (#3516) @Steve-Mcl
- Prevent shortcut deploy when deploy button shaded (#3517) @Steve-Mcl
- Fix: Sidebar "Configuration" filter button tooltip (#3500) @ralphwetzel
- Add the ability to customize diff colors even more (#3499) @bonanitech
- Do JSON comparison of old value/new value in editor (#3481) @Steve-Mcl
- Fix nodes losing their wires when in an iframe (#3484) @zettca
- Improve scroll into view (#3468) @Steve-Mcl
- Do not show 1st tab if hidden when loading (#3464) @Steve-Mcl
Runtime
- Fix importing external module from node-red module (#3541) @knolleary
- Add support for multiple static paths with optional static root (#3542) @Steve-Mcl
- Store external token when authenticating if provided (#3460) @ArFe
- Support OAuth/OpenID logout (#3388) @mw75
- Allow adminAuth to auto-login users when using passport strategy (#3519) @knolleary
- Add runtime diagnostics admin endpoint (#3511) @Steve-Mcl
- Don't start if user has no home directory (#3540) @hardillb
- Error on invalid encrypted credentials (#3498) @sammachin
Nodes
- Debug: Add message count option to Debug status (#3544 #3551) @rafaelmuynarsk @knolleary
- File: Change basic Filename field to a typedInput (#3533) @Steve-Mcl
- HTTP Request: Add UI for Http Request node headers (#3488) @Steve-Mcl
- Inject: let inject optionally fire at start in only at time mode. (#3385) @dceejay
- Link Call: Dynamic link call (#3463) @Steve-Mcl
- Link Call: Display link targets of nodes in a regular flow, for Link Call nodes inside a subflow (#3528) @Steve-Mcl
- MQTT: MQTT payload auto parsing improvements (#3530) @Steve-Mcl
- MQTT: Add client and Runtime MQTT topic validation (#3563) @Steve-Mcl [dev]
- MQTT: save and restore v5 config user props (#3562) @Steve-Mcl
- MQTT: Fix incorrect MQTT status (#3552) @Steve-Mcl
- MQTT: fix reference error of msg.status in debug node (#3526) @HiroyasuNishiyama
- MQTT: Add unit tests for MQTT nodes (#3497) @Steve-Mcl
- MQTT: fix typo of will properties (#3502) @Steve-Mcl
- MQTT: ensure mqtt v5 props can be set false (#3472) @Steve-Mcl
- Switch: add check for NaN in is of type number to be false (#3409) @dceejay
- TCP: TCP node better split (#3465) @dceejay
- Watch: Update Watch node to use node-watch module (#3559 #3569) @knolleary
- WebSocket: call done after ws disconnects (#3531) @Steve-Mcl
#### Older Releases
Change logs for older releases are available on GitHub: https://github.com/node-red/node-red/releases

View File

@@ -9,16 +9,13 @@ We welcome contributions, but request you follow these guidelines.
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
By participating, you are expected to uphold this code. Please report unacceptable By participating, you are expected to uphold this code. Please report unacceptable
behavior to the project's core team at team@nodered.org. behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
## Raising issues ## Raising issues
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,33 @@ 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 ### Contributor License Agreement
get it raised - just make sure you link back to the issue it's fixing and
try to include some tests!
All contributors need to sign the OpenJS Foundation's Contributor License Agreement. In order for us to accept pull-requests, the contributor must first complete
It is an online process and quick to do. If you raise a pull-request without a Contributor License Agreement (CLA). This clarifies the intellectual
having signed the CLA, you will be prompted to do so automatically. property license granted with any contribution. It is for your protection as a
Contributor as well as the protection of IBM and its customers; it does not
change your rights to use your own Contributions for any other purpose.
### Code Branches You can download the CLAs here:
When raising a PR for a fix or a new feature, it is important to target the right branch. - [individual](http://nodered.org/cla/node-red-cla-individual.pdf)
- [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf)
- `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch. If you are an IBMer, please contact us directly as the contribution process is
- `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. slightly different.
- `dev` - this is the branch for new feature development targeting the next milestone release.
### Coding standards ### Coding standards

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * Copyright 2013, 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -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,29 +37,10 @@ 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: {
all: {
configFile: 'test/editor/wdio.conf.js'
}
},
nyc: {
options: {
cwd: '.',
include: ['packages/node_modules/**'],
excludeNodeModules: false,
exclude: ['packages/node_modules/@node-red/editor-client/**'],
reporter: ['lcov', 'html','text-summary'],
reportDir: 'coverage',
all: true
},
all: { cmd: false, args: ['grunt', 'simplemocha:all'] },
core: { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] },
nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] }
},
jshint: { jshint: {
options: { options: {
jshintrc:true jshintrc:true
@@ -92,20 +55,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 +78,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,110 +98,60 @@ 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/main.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/bidi/bidi.js",
"packages/node_modules/@node-red/editor-client/src/js/user.js", "editor/js/bidi/base-text-dir.js",
"packages/node_modules/@node-red/editor-client/src/js/comms.js", "editor/js/bidi/format.js",
"packages/node_modules/@node-red/editor-client/src/js/runtime.js", "editor/js/bidi/numeric-shaping.js",
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js", "editor/js/ui/state.js",
"packages/node_modules/@node-red/editor-client/src/js/text/format.js", "editor/js/nodes.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js", "editor/js/history.js",
"packages/node_modules/@node-red/editor-client/src/js/plugins.js", "editor/js/validators.js",
"packages/node_modules/@node-red/editor-client/src/js/nodes.js", "editor/js/ui/common/editableList.js",
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js", "editor/js/ui/common/menu.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/typedInput.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", "editor/js/ui/deploy.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js", "editor/js/ui/keyboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js", "editor/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js", "editor/js/ui/view.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js", "editor/js/ui/sidebar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js", "editor/js/ui/palette.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js", "editor/js/ui/tab-info.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", "editor/js/ui/tab-config.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", "editor/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", "editor/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", "editor/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", "editor/js/ui/clipboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", "editor/js/ui/library.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", "editor/js/ui/notifications.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", "editor/js/ui/search.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", "editor/js/ui/subflow.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js", "editor/js/ui/touch/radialMenu.js"
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/search.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/group.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
], ],
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js" dest: "public/red/red.js"
}, },
vendor: { vendor: {
files: { files: {
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [ "public/vendor/vendor.js": [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js", "editor/vendor/jquery/js/jquery-1.11.3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js", "editor/vendor/bootstrap/js/bootstrap.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.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.touch-punch.min.js", "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"node_modules/marked/marked.min.js", "editor/vendor/marked/marked.min.js",
"node_modules/dompurify/dist/purify.min.js", "editor/vendor/d3/d3.v3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js", "editor/vendor/i18next/i18next.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"
], ],
// "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [ "public/vendor/vendor.css": [
// // TODO: resolve relative resource paths in // TODO: resolve relative resource paths in
// // bootstrap/FA/jquery // bootstrap/FA/jquery
// ],
"packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
],
"packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js": [
"node_modules/mermaid/dist/mermaid.min.js"
] ]
} }
} }
@@ -244,62 +159,54 @@ module.exports = function(grunt) {
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',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js'
} }
} }
}, },
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: {
src: [
'packages/node_modules/@node-red/editor-client/src/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'
] ]
}, },
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: {
@@ -311,36 +218,24 @@ 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: ['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: {
files: [
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
],
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'
@@ -352,13 +247,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'
] ]
} }
} }
@@ -375,77 +269,62 @@ module.exports = function(grunt) {
copy: { copy: {
build: { build: {
files:[ files:[{
{ cwd: 'editor/images',
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js', src: '**',
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js' expand: true,
}, dest: 'public/red/images/'
{ },
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json', {
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json' cwd: 'editor/vendor',
}, src: [
{ 'ace/**',
cwd: 'packages/node_modules/@node-red/editor-client/src/images', //'bootstrap/css/**',
src: '**', 'bootstrap/img/**',
expand: true, 'jquery/css/**',
dest: 'packages/node_modules/@node-red/editor-client/public/red/images/' 'font-awesome/**'
}, ],
{ expand: true,
cwd: 'packages/node_modules/@node-red/editor-client/src/vendor', dest: 'public/vendor/'
src: [ },
'ace/**', {
'jquery/css/base/**', cwd: 'editor/icons',
'font-awesome/**', src: '**',
'monaco/dist/**', expand: true,
'monaco/types/extraLibs.js', dest: 'public/icons/'
'monaco/style.css', },
'monaco/monaco-bootstrap.js' {
], expand: true,
expand: true, src: ['editor/index.html','editor/favicon.ico'],
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' dest: 'public/',
}, flatten: true
{ },
cwd: 'packages/node_modules/@node-red/editor-client/src', {
src: [ src: 'CHANGELOG.md',
'types/node/**/*.ts', dest: 'public/red/about'
'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: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/icons/'
},
{
expand: true,
src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'],
dest: 'packages/node_modules/@node-red/editor-client/public/',
flatten: true
},
{
src: 'CHANGELOG.md',
dest: 'packages/node_modules/@node-red/editor-client/public/red/about'
},
{
src: 'CHANGELOG.md',
dest: 'packages/node_modules/node-red/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/tours',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/'
}
] ]
},
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: {
@@ -453,96 +332,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'
} }
} }
}); });
@@ -555,42 +358,16 @@ 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-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 2013, 2015 IBM Corp.\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"+
@@ -606,7 +383,7 @@ module.exports = function(grunt) {
" **/\n"; " **/\n";
if (files) { if (files) {
for (var i=0; i<files.length; i++) { for (var i=0;i<files.length;i++) {
var file = files[i]; var file = files[i];
if (!grunt.file.exists(file)) { if (!grunt.file.exists(file)) {
grunt.log.warn('File '+ file + ' not found'); grunt.log.warn('File '+ file + ' not found');
@@ -627,38 +404,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 () {
@@ -667,42 +412,23 @@ 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']); ['jshint:core','simplemocha: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',
'Builds editor content then runs unit tests on editor ui',
['verifyUiTestDependencies']);
} else {
grunt.registerTask('test-ui',
'Builds editor content then runs unit tests on editor ui',
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
}
grunt.registerTask('test-nodes', grunt.registerTask('test-nodes',
'Runs unit tests on core nodes', 'Runs unit tests on core nodes',
['build','nyc:nodes']); ['simplemocha: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','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy: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',
@@ -710,18 +436,6 @@ module.exports = function(grunt) {
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',
'Run Istanbul code test coverage task',
['build','nyc:all']);
grunt.registerTask('docs',
'Generates API documentation',
['jsdoc']);
}; };

View File

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

View File

@@ -1,27 +1,29 @@
# 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 node-red`
2. `node-red` 2. `node-red`
3. Open <http://localhost:1880> 3. Open <http://localhost:1880>
## 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
@@ -51,19 +56,17 @@ Before raising a pull-request, please read our
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
By participating, you are expected to uphold this code. Please report unacceptable By participating, you are expected to uphold this code. Please report unacceptable
behavior to any of the project's core team at team@nodered.org. behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
## Authors ## Authors
Node-RED is a project of the [OpenJS Foundation](http://openjsf.org). Node-RED is a creation of [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
It is maintained by: * Nick O'Leary [@knolleary](http://twitter.com/knolleary)
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
* And many others...
For more open-source projects from IBM, head over [here](http://ibm.github.io).
## Copyright and license ## Copyright and license
Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE). Copyright 2013, 2016 IBM Corp. 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,6 +1,6 @@
#!/bin/sh #!/bin/bash
# #
# Copyright JS Foundation and other contributors, http://js.foundation # Copyright 2015 IBM Corp.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -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/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

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/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: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

View File

@@ -0,0 +1,67 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.bidi.baseTextDir = (function() {
var LRE = "\u202A",
RLE = "\u202B",
PDF = "\u202C";
function _isRTLValue(stringValue) {
var length = stringValue.length;
for (var i = 0 ; i < length; i++) {
if (_isBidiChar(stringValue.charCodeAt(i))) {
return true;
} else if (_isLatinChar(stringValue.charCodeAt(i))) {
return false;
}
}
return RED.bidi.isMirroringEnabled();
}
function _isBidiChar(c) {
return (c >= 0x05d0 && c <= 0x05ff)||
(c >= 0x0600 && c <= 0x065f)||
(c >= 0x066a && c <= 0x06ef)||
(c >= 0x06fa && c <= 0x07ff)||
(c >= 0xfb1d && c <= 0xfdff)||
(c >= 0xfe70 && c <= 0xfefc);
}
function _isLatinChar(c) {
return (c > 64 && c < 91) || (c > 96 && c < 123);
}
/**
* Enforces the text direction of a given string by adding
* UCC (Unicode Control Characters)
* @param value - the string
*/
function _enforceTextDirectionWithUCC(value) {
if (value) {
var dir = RED.bidi.resolveBaseTextDir(value);
if (dir == "ltr") {
return LRE + value + PDF;
} else if (dir == "rtl") {
return RLE + value + PDF;
}
}
return value;
}
return {
enforceTextDirectionWithUCC: _enforceTextDirectionWithUCC,
isRTLValue : _isRTLValue
}
})();

189
editor/js/bidi/bidi.js Normal file
View File

@@ -0,0 +1,189 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.bidi = (function() {
var textDir = "";
var shaperType = "";
var calendarType = "";
/**
* Indicates the type of bidi-support
* BTD : Base text direction
* NS : Numeric Shaping
* CALENDAR : National Calendar
* STT_ATTACH : Structure Text Support, it is using to call attach function located at format.js
* STT_GETHTML : STT_ATTACH : Structure Text Support, it is using to call getHtml function located at format.js
*/
var _flags = {
BTD: 1,
NS: 2,
CALENDAR: 4,
STT_ATTACH: 8,
STT_GETHTML: 16
};
/**
* Reverse component position when mirroring is enabled
*/
var _componentPos = {};
/**
* Check if browser language is RTL language
*/
function _isMirroringEnabled() {
var isRTLLang = new RegExp("^(ar|he)").test(navigator.language);
if (isRTLLang) {
_componentPos.left = "right";
_componentPos.right = "left";
return true;
} else {
_componentPos.left = "left";
_componentPos.right = "right";
return false;
}
}
/**
* @param val - the numeric shaping type: None , National or contextual
*/
function _setNumericShapingType(val) {
shaperType = val;
_refreshView();
}
/**
* Sets the national calendar preference
* @param val - the calendar type hijri, hebrew or gregorian
*/
function _setCalendarType(val) {
calendarType = val;
}
/**
* Formats the date according to the current selected calendar
* @param date - the date object to be formatted
*/
function _getGlobalizedDate(date) {
var options = {};
var lang = navigator.language;
if (calendarType === "hijri") {
options = lang + "-u-ca-islamic";
} else if (calendarType === "hebrew") {
options = lang + "-u-ca-hebrew";
}
return date.toLocaleString(options);
}
/**
* Sets the text direction preference
* @param dir - the text direction preference
*/
function _setTextDirection(dir) {
textDir = dir;
_refreshView();
_enforceTextDirectionOnPage();
}
/**
* Enforces the text direction for all the spans with style bidiAware under
* workspace or sidebar div
*/
function _enforceTextDirectionOnPage() {
$("#workspace").find('span.bidiAware').each(function() {
$(this).attr("dir", _resolveBaseTextDir($(this).html()));
});
$("#sidebar").find('span.bidiAware').each(function() {
$(this).attr("dir", _resolveBaseTextDir($(this).text()));
});
}
/**
* Determines the text direction of a given string.
* @param value - the string
*/
function _resolveBaseTextDir(value) {
if (textDir == "auto") {
if (RED.bidi.baseTextDir.isRTLValue(value)) {
return "rtl";
} else {
return "ltr";
}
} else {
return textDir;
}
}
/**
* Adds event listeners to the Input to ensure its text-direction attribute
* is properly set based on its content.
* @param input - the input field
*/
function _prepareInput(input) {
input.on("keyup",_onInputChange).on("paste",_onInputChange).on("cut",_onInputChange);
// Set the initial text direction
_onInputChange.call(input);
}
function _onInputChange() {
$(this).attr("dir", _resolveBaseTextDir($(this).val()));
}
/**
* Refreshes the view whenever changing the user preferences
*/
function _refreshView() {
RED.nodes.eachNode(function(n) { n.dirty = true;});
RED.view.redraw();
RED.palette.refresh();
}
/**
* Applying bidi support for these features: base-text-dir ,Numeric-shaping or both, STT ,Calendar which is controlled by the flag value.
* @param value- the string to apply the bidi-support on it.
* @param flag - indicates the type of bidi-support (Base-text-dir ,Numeric-shaping or both, STT , Calendar)
* @param type - could be one of filepath, url, email
* @param args - pass additional arguments to the handler. generally null.
*/
function _applyBidiSupport(value, flag, type, args) {
switch (flag) {
case 0:
value = RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value);
return RED.bidi.numericShaping.shape(value, shaperType, textDir);
case 1:
return RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value);
case 2:
return RED.bidi.numericShaping.shape(value, shaperType, textDir);
case 4:
return _getGlobalizedDate(value);
case 8:
return RED.bidi.format.attach(value, type, args, _isMirroringEnabled(), navigator.language);
case 16:
return RED.bidi.format.getHtml(value, type, args, _isMirroringEnabled(), navigator.language);
default:
return value;
}
}
return {
isMirroringEnabled: _isMirroringEnabled,
setNumericShapingType : _setNumericShapingType,
setCalendarType: _setCalendarType,
setTextDirection : _setTextDirection,
applyBidiSupport : _applyBidiSupport,
resolveBaseTextDir : _resolveBaseTextDir,
prepareInput: _prepareInput,
flags: _flags,
componentPos: _componentPos
}
})();

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * Copyright 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -13,7 +13,8 @@
* 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.
**/ **/
RED.text.format = (function() {
RED.bidi.format = (function() {
var TextSegment = (function() { var TextSegment = (function() {
var TextSegment = function (obj) { var TextSegment = function (obj) {
@@ -758,7 +759,7 @@ RED.text.format = (function() {
{ {
guiDir: isRtl ? "rtl" : "ltr", guiDir: isRtl ? "rtl" : "ltr",
dir: "ltr", dir: "ltr",
points: "/\\:." points: "/\\:.&<>"
}; };
if (!parseOnly) { if (!parseOnly) {
return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale);
@@ -876,7 +877,7 @@ RED.text.format = (function() {
{ {
guiDir: isRtl ? "rtl" : "ltr", guiDir: isRtl ? "rtl" : "ltr",
dir: "ltr", dir: "ltr",
points: ":?#/@.[]=" points: ":?#/@.[]=&<>"
}; };
if (!parseOnly) { if (!parseOnly) {
return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale);
@@ -1242,7 +1243,7 @@ RED.text.format = (function() {
element.dispatchEvent(event); element.dispatchEvent(event);
return; return;
} }
var range = selection.getRangeAt(0); var range = selection.getRangeAt(0);
var tempRange = range.cloneRange(), startNode, startOffset; var tempRange = range.cloneRange(), startNode, startOffset;
startNode = range.startContainer; startNode = range.startContainer;
@@ -1304,7 +1305,19 @@ RED.text.format = (function() {
} }
return { return {
/*! /**
* Returns the string representation of a given structured text
* @param text - the structured text
* @param type - could be one of filepath, url, email
* @param args - pass additional arguments to the handler. generally null.
* @param isRtl - indicates if the GUI is mirrored
* @param locale - the browser locale
*/
getString: function (text, type, args, isRtl, locale) {
return getHandler(type).format(text, args, isRtl, false, locale);
},
/**
* Returns the HTML representation of a given structured text * Returns the HTML representation of a given structured text
* @param text - the structured text * @param text - the structured text
* @param type - could be one of filepath, url, email * @param type - could be one of filepath, url, email
@@ -1315,7 +1328,7 @@ RED.text.format = (function() {
getHtml: function (text, type, args, isRtl, locale) { getHtml: function (text, type, args, isRtl, locale) {
return getHandler(type).format(text, args, isRtl, true, locale); return getHandler(type).format(text, args, isRtl, true, locale);
}, },
/*! /**
* Handle Structured text correct display for a given HTML element. * Handle Structured text correct display for a given HTML element.
* @param element - the element : should be of type div contenteditable=true * @param element - the element : should be of type div contenteditable=true
* @param type - could be one of filepath, url, email * @param type - could be one of filepath, url, email

View File

@@ -0,0 +1,86 @@
/**
* Copyright 2016 IBM Corp.
*
* 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.bidi.numericShaping = (function(){
var regex = /([0-9])|([\u0660-\u0669])|([\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE]+)|([^0-9\u0660-\u0669\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE\u0600-\u0607\u0609-\u060A\u060C\u060E-\u061A\u064B-\u066C\u0670\u06D6-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u08E4-\u08FF\uFD3E-\uFD3F\uFDD0-\uFDEF\uFDFD\uFEFF\u0000-\u0040\u005B-\u0060\u007B-\u007F\u0080-\u00A9\u00AB-\u00B4\u00B6-\u00B9\u00BB-\u00BF\u00D7\u00F7\u02B9-\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u02FF\u2070\u2074-\u207E\u2080-\u208E\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A-\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189\uA720-\uA721\uA788\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE]+)/g;
/**
* Converts the digits in the text to European or Arabic digits According to
* the shaperType & the textDir.
*/
function _shape(text, shaperType, textDir) {
text = text.toString();
if(textDir === "auto"){
textDir = RED.bidi.resolveBaseTextDir(text);
}
if (!text) {
return text;
}
switch (shaperType) {
case "defaultNumeral":
return _shapeEuropean(text);
case "national":
return _shapeArabic(text);
case "contextual":
return _shapeContextual(text, textDir === "rtl" ? 2 : 1);
default:
return text;
}
}
/**
* Converts the digits in the text to European digits.
*/
function _shapeEuropean(text) {
return text.replace(/[\u0660-\u0669]/g, function(c) {
return c.charCodeAt(0) - 1632;
});
}
/**
* Converts the digits in the text to Arabic digits.
*/
function _shapeArabic(text) {
return text.replace(/[0-9]/g, function(c) {
return String.fromCharCode(parseInt(c) + 1632);
});
}
/**
* Converts the digits in the text to European or Arabic digits
* According to the type of the preceding strong character.
* @param context:The current effective context.
* Allowed values:
* '1': European context
* '2': Arabic context
*/
function _shapeContextual(text, context) {
return text.replace(regex, function(match, latinDigit, arabicDigit, strongArabic, strongLatin){
if (latinDigit) {
return (context === 2) ? String.fromCharCode(parseInt(latinDigit) + 1632) : latinDigit;
} else if (arabicDigit) {
return (context === 1) ? arabicDigit.charCodeAt(0) - 1632 : arabicDigit;
} else if (strongArabic) {
context = 2;
} else if (strongLatin) {
context = 1;
}
return match;
});
}
return {
shape: _shape
}
})();

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * Copyright 2014, 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -28,24 +28,14 @@ RED.comms = (function() {
function connectWS() { function connectWS() {
active = true; active = true;
var wspath; var path = location.hostname;
var port = location.port;
if (RED.settings.apiRootUrl) { if (port.length !== 0) {
var m = /^(https?):\/\/(.*)$/.exec(RED.settings.apiRootUrl); path = path+":"+port;
if (m) {
console.log(m);
wspath = "ws"+(m[1]==="https"?"s":"")+"://"+m[2]+"comms";
}
} else {
var path = location.hostname;
var port = location.port;
if (port.length !== 0) {
path = path+":"+port;
}
path = path+document.location.pathname;
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
wspath = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
} }
path = path+document.location.pathname;
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
var auth_tokens = RED.settings.get("auth-tokens"); var auth_tokens = RED.settings.get("auth-tokens");
pendingAuth = (auth_tokens!=null); pendingAuth = (auth_tokens!=null);
@@ -58,7 +48,7 @@ RED.comms = (function() {
} }
} }
ws = new WebSocket(wspath); ws = new WebSocket(path);
ws.onopen = function() { ws.onopen = function() {
reconnectAttempts = 0; reconnectAttempts = 0;
if (errornotification) { if (errornotification) {
@@ -74,41 +64,27 @@ RED.comms = (function() {
} }
} }
ws.onmessage = function(event) { ws.onmessage = function(event) {
var message = JSON.parse(event.data); var msg = JSON.parse(event.data);
if (message.auth) { if (pendingAuth && msg.auth) {
if (pendingAuth) { if (msg.auth === "ok") {
if (message.auth === "ok") { pendingAuth = false;
pendingAuth = false; completeConnection();
completeConnection(); } else if (msg.auth === "fail") {
} else if (message.auth === "fail") { // anything else is an error...
// anything else is an error...
active = false;
RED.user.login({updateMenu:true},function() {
connectWS();
})
}
} else if (message.auth === "fail") {
// Our current session has expired
active = false; active = false;
RED.user.login({updateMenu:true},function() { RED.user.login({updateMenu:true},function() {
connectWS(); connectWS();
}) })
} }
} else { } else if (msg.topic) {
// Otherwise, 'message' is an array of actual comms messages for (var t in subscriptions) {
for (var m = 0; m < message.length; m++) { if (subscriptions.hasOwnProperty(t)) {
var msg = message[m]; var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
if (msg.topic) { if (re.test(msg.topic)) {
for (var t in subscriptions) { var subscribers = subscriptions[t];
if (subscriptions.hasOwnProperty(t)) { if (subscribers) {
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); for (var i=0;i<subscribers.length;i++) {
if (re.test(msg.topic)) { subscribers[i](msg.topic,msg.data);
var subscribers = subscriptions[t];
if (subscribers) {
for (var i=0;i<subscribers.length;i++) {
subscribers[i](msg.topic,msg.data);
}
}
} }
} }
} }
@@ -142,10 +118,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();
}) })

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * Copyright 2015, 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -32,19 +32,14 @@
} }
} }
} }
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]) {
for (var i=0;i<handlers[evt].length;i++) { for (var i=0;i<handlers[evt].length;i++) {
try { try {
handlers[evt][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);
} }
} }

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

@@ -0,0 +1,297 @@
/**
* Copyright 2013, 2016 IBM Corp.
*
* 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 = [];
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();
var i;
var node;
var subflow;
var modifiedTabs = {};
if (ev) {
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.changed = n.changed;
}
// 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].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 {
RED.editor.updateNodeProperties(ev.node);
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();
}
},
peek: function() {
return undo_history[undo_history.length-1];
}
}
})();

43
editor/js/i18n.js Normal file
View File

@@ -0,0 +1,43 @@
/**
* Copyright 2013, 2015 IBM Corp.
*
* 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__',
dynamicLoad: false,
load:'current',
ns: {
namespaces: ["editor","node-red"],
defaultNs: "editor"
},
fallbackLng: ['en-US'],
useCookie: false
},function() {
done();
});
RED["_"] = function() {
return i18n.t.apply(null,arguments);
}
},
loadCatalog: function(namespace,done) {
i18n.loadNamespace(namespace,done);
}
}
})();

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

@@ -0,0 +1,283 @@
/**
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var RED = (function() {
function loadNodeList() {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'nodes',
success: function(data) {
RED.nodes.setNodeList(data);
var nsCount = 0;
for (var i=0;i<data.length;i++) {
var ns = data[i];
if (ns.module != "node-red") {
nsCount++;
RED.i18n.loadCatalog(ns.id, function() {
nsCount--;
if (nsCount === 0) {
loadNodes();
}
});
}
}
if (nsCount === 0) {
loadNodes();
}
}
});
}
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 loadFlows() {
$.ajax({
headers: {
"Accept":"application/json",
},
cache: false,
url: 'flows',
success: function(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));
}
RED.comms.subscribe("status/#",function(topic,msg) {
var parts = topic.split("/");
var node = RED.nodes.node(parts[1]);
if (node) {
if (msg.hasOwnProperty("text")) {
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
}
node.status = msg;
if (statusEnabled) {
node.dirty = true;
RED.view.redraw();
}
}
});
RED.comms.subscribe("node/#",function(topic,msg) {
var i,m;
var typeList;
var info;
if (topic == "node/added") {
var addedTypes = [];
for (i=0;i<msg.length;i++) {
m = msg[i];
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");
}
} else if (topic == "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");
}
}
} else if (topic == "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 == "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");
}
}
// 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();
});
}
var statusEnabled = false;
function toggleStatus(state) {
statusEnabled = state;
RED.view.status(statusEnabled);
}
function loadEditor() {
var menuOptions = [];
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
{id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid},
{id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid},
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, 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.bidi.setTextDirection("")}}},
{id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("ltr")}}},
{id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("rtl")}}},
{id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("auto")}}}
]},
{id:"menu-item-numerals",label:RED._("menu.label.view.numericShaping"),options:[
{id:"menu-item-numerals-default",toggle:"numeral-type",label:RED._("menu.label.view.defaultNumeral"),selected: true, onselect:function(s) { if(s){RED.bidi.setNumericShapingType("defaultNumeral")}}},
{id:"menu-item-numerals-national",toggle:"numeral-type",label:RED._("menu.label.view.national"), onselect:function(s) { if (s) {RED.bidi.setNumericShapingType("national")}}},
{id:"menu-item-numerals-contextual",toggle:"numeral-type",label:RED._("menu.label.view.contextual"), onselect:function(s) { if (s){RED.bidi.setNumericShapingType("contextual")}}}
]},
{id:"menu-item-calendars",label:RED._("menu.label.view.calendar"),options:[
{id:"menu-item-calendars-default",toggle:"calendar-type",label:RED._("menu.label.view.defaultCalendar"),selected: true, onselect:function(s) { if(s) {RED.bidi.setCalendarType("gregorian")}}},
{id:"menu-item-calendars-hijri",toggle:"calendar-type",label:RED._("menu.label.view.hijri"), onselect:function(s) { if (s) {RED.bidi.setCalendarType("hijri")}}},
{id:"menu-item-calendars-hebrew",toggle:"calendar-type",label:RED._("menu.label.view.hebrew"), onselect:function(s) { if (s) {RED.bidi.setCalendarType("hebrew")}}}
]},
null,
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}
]});
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:RED.clipboard.import},
{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:RED.clipboard.export},
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export}
]});
menuOptions.push(null);
menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:RED.search.show});
menuOptions.push(null);
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function() {}});
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add},
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit},
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove}
]});
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow},
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
]});
menuOptions.push(null);
if (RED.settings.theme('palette.editable') !== false) {
RED.palette.editor.init();
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:RED.palette.editor.show});
menuOptions.push(null);
}
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp});
menuOptions.push({id:"menu-item-help",
label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"),
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: showAbout });
RED.menu.init({id:"btn-sidemenu",options: menuOptions});
//apply rtl direction on body tag in case of GUI mirroring is enabled
if (RED.bidi.isMirroringEnabled()) {
$("body").attr("dir","rtl");
}
RED.user.init();
RED.library.init();
RED.palette.init();
RED.sidebar.init();
RED.subflow.init();
RED.workspaces.init();
RED.clipboard.init();
RED.search.init();
RED.view.init();
RED.editor.init();
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.add("workspace", /* ? */ 191,{shift:true},function() {RED.keyboard.showHelp();d3.event.preventDefault();});
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);
})
});
return {
};
})();

1249
editor/js/nodes.js Normal file

File diff suppressed because it is too large Load Diff

152
editor/js/settings.js Normal file
View File

@@ -0,0 +1,152 @@
/**
* Copyright 2014 IBM, Antoine Aflalo
*
* 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.settings = (function () {
var loadedSettings = {};
var hasLocalStorage = function () {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
};
var set = function (key, value) {
if (!hasLocalStorage()) {
return;
}
localStorage.setItem(key, JSON.stringify(value));
};
/**
* If the key is not set in the localStorage it returns <i>undefined</i>
* Else return the JSON parsed value
* @param key
* @returns {*}
*/
var get = function (key) {
if (!hasLocalStorage()) {
return undefined;
}
return JSON.parse(localStorage.getItem(key));
};
var remove = function (key) {
if (!hasLocalStorage()) {
return;
}
localStorage.removeItem(key);
};
var setProperties = function(data) {
for (var prop in loadedSettings) {
if (loadedSettings.hasOwnProperty(prop) && RED.settings.hasOwnProperty(prop)) {
delete RED.settings[prop];
}
}
for (prop in data) {
if (data.hasOwnProperty(prop)) {
RED.settings[prop] = data[prop];
}
}
loadedSettings = data;
};
var init = function (done) {
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
if (accessTokenMatch) {
var accessToken = accessTokenMatch[1];
RED.settings.set("auth-tokens",{access_token: accessToken});
window.location.search = "";
}
$.ajaxSetup({
beforeSend: function(jqXHR,settings) {
// Only attach auth header for requests to relative paths
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
var auth_tokens = RED.settings.get("auth-tokens");
if (auth_tokens) {
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);
}
jqXHR.setRequestHeader("Node-RED-API-Version","v2");
}
}
});
load(done);
}
var load = function(done) {
$.ajax({
headers: {
"Accept": "application/json"
},
dataType: "json",
cache: false,
url: 'settings',
success: function (data) {
setProperties(data);
if (RED.settings.user && RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
console.log("Node-RED: " + data.version);
done();
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) {
window.location.search = "";
}
RED.user.login(function() { load(done); });
} else {
console.log("Unexpected error:",jqXHR.status,textStatus);
}
}
});
};
function theme(property,defaultValue) {
if (!RED.settings.editorTheme) {
return defaultValue;
}
var parts = property.split(".");
var v = RED.settings.editorTheme;
try {
for (var i=0;i<parts.length;i++) {
v = v[parts[i]];
}
if (v === undefined) {
return defaultValue;
}
return v;
} catch(err) {
return defaultValue;
}
}
return {
init: init,
load: load,
set: set,
get: get,
remove: remove,
theme: theme
}
})
();

307
editor/js/ui/clipboard.js Normal file
View File

@@ -0,0 +1,307 @@
/**
* Copyright 2015 IBM Corp.
*
* 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.clipboard = (function() {
var dialog;
var dialogContainer;
var exportNodesDialog;
var importNodesDialog;
function setupDialogs() {
dialog = $('<div id="clipboard-dialog" class="hide node-red-dialog"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("body")
.dialog({
modal: true,
autoOpen: false,
width: 500,
resizable: false,
buttons: [
{
id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-close",
class: "primary",
text: RED._("common.label.close"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-copy",
class: "primary",
text: RED._("clipboard.export.copy"),
click: function() {
$("#clipboard-export").select();
document.execCommand("copy");
document.getSelection().removeAllRanges();
RED.notify(RED._("clipboard.nodesExported"));
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-ok",
class: "primary",
text: RED._("common.label.import"),
click: function() {
RED.view.importNodes($("#clipboard-import").val(),$("#import-tab > a.selected").attr('id') === 'import-tab-new');
$( this ).dialog( "close" );
}
}
],
open: function(e) {
$(this).parent().find(".ui-dialog-titlebar-close").hide();
},
close: function(e) {
}
});
dialogContainer = dialog.children(".dialog-form");
exportNodesDialog =
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.export.copy"></label>'+
'<span id="export-range-group" class="button-group">'+
'<a id="export-range-selected" class="editor-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
'<a id="export-range-flow" class="editor-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="export-range-full" class="editor-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'</div>'+
'<div class="form-row">'+
'<textarea readonly style="resize: none; width: 100%; border-radius: 4px;font-family: monospace; font-size: 12px; background:#f3f3f3; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
'</div>'+
'<div class="form-row" style="text-align: right;">'+
'<span id="export-format-group" class="button-group">'+
'<a id="export-format-mini" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
'<a id="export-format-full" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
'</span>'+
'</div>';
importNodesDialog = '<div class="form-row">'+
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+
RED._("clipboard.pasteNodes")+
'"></textarea>'+
'</div>'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.import.import"></label>'+
'<span id="import-tab" class="button-group">'+
'<a id="import-tab-current" class="editor-button toggle selected" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="import-tab-new" class="editor-button toggle" href="#" data-i18n="clipboard.import.newFlow"></a>'+
'</span>'+
'</div>';
}
function validateImport() {
var importInput = $("#clipboard-import");
var v = importInput.val();
v = v.substring(v.indexOf('['),v.lastIndexOf(']')+1);
try {
JSON.parse(v);
importInput.removeClass("input-error");
importInput.val(v);
$("#clipboard-dialog-ok").button("enable");
} catch(err) {
if (v !== "") {
importInput.addClass("input-error");
}
$("#clipboard-dialog-ok").button("disable");
}
}
function importNodes() {
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
dialogContainer.i18n();
$("#clipboard-dialog-ok").show();
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-close").hide();
$("#clipboard-dialog-copy").hide();
$("#clipboard-dialog-ok").button("disable");
$("#clipboard-import").keyup(validateImport);
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
$("#import-tab > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
});
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
}
function exportNodes() {
dialogContainer.empty();
dialogContainer.append($(exportNodesDialog));
dialogContainer.i18n();
$("#export-format-group > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var flow = $("#clipboard-export").val();
if (flow.length > 0) {
var nodes = JSON.parse(flow);
var format = $(this).attr('id');
if (format === 'export-format-full') {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
$("#clipboard-export").val(flow);
}
});
$("#export-range-group > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var type = $(this).attr('id');
var flow = "";
var nodes = null;
if (type === 'export-range-selected') {
var selection = RED.view.selection();
nodes = RED.nodes.createExportableNodeSet(selection.nodes);
} else if (type === 'export-range-flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.filterNodes({z:activeWorkspace});
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
} else if (type === 'export-range-full') {
nodes = RED.nodes.createCompleteNodeSet(false);
}
if (nodes !== null) {
if (RED.settings.flowFilePretty) {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
}
if (flow.length > 0) {
$("#export-copy").removeClass('disabled');
} else {
$("#export-copy").addClass('disabled');
}
$("#clipboard-export").val(flow);
})
$("#clipboard-dialog-ok").hide();
$("#clipboard-dialog-cancel").hide();
$("#clipboard-dialog-copy").hide();
$("#clipboard-dialog-close").hide();
var selection = RED.view.selection();
if (selection.nodes) {
$("#export-range-selected").click();
} else {
$("#export-range-selected").addClass('disabled').removeClass('selected');
$("#export-range-flow").click();
}
if (RED.settings.flowFilePretty) {
$("#export-format-full").click();
} else {
$("#export-format-mini").click();
}
$("#clipboard-export")
.focus(function() {
var textarea = $(this);
textarea.select();
textarea.mouseup(function() {
textarea.unbind("mouseup");
return false;
})
});
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
setTimeout(function() {
$("#clipboard-export").focus();
if (!document.queryCommandEnabled("copy")) {
$("#clipboard-dialog-cancel").hide();
$("#clipboard-dialog-close").show();
} else {
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-copy").show();
}
},0);
}
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
}
return {
init: function() {
setupDialogs();
RED.events.on("view:selection-changed",function(selection) {
if (!selection.nodes) {
RED.menu.setDisabled("menu-item-export",true);
RED.menu.setDisabled("menu-item-export-clipboard",true);
RED.menu.setDisabled("menu-item-export-library",true);
} else {
RED.menu.setDisabled("menu-item-export",false);
RED.menu.setDisabled("menu-item-export-clipboard",false);
RED.menu.setDisabled("menu-item-export-library",false);
}
});
RED.keyboard.add("workspace", /* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
RED.keyboard.add("workspace", /* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
$('#chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
$("#dropTarget").css({display:'table'});
RED.keyboard.add("*", /* ESCAPE */ 27,hideDropTarget);
}
});
$('#dropTarget').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
event.preventDefault();
}
})
.on("dragleave",function(event) {
hideDropTarget();
})
.on("drop",function(event) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
hideDropTarget();
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
RED.view.importNodes(data);
event.preventDefault();
});
},
import: importNodes,
export: exportNodes
}
})();

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright JS Foundation and other contributors, http://js.foundation * Copyright 2016 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
/** /**
* options: * options:
* - addButton : boolean|string - text for add label, default 'add' * - addButton : boolean|string - text for add label, default 'add'
* - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click')
* - height : number|'auto' * - height : number|'auto'
* - resize : function - called when list as a whole is resized * - resize : function - called when list as a whole is resized
* - resizeItem : function(item) - called to resize individual item * - resizeItem : function(item) - called to resize individual item
@@ -28,22 +27,13 @@
* - removable : boolean - whether to display delete button on items * - removable : boolean - whether to display delete button on items
* - addItem : function(row,index,itemData) - when an item is added * - addItem : function(row,index,itemData) - when an item is added
* - removeItem : function(itemData) - called when an item is removed * - removeItem : function(itemData) - called when an item is removed
* - filter : function(itemData) - called for each item to determine if it should be shown
* - sort : function(itemDataA,itemDataB) - called to sort items
* - scrollOnAdd : boolean - whether to scroll to newly added items
* methods: * methods:
* - addItem(itemData) * - addItem(itemData)
* - insertItemAt : function(data,index) - add an item at the specified index * - removeItem(itemData)
* - removeItem(itemData, detach) - remove the item. Optionally detach to preserve any event handlers on the item's label
* - getItemAt(index)
* - indexOf(itemData)
* - width(width) * - width(width)
* - height(height) * - height(height)
* - items() * - items()
* - empty() * - empty()
* - filter(filter)
* - sort(sort)
* - length()
*/ */
$.widget( "nodered.editableList", { $.widget( "nodered.editableList", {
_create: function() { _create: function() {
@@ -54,78 +44,34 @@
this.uiContainer = this.element this.uiContainer = this.element
.wrap( "<div>" ) .wrap( "<div>" )
.parent(); .parent();
this.topContainer = this.uiContainer.wrap("<div>").parent();
if (this.options.header) {
this.options.header.addClass("red-ui-editableList-header");
this.borderContainer = this.uiContainer.wrap("<div>").parent();
this.borderContainer.prepend(this.options.header);
this.topContainer = this.borderContainer.wrap("<div>").parent();
} else {
this.topContainer = this.uiContainer.wrap("<div>").parent();
}
this.topContainer.addClass('red-ui-editableList'); this.topContainer.addClass('red-ui-editableList');
if (this.options.class) {
this.topContainer.addClass(this.options.class);
}
var buttons = this.options.buttons || [];
if (this.options.addButton !== false) { if (this.options.addButton !== false) {
var addLabel, addTitle; var addLabel;
if (typeof this.options.addButton === 'string') { if (typeof this.options.addButton === 'string') {
addLabel = this.options.addButton addLabel = this.options.addButton
} else { } else {
if (RED && RED._) { if (RED && RED._) {
addLabel = RED._("editableList.add"); addLabel = RED._("editableList.add");
addTitle = RED._("editableList.addTitle");
} else { } else {
addLabel = 'add'; addLabel = 'add';
addTitle = 'add new item';
} }
} }
buttons.unshift({ $('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
label: addLabel, .appendTo(this.topContainer)
icon: "fa fa-plus", .click(function(evt) {
click: function(evt) {
that.addItem({});
},
title: addTitle
});
}
buttons.forEach(function(button) {
var element = $('<button type="button" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></button>')
.appendTo(that.topContainer)
.on("click", function(evt) {
evt.preventDefault(); evt.preventDefault();
if (button.click !== undefined) { that.addItem({});
button.click(evt);
}
}); });
}
if (button.id) {
element.attr("id", button.id);
}
if (button.title) {
element.attr("title", button.title);
}
if (button.icon) {
element.append($("<i></i>").attr("class", button.icon));
}
if (button.label) {
element.append($("<span></span>").text(" " + button.label));
}
});
if (this.element.css("position") === "absolute") { if (this.element.css("position") === "absolute") {
["top","left","bottom","right"].forEach(function(s) { ["top","left","bottom","right"].forEach(function(s) {
var v = that.element.css(s); var v = that.element.css(s);
if (v!=="auto" && v!=="") { if (s!=="auto" && s!=="") {
that.topContainer.css(s,v); that.topContainer.css(s,v);
that.uiContainer.css(s,"0"); that.uiContainer.css(s,"0");
if (s === "top" && that.options.header) {
that.uiContainer.css(s,"20px")
}
that.element.css(s,'auto'); that.element.css(s,'auto');
} }
}) })
@@ -134,11 +80,6 @@
this.uiContainer.css("position","absolute"); this.uiContainer.css("position","absolute");
} }
if (this.options.header) {
this.borderContainer.addClass("red-ui-editableList-border");
} else {
this.uiContainer.addClass("red-ui-editableList-border");
}
this.uiContainer.addClass("red-ui-editableList-container"); this.uiContainer.addClass("red-ui-editableList-container");
this.uiHeight = this.element.height(); this.uiHeight = this.element.height();
@@ -154,13 +95,8 @@
this.uiContainer.css("minHeight",minHeight); this.uiContainer.css("minHeight",minHeight);
this.element.css("minHeight",0); this.element.css("minHeight",0);
} }
var maxHeight = this.element.css("maxHeight");
if (maxHeight !== '0px') {
this.uiContainer.css("maxHeight",maxHeight);
this.element.css("maxHeight",null);
}
if (this.options.height !== 'auto') { if (this.options.height !== 'auto') {
this.uiContainer.css("overflow-y","auto"); this.uiContainer.css("overflow-y","scroll");
if (!isNaN(this.options.height)) { if (!isNaN(this.options.height)) {
this.uiHeight = this.options.height; this.uiHeight = this.options.height;
} }
@@ -218,22 +154,17 @@
if (this.options.resizeItem) { if (this.options.resizeItem) {
var that = this; var that = this;
this.element.children().each(function(i) { this.element.children().each(function(i) {
that.options.resizeItem($(this).children(".red-ui-editableList-item-content"),i); that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i);
}); });
} }
}, },
_destroy: function() { _destroy: function() {
if (this.topContainer) {
var tc = this.topContainer;
delete this.topContainer;
tc.remove();
}
}, },
_refreshFilter: function() { _refreshFilter: function() {
var that = this; var that = this;
var count = 0; var count = 0;
if (!this.activeFilter) { if (!this.activeFilter) {
return this.element.children().show(); this.element.children().show();
} }
var items = this.items(); var items = this.items();
items.each(function (i,el) { items.each(function (i,el) {
@@ -258,7 +189,7 @@
var items = this.element.children(); var items = this.element.children();
var that = this; var that = this;
items.sort(function(A,B) { items.sort(function(A,B) {
return that.activeSort($(A).children(".red-ui-editableList-item-content").data('data'),$(B).children(".red-ui-editableList-item-content").data('data')); return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data'));
}); });
$.each(items,function(idx,li) { $.each(items,function(idx,li) {
that.element.append(li); that.element.append(li);
@@ -273,49 +204,10 @@
this.uiHeight = desiredHeight; this.uiHeight = desiredHeight;
this._resize(); this._resize();
}, },
getItemAt: function(index) { addItem: function(data) {
var items = this.items();
if (index >= 0 && index < items.length) {
return $(items[index]).data('data');
} else {
return;
}
},
indexOf: function(data) {
var items = this.items();
for (var i=0;i<items.length;i++) {
if ($(items[i]).data('data') === data) {
return i
}
}
return -1
},
insertItemAt: function(data,index) {
var that = this; var that = this;
data = data || {}; data = data || {};
var li = $('<li>'); var li = $('<li>');
var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
row.data('data',data);
if (this.options.sortable === true) {
$('<i class="red-ui-editableList-item-handle fa fa-bars"></i>').appendTo(li);
li.addClass("red-ui-editableList-item-sortable");
}
if (this.options.removable) {
var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(li);
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
li.addClass("red-ui-editableList-item-removable");
deleteButton.on("click", function(evt) {
evt.preventDefault();
var data = row.data('data');
li.addClass("red-ui-editableList-item-deleting")
li.fadeOut(300, function() {
$(this).remove();
if (that.options.removeItem) {
that.options.removeItem(data);
}
});
});
}
var added = false; var added = false;
if (this.activeSort) { if (this.activeSort) {
var items = this.items(); var items = this.items();
@@ -330,17 +222,32 @@
}); });
} }
if (!added) { if (!added) {
if (index <= 0) { li.appendTo(this.element);
li.prependTo(this.element); }
} else if (index > that.element.children().length-1) { var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li);
li.appendTo(this.element); row.data('data',data);
} else { if (this.options.sortable === true) {
li.insertBefore(this.element.children().eq(index)); $('<i class="red-ui-editableList-item-handle fa fa-bars"></i>').appendTo(li);
} li.addClass("red-ui-editableList-item-sortable");
}
if (this.options.removable) {
var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(li);
$('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton);
li.addClass("red-ui-editableList-item-removable");
deleteButton.click(function() {
var data = row.data('data');
li.addClass("red-ui-editableList-item-deleting")
li.fadeOut(300, function() {
$(this).remove();
if (that.options.removeItem) {
that.options.removeItem(data);
}
});
});
} }
if (this.options.addItem) { if (this.options.addItem) {
var index = that.element.children().length-1; var index = that.element.children().length-1;
// setTimeout(function() { setTimeout(function() {
that.options.addItem(row,index,data); that.options.addItem(row,index,data);
if (that.activeFilter) { if (that.activeFilter) {
try { try {
@@ -356,36 +263,23 @@
that.uiContainer.scrollTop(that.element.height()); that.uiContainer.scrollTop(that.element.height());
},0); },0);
} }
// },0); },0);
} }
}, },
addItem: function(data) { removeItem: function(data) {
this.insertItemAt(data,this.element.children().length)
},
addItems: function(items) {
for (var i=0; i<items.length;i++) {
this.addItem(items[i]);
}
},
removeItem: function(data,detach) {
var items = this.element.children().filter(function(f) { var items = this.element.children().filter(function(f) {
return data === $(this).children(".red-ui-editableList-item-content").data('data'); return data === $(this).find(".red-ui-editableList-item-content").data('data');
}); });
if (detach) { items.remove();
items.detach();
} else {
items.remove();
}
if (this.options.removeItem) { if (this.options.removeItem) {
this.options.removeItem(data); this.options.removeItem(data);
} }
}, },
items: function() { items: function() {
return this.element.children().map(function(i) { return $(this).children(".red-ui-editableList-item-content"); }); return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); });
}, },
empty: function() { empty: function() {
this.element.empty(); this.element.empty();
this.uiContainer.scrollTop(0);
}, },
filter: function(filter) { filter: function(filter) {
if (filter !== undefined) { if (filter !== undefined) {
@@ -401,25 +295,6 @@
}, },
length: function() { length: function() {
return this.element.children().length; return this.element.children().length;
},
show: function(item) {
var items = this.element.children().filter(function(f) {
return item === $(this).children(".red-ui-editableList-item-content").data('data');
});
if (items.length > 0) {
this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top)
}
},
getItem: function(li) {
var el = li.children(".red-ui-editableList-item-content");
if (el.length) {
return el.data('data');
} else {
return null;
}
},
cancel: function() {
this.element.sortable("cancel");
} }
}); });
})(jQuery); })(jQuery);

257
editor/js/ui/common/menu.js Normal file
View File

@@ -0,0 +1,257 @@
/**
* Copyright 2014, 2016 IBM Corp.
*
* 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.menu = (function() {
var menuItems = {};
function createMenuItem(opt) {
var item;
if (opt !== null && opt.id) {
var themeSetting = RED.settings.theme("menu."+opt.id);
if (themeSetting === false) {
return null;
}
}
function setInitialState() {
var savedStateActive = isSavedStateActive(opt.id);
if (savedStateActive) {
link.addClass("active");
opt.onselect.call(opt, true);
} else if (savedStateActive === false) {
link.removeClass("active");
opt.onselect.call(opt, false);
} else if (opt.hasOwnProperty("selected")) {
if (opt.selected) {
link.addClass("active");
} else {
link.removeClass("active");
}
opt.onselect.call(opt, opt.selected);
}
}
if (opt === null) {
item = $('<li class="divider"></li>');
} else {
item = $('<li></li>');
if (opt.group) {
item.addClass("menu-group-"+opt.group);
}
var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">';
if (opt.toggle) {
linkContent += '<i class="fa fa-square pull-left"></i>';
linkContent += '<i class="fa fa-check-square pull-left"></i>';
}
if (opt.icon !== undefined) {
if (/\.png/.test(opt.icon)) {
linkContent += '<img src="'+opt.icon+'"/> ';
} else {
linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> ';
}
}
if (opt.sublabel) {
linkContent += '<span class="menu-label-container"><span class="menu-label">'+opt.label+'</span>'+
'<span class="menu-sublabel">'+opt.sublabel+'</span></span>'
} else {
linkContent += '<span class="menu-label">'+RED.bidi.applyBidiSupport(opt.label, RED.bidi.flags.NS)+'</span>'
}
linkContent += '</a>';
var link = $(linkContent).appendTo(item);
menuItems[opt.id] = opt;
if (opt.onselect) {
link.click(function() {
if ($(this).parent().hasClass("disabled")) {
return;
}
if (opt.toggle) {
var selected = isSelected(opt.id);
if (typeof opt.toggle === "string") {
if (!selected) {
for (var m in menuItems) {
if (menuItems.hasOwnProperty(m)) {
var mi = menuItems[m];
if (mi.id != opt.id && opt.toggle == mi.toggle) {
setSelected(mi.id,false);
}
}
}
setSelected(opt.id,true);
}
} else {
setSelected(opt.id, !selected);
}
} else {
opt.onselect.call(opt);
}
});
setInitialState();
} else if (opt.href) {
link.attr("target","_blank").attr("href",opt.href);
} else if (!opt.options) {
item.addClass("disabled");
link.click(function(event) {
event.preventDefault();
});
}
if (opt.options) {
item.addClass("dropdown-submenu pull-left");
var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item);
for (var i=0;i<opt.options.length;i++) {
var li = createMenuItem(opt.options[i]);
if (li) {
li.appendTo(submenu);
}
}
}
if (opt.disabled) {
item.addClass("disabled");
}
}
return item;
}
function createMenu(options) {
var button = $("#"+options.id);
//button.click(function(event) {
// $("#"+options.id+"-submenu").show();
// event.preventDefault();
//});
var topMenu = $("<ul/>",{id:options.id+"-submenu", class:"dropdown-menu pull-right"}).insertAfter(button);
var lastAddedSeparator = false;
for (var i=0;i<options.options.length;i++) {
var opt = options.options[i];
if (opt !== null || !lastAddedSeparator) {
var li = createMenuItem(opt);
if (li) {
li.appendTo(topMenu);
lastAddedSeparator = (opt === null);
}
}
}
}
function isSavedStateActive(id) {
return RED.settings.get("menu-" + id);
}
function isSelected(id) {
return $("#" + id).hasClass("active");
}
function setSavedState(id, state) {
RED.settings.set("menu-" + id, state);
}
function setSelected(id,state) {
if (isSelected(id) == state) {
return;
}
var opt = menuItems[id];
if (state) {
$("#"+id).addClass("active");
} else {
$("#"+id).removeClass("active");
}
if (opt && opt.onselect) {
opt.onselect.call(opt,state);
}
setSavedState(id, state);
}
function setDisabled(id,state) {
if (state) {
$("#"+id).parent().addClass("disabled");
} else {
$("#"+id).parent().removeClass("disabled");
}
}
function addItem(id,opt) {
var item = createMenuItem(opt);
if (opt.group) {
var groupItems = $("#"+id+"-submenu").children(".menu-group-"+opt.group);
if (groupItems.length === 0) {
item.appendTo("#"+id+"-submenu");
} else {
for (var i=0;i<groupItems.length;i++) {
var groupItem = groupItems[i];
var label = $(groupItem).find(".menu-label").html();
if (opt.label < label) {
$(groupItem).before(item);
break;
}
}
if (i === groupItems.length) {
item.appendTo("#"+id+"-submenu");
}
}
} else {
item.appendTo("#"+id+"-submenu");
}
}
function removeItem(id) {
$("#"+id).parent().remove();
}
function setAction(id,action) {
var opt = menuItems[id];
if (opt) {
opt.onselect = action;
// $("#"+id).click(function() {
// if ($(this).parent().hasClass("disabled")) {
// return;
// }
// if (menuItems[id].toggle) {
// setSelected(id,!isSelected(id));
// } else {
// menuItems[id].onselect.call(menuItems[id]);
// }
// });
}
}
return {
init: createMenu,
setSelected: setSelected,
isSelected: isSelected,
setDisabled: setDisabled,
addItem: addItem,
removeItem: removeItem,
setAction: setAction
//TODO: add an api for replacing a submenu - see library.js:loadFlowLibrary
}
})();

View File

@@ -0,0 +1,79 @@
/**
* Copyright 2015 IBM Corp.
*
* 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.popover = (function() {
function createPopover(options) {
var target = options.target;
var content = options.content;
var delay = options.delay;
var timer = null;
var active;
var div;
var openPopup = function() {
if (active) {
div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body");
var targetPos = target.offset();
var targetWidth = target.width();
var targetHeight = target.height();
var divHeight = div.height();
var popoverPos = ((RED.bidi.isMirroringEnabled()) ? targetPos.left-targetWidth-210 : targetPos.left+targetWidth+17);
div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:popoverPos});
div.fadeIn("fast");
}
}
var closePopup = function() {
if (!active) {
if (div) {
div.fadeOut("fast",function() {
$(this).remove();
});
div = null;
}
}
}
target.on('mouseenter',function(e) {
clearTimeout(timer);
active = true;
timer = setTimeout(openPopup,delay.show);
});
target.on('mouseleave', function(e) {
if (timer) {
clearTimeout(timer);
}
active = false;
setTimeout(closePopup,delay.hide);
});
var res = {
setContent: function(_content) {
content = _content;
}
}
target.data('popover',res);
return res;
}
return {
create: createPopover
}
})();

View File

@@ -0,0 +1,97 @@
/**
* Copyright 2016 IBM Corp.
*
* 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($) {
$.widget( "nodered.searchBox", {
_create: function() {
var that = this;
this.currentTimeout = null;
this.lastSent = "";
this.element.val("");
this.uiContainer = this.element.wrap("<div>").parent();
this.uiContainer.addClass("red-ui-searchBox-container");
$('<i class="fa fa-search"></i>').prependTo(this.uiContainer);
this.clearButton = $('<a href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer);
this.clearButton.on("click",function(e) {
e.preventDefault();
that.element.val("");
that._change("",true);
that.element.focus();
});
this.resultCount = $('<span>',{class:"red-ui-searchBox-resultCount hide"}).appendTo(this.uiContainer);
this.element.val("");
this.element.on("keydown",function(evt) {
if (evt.keyCode === 27) {
that.element.val("");
}
})
this.element.on("keyup",function(evt) {
that._change($(this).val());
});
this.element.on("focus",function() {
$("body").one("mousedown",function() {
that.element.blur();
});
});
},
_change: function(val,instant) {
var fireEvent = false;
if (val === "") {
this.clearButton.hide();
fireEvent = true;
} else {
this.clearButton.show();
fireEvent = (val.length >= (this.options.minimumLength||0));
}
var current = this.element.val();
fireEvent = fireEvent && current !== this.lastSent;
if (fireEvent) {
if (!instant && this.options.delay > 0) {
clearTimeout(this.currentTimeout);
var that = this;
this.currentTimeout = setTimeout(function() {
that.lastSent = that.element.val();
that._trigger("change");
},this.options.delay);
} else {
this._trigger("change");
}
}
},
value: function(val) {
if (val === undefined) {
return this.element.val();
} else {
this.element.val(val);
this._change(val);
}
},
count: function(val) {
if (val === undefined || val === null || val === "") {
this.resultCount.text("").hide();
} else {
val = RED.bidi.applyBidiSupport(val,RED.bidi.flags.NS);
this.resultCount.text(val).show();
}
}
});
})(jQuery);

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