Compare commits
No commits in common. "master" and "0.13.3" have entirely different histories.
1
.gitattributes
vendored
@ -1 +0,0 @@
|
||||
/packages/node_modules/** linguist-generated=false
|
61
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,61 +0,0 @@
|
||||
name: 🐞 Report a bug
|
||||
description: File a bug/issue on the core of Node-RED
|
||||
labels: [needs-triage]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
|
||||
|
||||
If your issue is:
|
||||
- a general 'how-to' type question,
|
||||
- a feature request or suggestion for a change,
|
||||
- or problems with 3rd party (`node-red-contrib-`) nodes
|
||||
|
||||
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
|
||||
|
||||
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
|
||||
|
||||
That way the whole Node-RED user community can help, rather than rely on the core development team.
|
||||
|
||||
To help us understand the issue, please fill-in as much of the following information as you can:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A clear & concise description of what you're experiencing.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear & concise description of what you expected to happen.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Example flow
|
||||
description: If you have a minimal example flow that demonstrates the issue, share it here.
|
||||
value: |
|
||||
```
|
||||
paste your flow here
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Please tell us about your environment. Include any relevant information on how you are running Node-RED.
|
||||
value: |
|
||||
- Node-RED version:
|
||||
- Node.js version:
|
||||
- npm version:
|
||||
- Platform/OS:
|
||||
- Browser:
|
||||
validations:
|
||||
required: false
|
14
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,14 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: ❓ Questions
|
||||
url: https://discourse.nodered.org
|
||||
about: Ask your question on the Node-RED forum
|
||||
- name: ⭐️ Feature Request
|
||||
url: https://discourse.nodered.org/c/development/feature-requests
|
||||
about: Discuss your request with the community
|
||||
- name: 🗂 Documentation
|
||||
url: https://nodered.org/docs
|
||||
about: Go straight to the documentation
|
||||
- name: 💬 Slack
|
||||
url: https://nodered.org/slack
|
||||
about: Chat about the project on our slack team
|
34
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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
|
15
.github/dependabot.yml
vendored
@ -1,15 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
29
.github/scripts/update-node-red-docker.js
vendored
@ -1,29 +0,0 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const newVersion = require("../../package.json").version;
|
||||
|
||||
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
|
||||
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
|
||||
console.log(`Not updating for a non-stable release - ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const currentVersion = require("../../../node-red-docker/package.json").version;
|
||||
|
||||
console.log(`Update from ${currentVersion} to ${newVersion}`)
|
||||
|
||||
updateFile(__dirname+"/../../../node-red-docker/package.json", currentVersion, newVersion);
|
||||
updateFile(__dirname+"/../../../node-red-docker/docker-custom/package.json", currentVersion, newVersion);
|
||||
updateFile(__dirname+"/../../../node-red-docker/README.md", currentVersion, newVersion);
|
||||
|
||||
console.log(`::set-env name=newVersion::${newVersion}`);
|
||||
|
||||
function updateFile(path,from,to) {
|
||||
let contents = fs.readFileSync(path,"utf8");
|
||||
contents = contents.replace(new RegExp(from.replace(/\./g,"\\."),"g"), to);
|
||||
fs.writeFileSync(path, contents);
|
||||
}
|
18
.github/scripts/update-node-red-website.js
vendored
@ -1,18 +0,0 @@
|
||||
const fs = require("fs");
|
||||
|
||||
const newVersion = require("../../package.json").version;
|
||||
|
||||
if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) {
|
||||
console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!/^\d+\.\d+\.\d+$/.test(newVersion)) {
|
||||
console.log(`Not updating for a non-stable release - ${newVersion}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const path = __dirname+"/../../../node-red.github.io/index.html";
|
||||
let contents = fs.readFileSync(path, "utf8");
|
||||
contents = contents.replace(/<span class="node-red-latest-version">v\d+\.\d+\.\d+<\/span>/, `<span class="node-red-latest-version">v${newVersion}<\/span>` );
|
||||
fs.writeFileSync(path, contents);
|
62
.github/workflows/release.yml
vendored
@ -1,62 +0,0 @@
|
||||
name: Publish Release
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
name: 'Update node-red-docker image'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out node-red repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: 'node-red'
|
||||
- name: Check out node-red-docker repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'node-red/node-red-docker'
|
||||
path: 'node-red-docker'
|
||||
- name: Check out node-red.github.io repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'node-red/node-red.github.io'
|
||||
path: 'node-red.github.io'
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: node ./node-red/.github/scripts/update-node-red-docker.js
|
||||
- name: Create Docker Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
path: 'node-red-docker'
|
||||
commit-message: 'Bump to ${{ env.newVersion }}'
|
||||
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
|
||||
body: |
|
||||
Updates the Node-RED Docker repo for the ${{ env.newVersion }} release.
|
||||
|
||||
Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`.
|
||||
|
||||
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
|
||||
- run: node ./node-red/.github/scripts/update-node-red-website.js
|
||||
- name: Create Website Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.NR_REPO_TOKEN }}
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
path: 'node-red.github.io'
|
||||
commit-message: 'Bump to ${{ env.newVersion }}'
|
||||
title: '🚀 Update to Node-RED ${{ env.newVersion }} release'
|
||||
body: |
|
||||
Updates the Node-RED Website repo for the ${{ env.newVersion }} release.
|
||||
|
||||
This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary
|
30
.github/workflows/tests.yml
vendored
@ -1,30 +0,0 @@
|
||||
name: Run tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, dev ]
|
||||
pull_request:
|
||||
branches: [ master, dev ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: read # for actions/checkout to fetch code
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18, 20, 22]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run test
|
12
.gitignore
vendored
@ -7,9 +7,7 @@
|
||||
.sessions.json
|
||||
.settings
|
||||
.tern-project
|
||||
.i18n-editor-metadata
|
||||
*.backup
|
||||
*.bak
|
||||
*_cred*
|
||||
coverage
|
||||
credentials.json
|
||||
@ -19,13 +17,3 @@ node_modules
|
||||
public
|
||||
locales/zz-ZZ
|
||||
nodes/core/locales/zz-ZZ
|
||||
!packages/node_modules
|
||||
packages/node_modules/@node-red/editor-client/public
|
||||
!test/**/node_modules
|
||||
docs
|
||||
!packages/node_modules/**/docs
|
||||
.vscode
|
||||
.nyc_output
|
||||
sync.ffs_db
|
||||
package-lock.json
|
||||
.editorconfig
|
||||
|
10
.jshintrc
@ -2,18 +2,12 @@
|
||||
"asi": true, // allow missing semicolons
|
||||
"curly": true, // require braces
|
||||
"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
|
||||
"immed": true, // require immediate functions to be wrapped in ( )
|
||||
"nonbsp": true, // warn on unexpected whitespace breaking chars
|
||||
//"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually
|
||||
//"unused": true, // Check for unused functions and variables
|
||||
"loopfunc": true, // allow functions to be defined in loops
|
||||
//"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
|
||||
"proto": true, // allow setting of __proto__ in node < v0.12,
|
||||
"esversion": 11 // allow es11(ES2020)
|
||||
//"expr": true, // allow ternery operator syntax...
|
||||
"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
}
|
||||
|
5
.nodemonignore
Normal file
@ -0,0 +1,5 @@
|
||||
/Gruntfile.js
|
||||
/.git/*
|
||||
/lib/*
|
||||
*.backup
|
||||
/public/*
|
7
.npmignore
Normal file
@ -0,0 +1,7 @@
|
||||
.settings
|
||||
.jshintignore
|
||||
.jshintrc
|
||||
.project
|
||||
.tern-project
|
||||
.travis.yml
|
||||
.git
|
26
.travis.yml
Normal file
@ -0,0 +1,26 @@
|
||||
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: "5"
|
||||
before_install:
|
||||
- npm install -g npm@latest-2
|
||||
node_js:
|
||||
- "5"
|
||||
- "4"
|
||||
- "0.12"
|
||||
- "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
@ -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
|
307
CHANGELOG.md
@ -1,307 +0,0 @@
|
||||
#### 4.0.9: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Add details for the dynamic subscription to match the English docs (#5050) @aikitori
|
||||
- Fix tooltip snapping based on `typedInput` type (#5051) @GogoVega
|
||||
- Prevent symbol usage warning in monaco (#5049) @Steve-Mcl
|
||||
- Show subflow flow context under node section of sidebar (#5025) @knolleary
|
||||
- feat: Add custom label for default deploy button in settings.editorTheme (#5030) @matiseni51
|
||||
- Handle long auto-complete suggests (#5042) @knolleary
|
||||
- Handle undefined username when generating user icon (#5043) @knolleary
|
||||
- Handle dragging node into group and splicing link at same time (#5027) @knolleary
|
||||
- Remember context sidebar tree state when refreshing (#5021) @knolleary
|
||||
- Update sf instance env vars when removed from template (#5023) @knolleary
|
||||
- Do not select group when triggering quick-add within it (#5022) @knolleary
|
||||
- Fix library icon handling within library browser component (#5017) @knolleary
|
||||
|
||||
Runtime
|
||||
- Allow env var access to context (#5016) @knolleary
|
||||
- fix debug status reporting if null (#5018) @dceejay
|
||||
- Fix grunt dev via better ndoemon ignore rules (#5015) @knolleary
|
||||
- Fix typo in CHANGELOG (4.0.7-->4.0.8) (#5007) @natcl
|
||||
|
||||
Nodes
|
||||
- Switch: Avoid exceeding call stack when draining message group in Switch (#5014) @knolleary
|
||||
|
||||
#### 4.0.8: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix config node sort order when importing (#5000) @knolleary
|
||||
|
||||
#### 4.0.7: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix def can be undefined if the type is missing (#4997) @GogoVega
|
||||
- Fix the user list of nested config node (#4995) @GogoVega
|
||||
- Support custom login message and button (#4993) @knolleary
|
||||
|
||||
#### 4.0.6: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Roll up various fixes on config node change history (#4975) @knolleary
|
||||
- Add quotes when installing local tgz to fix spacing in the file path (#4949) @AGhorab-upland
|
||||
- Validate json dropped into editor to avoid unhelpful error messages (#4964) @knolleary
|
||||
- Fix junction insert position via context menu (#4974) @knolleary
|
||||
- Apply zoom scale when calculating annotation positions (#4981) @knolleary
|
||||
- Handle the import of an incomplete Subflow (#4811) @GogoVega
|
||||
- Fix updating the Subflow name during a copy (#4809) @GogoVega
|
||||
- Rename variable to avoid confusion in view.js (#4963) @knolleary
|
||||
- Change groups.length to groups.size (#4959) @hungtcs
|
||||
- Remove disabled node types from QuickAddDialog list (#4946) @GogoVega
|
||||
- Fix `setModulePendingUpdated` with plugins (#4939) @GogoVega
|
||||
- Missing getSubscriptions in the docs while its implemented (#4934) @ersinpw
|
||||
- Apply `envVarExcludes` setting to `util.getSetting` into the function node (#4925) @GogoVega
|
||||
- Fix `envVar` editable list should be sortable (#4932) @GogoVega
|
||||
- Improve the node name auto-generated with the first available number (#4912) @GogoVega
|
||||
|
||||
Runtime
|
||||
|
||||
- Get the env config node from the parent subflow (#4960) @GogoVega
|
||||
- Update dependencies (#4987) @knolleary
|
||||
|
||||
Nodes
|
||||
|
||||
- Performance : make reading single buffer / string file faster by not re-allocating and handling huge buffers (#4980) @Fadoli
|
||||
- Make delay node rate limit reset consistent - not send on reset. (#4940) @dceejay
|
||||
- Fix trigger node date handling for latest time type input (#4915) @dceejay
|
||||
- Fix delay node not dropping when nodeMessageBufferMaxLength is set (#4973)
|
||||
- Ensure node.sep is honoured when generating CSV (#4982) @knolleary
|
||||
|
||||
#### 4.0.5: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Refix link call node can call out of a subflow (#4908) @GogoVega
|
||||
|
||||
#### 4.0.4: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix `link call` node can call out of a subflow (#4892) @GogoVega
|
||||
- Fix wrong unlock state when event is triggered after deployment (#4889) @GogoVega
|
||||
- i18n(App) update with latest language file changes (#4903) @joebordes
|
||||
- fix typo: depreciated (#4895) @dxdc
|
||||
|
||||
Runtime
|
||||
|
||||
- Update dev dependencies (#4893) @knolleary
|
||||
|
||||
Nodes
|
||||
|
||||
- MQTT: Allow msg.userProperties to have number values (#4900) @hardillb
|
||||
|
||||
#### 4.0.3: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Refresh page title after changing tab name (#4850) @kazuhitoyokoi
|
||||
- Add Japanese translations for v4.0.2 (again) (#4853) @kazuhitoyokoi
|
||||
- Stay in quick-add mode following context menu insert (#4883) @knolleary
|
||||
- Do not include Junction type in quick-add for virtual links (#4879) @knolleary
|
||||
- Multiplayer cursor tracking (#4845) @knolleary
|
||||
- Hide add-flow options when disabled via editorTheme (#4869) @knolleary
|
||||
- Fix env-var config select when multiple defined (#4872) @knolleary
|
||||
- Fix subflow outbound-link filter (#4857) @GogoVega
|
||||
- Add French translations for v4.0.2 (#4856) @GogoVega
|
||||
- Fix moving link wires (#4851) @knolleary
|
||||
- Adjust type search dialog position to prevent x-overflow (#4844) @Steve-Mcl
|
||||
- fix: modulesInUse might be undefined (#4838) @lorenz-maurer
|
||||
- Add Japanese translations for v4.0.2 (#4849) @kazuhitoyokoi
|
||||
- Fix menu to enable/disable selection when it's a group (#4828) @GogoVega
|
||||
|
||||
Runtime
|
||||
|
||||
- Update dependencies (#4874) @knolleary
|
||||
- GitHub: Add citation file to enable "Cite this repository" feature (#4861) @lobis
|
||||
- Remove use of util.log (#4875) @knolleary
|
||||
|
||||
Nodes
|
||||
|
||||
- Fix invalid property error in range node example (#4855)
|
||||
- Fix typo in flow example name (#4854) @kazuhitoyokoi
|
||||
- Move SNI, ALPN and Verify Server cert out of check (#4882) @hardillb
|
||||
- Set status of mqtt nodes to "disconnected" when deregistered from broker (#4878) @Steve-Mcl
|
||||
- MQTT: Ensure will payload is a string (#4873) @knolleary
|
||||
- Let batch node terminate "early" if msg.parts set to end of sequence (#4829) @dceejay
|
||||
- Fix unintentional Capitalisation in Split node name (#4835) @dceejay
|
||||
|
||||
#### 4.0.2: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Use a more subtle border on the header (#4818) @bonanitech
|
||||
- Improve the editor's French translations (#4824) @GogoVega
|
||||
- Clean up orphaned editors (#4821) @Steve-Mcl
|
||||
- Fix node validation if the property is not required (#4812) @GogoVega
|
||||
- Ensure mermaid.min.js is cached properly between loads of the editor (#4817) @knolleary
|
||||
|
||||
Runtime
|
||||
|
||||
- Allow auth cookie name to be customised (#4815) @knolleary
|
||||
- Guard against undefined sessions in multiplayer (#4816) @knolleary
|
||||
|
||||
#### 4.0.1: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Ensure subflow instance credential property values are extracted (#4802) @knolleary
|
||||
- Use `_ADD_` value for both `add new...` and `none` options (#4800) @GogoVega
|
||||
- Fix the config node select value assignment (#4788) @GogoVega
|
||||
- Add tooltip for number of subflow instance on info tab (#4786) @kazuhitoyokoi
|
||||
- Add Japanese translations for v4.0.0 (#4785) @kazuhitoyokoi
|
||||
|
||||
Runtime
|
||||
|
||||
- Ensure group nodes are properly exported in /flow api (#4803) @knolleary
|
||||
|
||||
Nodes
|
||||
|
||||
- Joins: make using msg.parts optional in join node (#4796) @dceejay
|
||||
- HTTP Request: UI proxy should setup agents for both http_proxy and https_proxy (#4794) @Steve-Mcl
|
||||
- HTTP Request: Remove default user agent (#4791) @Steve-Mcl
|
||||
|
||||
#### 4.0.0: Milestone Release
|
||||
|
||||
This marks the next major release of Node-RED. The following changes represent
|
||||
those added since the last beta. Check the beta release details below for the complete
|
||||
list.
|
||||
|
||||
Breaking Changes
|
||||
|
||||
- Node-RED now requires Node 18.x or later. At the time of release, we recommend
|
||||
using Node 20.
|
||||
|
||||
Editor
|
||||
|
||||
- Add `httpStaticCors` (#4761) @knolleary
|
||||
- Update dependencies (#4763) @knolleary
|
||||
- Sync master to dev (#4756) @knolleary
|
||||
- Add tooltip and message validation to `typedInput` (#4747) @GogoVega
|
||||
- Replace bcrypt with @node-rs/bcrypt (#4744) @knolleary
|
||||
- Export Nodes dialog refinement (#4746) @Steve-Mcl
|
||||
|
||||
#### 4.0.0-beta.4: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Fix the Sidebar Config is not refreshed after a deploy (#4734) @GogoVega
|
||||
- Fix checkboxes are not updated when calling `typedInput("value", "")` (#4729) @GogoVega
|
||||
- Fix panning with middle mouse button on windows 10/11 (#4716) @corentin-sodebo-voile
|
||||
- Add Japanese translation for sidebar tooltip (#4727) @kazuhitoyokoi
|
||||
- Translate the number of items selected in the options list (#4730) @GogoVega
|
||||
- Fix a checkbox should return a Boolean value and not the string `on` (#4715) @GogoVega
|
||||
- Deleting a grouped node should update the group (#4714) @GogoVega
|
||||
- Change the Config Node cursor to `pointer` (#4711) @GogoVega
|
||||
- Add missing tooltips to Sidebar (#4713) @GogoVega
|
||||
- Allow nodes to return additional history entries in onEditSave (#4710) @knolleary
|
||||
- Update to Monaco 0.49.0 (#4725) @Steve-Mcl
|
||||
- Add Japanese translations for 4.0.0-beta.3 (#4726) @kazuhitoyokoi
|
||||
- Show lock on deploy if user is read-only (#4706) @knolleary
|
||||
|
||||
Runtime
|
||||
|
||||
- Ensure all CSS variables are in the output file (#3743) @bonanitech
|
||||
- Add httpAdminCookieOptions (#4718) @knolleary
|
||||
- chore: migrate deprecated `util.isArray` (#4724) @Rotzbua
|
||||
- Add --version cli args (#4707) @knolleary
|
||||
- feat(grunt): fail if files are missing (#4739) @Rotzbua
|
||||
- fix(node-red-pi): node-red not started by path (#4736) @Rotzbua
|
||||
- fix(editor): remove trailing slash (#4735) @Rotzbua
|
||||
- fix: remove deprecated mqtt.js (#4733) @Rotzbua
|
||||
|
||||
Nodes
|
||||
|
||||
- Perform Proxy logic more like cURL (#4616) @Steve-Mcl
|
||||
|
||||
#### 4.0.0-beta.3: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Improve background-deploy notification handling (#4692) @knolleary
|
||||
- Hide workspace tab on middle mouse click (#4657) @Steve-Mcl
|
||||
- multiplayer: Add user presence indicators (#4666) @knolleary
|
||||
- Enable updating dependency node of package.json in project feature (#4676) @kazuhitoyokoi
|
||||
- Add French translations for 4.0.0-beta.2 (#4681) @GogoVega
|
||||
- Add Japanese translations for 4.0.0-beta.2 (#4674) @kazuhitoyokoi
|
||||
- Fix saving of conf-type properties in module packaged subflows (#4658) @knolleary
|
||||
- Add npm install timeout notification (#4662) @hardillb
|
||||
- Fix undo of subflow env property edits (#4667) @knolleary
|
||||
- Fix three error typos in monaco.js (#4660) @JoshuaCWebDeveloper
|
||||
- docs: Add closing paragraph tag (#4664) @ZJvandeWeg
|
||||
- Avoid login loops when autoLogin enabled but login fails (#4684) @knolleary
|
||||
|
||||
Runtime
|
||||
|
||||
- Allow blank strings to be used for env var property substitutions (#4672) @knolleary
|
||||
- Use rfdc for cloning pure JSON values (#4679) @knolleary
|
||||
- fix: remove outdated Node 11+ check (#4314) @Rotzbua
|
||||
- feat(ci): add new nodejs v22 (#4694) @Rotzbua
|
||||
- fix(node): increase required node >=18.5 (#4690) @Rotzbua
|
||||
- fix(dns): remove outdated node check (#4689) @Rotzbua
|
||||
- fix(polyfill): remove import module polyfill (#4688) @Rotzbua
|
||||
- Fix typo (#4686) @Rotzbua
|
||||
|
||||
Nodes
|
||||
|
||||
- Pass full error object in Function node and copy over cause property (#4685) @knolleary
|
||||
- Replacing vm.createScript in favour of vm.Script (#4534) @patlux
|
||||
|
||||
#### 4.0.0-beta.2: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Introduce multiplayer feature (#4629) @knolleary
|
||||
- Separate the "add new config-node" option into a new (+) button (#4627) @GogoVega
|
||||
- Retain Palette categories collapsed and filter to localStorage (#4634) @knolleary
|
||||
- Ensure palette filter reapplies and clear up unknown categories (#4637) @knolleary
|
||||
- Add support for plugin (only) modules to the palette manager (#4620) @knolleary
|
||||
- Update monaco to latest and node types to 18 LTS (#4615) @Steve-Mcl
|
||||
|
||||
Runtime
|
||||
|
||||
- Fix handling of subflow config-node select type in sf module (#4643) @knolleary
|
||||
- Comms API updates (#4628) @knolleary
|
||||
- Add French translations for 4.0.0-beta.1 (#4621) @GogoVega
|
||||
- Add Japanese translations for 4.0.0-beta.1 (#4612) @kazuhitoyokoi
|
||||
|
||||
Nodes
|
||||
- Fix change node handling of replacing with boolean (#4639) @knolleary
|
||||
|
||||
#### 4.0.0-beta.1: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- Click on id in debug panel highlights node or flow (#4439) @ralphwetzel
|
||||
- Support config selection in a subflow env var (#4587) @Steve-Mcl
|
||||
- Add timestamp formatting options to TypedInput (#4468) @knolleary
|
||||
- Allow RED.view.select to select links (#4553) @lgrkvst
|
||||
- Add auto-complete to flow/global/env typedInput types (#4480) @knolleary
|
||||
- Improve the appearance of the Node-RED primary header (#4598) @joepavitt
|
||||
|
||||
Runtime
|
||||
|
||||
- let settings.httpNodeAuth accept single middleware or array of middlewares (#4572) @kevinGodell
|
||||
- Upgrade to JSONata 2.x (#4590) @knolleary
|
||||
- Bump minimum version to node 18 (#4571) @knolleary
|
||||
- npm: Remove production flag on npm invocation (#4347) @ZJvandeWeg
|
||||
- Timer testing fix (#4367) @hlovdal
|
||||
- Bump to 4.0.0-dev (#4322) @knolleary
|
||||
|
||||
Nodes
|
||||
|
||||
- TCP node - when resetting, if no payload, stay disconnected @dceejay
|
||||
- HTML node: add option for collecting attributes and content (#4513) @gorenje
|
||||
- let split node specify property to split on, and join auto join correctly (#4386) @dceejay
|
||||
- Add RFC4180 compliant mode to CSV node (#4540) @Steve-Mcl
|
||||
- Fix change node to return boolean if asked (#4525) @dceejay
|
||||
- Let msg.reset reset Tcp request node connection when in stay connected mode (#4406) @dceejay
|
||||
- Let debug node status msg length be settable via settings (#4402) @dceejay
|
||||
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
|
||||
|
||||
#### Older Releases
|
||||
|
||||
Change logs for older releases are available on GitHub: https://github.com/node-red/node-red/releases
|
@ -1,7 +0,0 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use this software, please cite it as below."
|
||||
title: "Node-RED"
|
||||
authors:
|
||||
- family-names: "OpenJS Foundation"
|
||||
- family-names: "Contributors"
|
||||
url: "https://nodered.org"
|
@ -1,74 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at team@nodered.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
@ -7,18 +7,11 @@ We welcome contributions, but request you follow these guidelines.
|
||||
- [Pull-Requests](#pull-requests)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
|
||||
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
|
||||
behavior to the project's core team at team@nodered.org.
|
||||
|
||||
## Raising issues
|
||||
|
||||
Please raise any bug reports on the relevant project's issue tracker. Be sure to
|
||||
search the list to see if your issue has already been raised.
|
||||
|
||||
If your issue is more of a question on how to do something with Node-RED, please
|
||||
consider using the [community forum](https://discourse.nodered.org/).
|
||||
|
||||
A good bug report is one that make it easy for us to understand what you were
|
||||
trying to do and what went wrong.
|
||||
|
||||
@ -29,34 +22,33 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog.
|
||||
At a minimum, please include:
|
||||
|
||||
- Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly.
|
||||
- Version of Node.js - what does `node -v` say?
|
||||
- Version of node.js - what does `node -v` say?
|
||||
|
||||
## Feature requests
|
||||
|
||||
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
|
||||
|
||||
If you want to raise a pull-request with a new feature, or a refactoring
|
||||
of existing code, please come and discuss it with us first. We prefer to
|
||||
do it that way to make sure your time and effort is well spent on something
|
||||
that fits with our goals.
|
||||
of existing code, it may well get rejected if you haven't discussed it on
|
||||
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
|
||||
|
||||
If you've got a bug-fix or similar for us, then you are most welcome to
|
||||
get it raised - just make sure you link back to the issue it's fixing and
|
||||
try to include some tests!
|
||||
### Contributor License Agreement
|
||||
|
||||
All contributors need to sign the OpenJS Foundation's Contributor License Agreement.
|
||||
It is an online process and quick to do. If you raise a pull-request without
|
||||
having signed the CLA, you will be prompted to do so automatically.
|
||||
In order for us to accept pull-requests, the contributor must first complete
|
||||
a Contributor License Agreement (CLA). This clarifies the intellectual
|
||||
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.
|
||||
- `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream.
|
||||
- `dev` - this is the branch for new feature development targeting the next milestone release.
|
||||
If you are an IBMer, please contact us directly as the contribution process is
|
||||
slightly different.
|
||||
|
||||
### Coding standards
|
||||
|
||||
|
641
Gruntfile.js
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
* 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.
|
||||
@ -15,35 +15,17 @@
|
||||
**/
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs-extra");
|
||||
var sass = require("sass");
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
var nodemonArgs = ["-V"];
|
||||
var nodemonArgs = ["-v"];
|
||||
var flowFile = grunt.option('flowFile');
|
||||
if (flowFile) {
|
||||
nodemonArgs.push(flowFile);
|
||||
process.env.NODE_RED_ENABLE_PROJECTS=false;
|
||||
}
|
||||
var userDir = grunt.option('userDir');
|
||||
if (userDir) {
|
||||
nodemonArgs.push("-u");
|
||||
nodemonArgs.push(userDir);
|
||||
}
|
||||
|
||||
var browserstack = grunt.option('browserstack');
|
||||
if (browserstack) {
|
||||
process.env.BROWSERSTACK = true;
|
||||
}
|
||||
var nonHeadless = grunt.option('non-headless');
|
||||
if (nonHeadless) {
|
||||
process.env.NODE_RED_NON_HEADLESS = true;
|
||||
}
|
||||
const pkg = grunt.file.readJSON('package.json');
|
||||
process.env.NODE_RED_PACKAGE_VERSION = pkg.version;
|
||||
grunt.initConfig({
|
||||
pkg: pkg,
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
paths: {
|
||||
dist: ".dist"
|
||||
},
|
||||
@ -55,29 +37,10 @@ module.exports = function(grunt) {
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
|
||||
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
|
||||
all: { src: ['test/**/*_spec.js'] },
|
||||
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
|
||||
nodes: { src: ["test/nodes/**/*_spec.js"]}
|
||||
},
|
||||
webdriver: {
|
||||
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: {
|
||||
options: {
|
||||
jshintrc:true
|
||||
@ -92,20 +55,22 @@ module.exports = function(grunt) {
|
||||
//"loopfunc": true, // allow functions to be defined in loops
|
||||
//"sub": true // don't warn that foo['bar'] should be written as foo.bar
|
||||
},
|
||||
// all: [
|
||||
// 'Gruntfile.js',
|
||||
// 'red.js',
|
||||
// 'packages/**/*.js'
|
||||
// ],
|
||||
// core: {
|
||||
// files: {
|
||||
// src: [
|
||||
// 'Gruntfile.js',
|
||||
// 'red.js',
|
||||
// 'packages/**/*.js',
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js',
|
||||
'nodes/core/*/*.js',
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
core: {
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'red.js',
|
||||
'red/**/*.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
nodes: {
|
||||
files: {
|
||||
src: [ 'nodes/core/*/*.js' ]
|
||||
@ -113,7 +78,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
editor: {
|
||||
files: {
|
||||
src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ]
|
||||
src: [ 'editor/js/**/*.js' ]
|
||||
}
|
||||
},
|
||||
tests: {
|
||||
@ -121,7 +86,7 @@ module.exports = function(grunt) {
|
||||
src: ['test/**/*.js']
|
||||
},
|
||||
options: {
|
||||
"expr": true
|
||||
"expr": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -130,191 +95,111 @@ module.exports = function(grunt) {
|
||||
separator: ";",
|
||||
},
|
||||
build: {
|
||||
src: [
|
||||
// Ensure editor source files are concatenated in
|
||||
// the right order
|
||||
"packages/node_modules/@node-red/editor-client/src/js/polyfills.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/red.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/events.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/hooks.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/i18n.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/settings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/user.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/comms.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/runtime.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/multiplayer.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/text/bidi.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/text/format.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/state.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/plugins.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/nodes.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/font-awesome.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/history.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/validators.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/utils.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/actions.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/diff.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/palette.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editor.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tray.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/library.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/search.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/group.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
|
||||
src: [
|
||||
// Ensure editor source files are concatenated in
|
||||
// the right order
|
||||
"editor/js/main.js",
|
||||
"editor/js/events.js",
|
||||
"editor/js/i18n.js",
|
||||
"editor/js/settings.js",
|
||||
"editor/js/user.js",
|
||||
"editor/js/comms.js",
|
||||
"editor/js/ui/state.js",
|
||||
"editor/js/nodes.js",
|
||||
"editor/js/history.js",
|
||||
"editor/js/validators.js",
|
||||
"editor/js/ui/deploy.js",
|
||||
"editor/js/ui/menu.js",
|
||||
"editor/js/ui/keyboard.js",
|
||||
"editor/js/ui/tabs.js",
|
||||
"editor/js/ui/popover.js",
|
||||
"editor/js/ui/workspaces.js",
|
||||
"editor/js/ui/view.js",
|
||||
"editor/js/ui/sidebar.js",
|
||||
"editor/js/ui/palette.js",
|
||||
"editor/js/ui/tab-info.js",
|
||||
"editor/js/ui/tab-config.js",
|
||||
"editor/js/ui/editor.js",
|
||||
"editor/js/ui/clipboard.js",
|
||||
"editor/js/ui/library.js",
|
||||
"editor/js/ui/notifications.js",
|
||||
"editor/js/ui/subflow.js",
|
||||
"editor/js/ui/touch/radialMenu.js",
|
||||
"editor/js/ui/typedInput.js"
|
||||
],
|
||||
dest: "public/red/red.js"
|
||||
},
|
||||
vendor: {
|
||||
files: [
|
||||
{
|
||||
src: [
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"node_modules/marked/marked.min.js",
|
||||
"node_modules/dompurify/dist/purify.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
|
||||
"node_modules/i18next/i18next.min.js",
|
||||
"node_modules/i18next-http-backend/i18nextHttpBackend.min.js",
|
||||
"node_modules/jquery-i18next/jquery-i18next.min.js",
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js"
|
||||
},
|
||||
// {
|
||||
// src: [
|
||||
// // TODO: resolve relative resource paths in
|
||||
// // bootstrap/FA/jquery
|
||||
// ],
|
||||
// dest: "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css"
|
||||
// },
|
||||
{
|
||||
src: [
|
||||
"node_modules/jsonata/jsonata-es5.min.js",
|
||||
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
|
||||
],
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js",
|
||||
},
|
||||
{
|
||||
src: "node_modules/mermaid/dist/mermaid.min.js",
|
||||
nonull: true,
|
||||
dest: "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js",
|
||||
},
|
||||
]
|
||||
files: {
|
||||
"public/vendor/vendor.js": [
|
||||
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
|
||||
"editor/vendor/bootstrap/js/bootstrap.min.js",
|
||||
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
|
||||
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
|
||||
"editor/vendor/marked/marked.min.js",
|
||||
"editor/vendor/orion/built-editor.min.js",
|
||||
"editor/vendor/d3/d3.v3.min.js",
|
||||
"editor/vendor/i18next/i18next.min.js"
|
||||
],
|
||||
"public/vendor/vendor.css": [
|
||||
"editor/vendor/orion/built-editor.css"
|
||||
// TODO: resolve relative resource paths in
|
||||
// bootstrap/FA/jquery
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
build: {
|
||||
files: {
|
||||
'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js'
|
||||
'public/red/red.min.js': 'public/red/red.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
sass: {
|
||||
build: {
|
||||
options: {
|
||||
implementation: sass,
|
||||
outputStyle: 'compressed'
|
||||
},
|
||||
files: [{
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css',
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss'
|
||||
dest: 'public/red/style.min.css',
|
||||
src: 'editor/sass/style.scss'
|
||||
},
|
||||
{
|
||||
dest: 'public/vendor/bootstrap/css/bootstrap.min.css',
|
||||
src: 'editor/vendor/bootstrap/css/bootstrap.css'
|
||||
}]
|
||||
}
|
||||
},
|
||||
jsonlint: {
|
||||
messages: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/nodes/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/editor-client/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/runtime/locales/**/*.json'
|
||||
]
|
||||
},
|
||||
keymaps: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
]
|
||||
}
|
||||
},
|
||||
attachCopyright: {
|
||||
js: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/public/red/red.min.js',
|
||||
'packages/node_modules/@node-red/editor-client/public/red/main.min.js'
|
||||
'public/red/red.min.js'
|
||||
]
|
||||
},
|
||||
css: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/public/red/style.min.css'
|
||||
'public/red/style.min.css'
|
||||
]
|
||||
}
|
||||
},
|
||||
clean: {
|
||||
build: {
|
||||
src: [
|
||||
"packages/node_modules/@node-red/editor-client/public/red",
|
||||
"packages/node_modules/@node-red/editor-client/public/index.html",
|
||||
"packages/node_modules/@node-red/editor-client/public/favicon.ico",
|
||||
"packages/node_modules/@node-red/editor-client/public/icons",
|
||||
"packages/node_modules/@node-red/editor-client/public/vendor",
|
||||
"packages/node_modules/@node-red/editor-client/public/types/node",
|
||||
"packages/node_modules/@node-red/editor-client/public/types/node-red",
|
||||
"public/red",
|
||||
"public/index.html",
|
||||
"public/favicon.ico",
|
||||
"public/icons",
|
||||
"public/vendor"
|
||||
]
|
||||
},
|
||||
release: {
|
||||
@ -326,54 +211,35 @@ module.exports = function(grunt) {
|
||||
watch: {
|
||||
js: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/**/*.js'
|
||||
'editor/js/**/*.js'
|
||||
],
|
||||
tasks: ['copy:build','concat',/*'uglify',*/ 'attachCopyright:js']
|
||||
tasks: ['concat','uglify','attachCopyright:js']
|
||||
},
|
||||
sass: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss'
|
||||
'editor/sass/**/*.scss'
|
||||
],
|
||||
tasks: ['sass','attachCopyright:css']
|
||||
},
|
||||
json: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/nodes/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/editor-client/locales/**/*.json',
|
||||
'packages/node_modules/@node-red/runtime/locales/**/*.json'
|
||||
'nodes/core/locales/en-US/messages.json',
|
||||
'red/api/locales/en-US/editor.json',
|
||||
'red/runtime/locales/en-US/runtime.json'
|
||||
],
|
||||
tasks: ['jsonlint:messages']
|
||||
},
|
||||
keymaps: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js/keymap.json'
|
||||
],
|
||||
tasks: ['jsonlint:keymaps','copy:build']
|
||||
},
|
||||
tours: {
|
||||
files: [
|
||||
'packages/node_modules/@node-red/editor-client/src/tours/**/*.js'
|
||||
],
|
||||
tasks: ['copy:build']
|
||||
},
|
||||
misc: {
|
||||
files: [
|
||||
'CHANGELOG.md'
|
||||
],
|
||||
tasks: ['copy:build']
|
||||
}
|
||||
},
|
||||
|
||||
nodemon: {
|
||||
/* uses .nodemonignore */
|
||||
dev: {
|
||||
script: 'packages/node_modules/node-red/red.js',
|
||||
script: 'red.js',
|
||||
options: {
|
||||
args: nodemonArgs,
|
||||
ext: 'js,html,json',
|
||||
watch: [
|
||||
'packages/node_modules',
|
||||
'!packages/node_modules/@node-red/editor-client'
|
||||
'red','nodes'
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -390,77 +256,57 @@ module.exports = function(grunt) {
|
||||
|
||||
copy: {
|
||||
build: {
|
||||
files:[
|
||||
{
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js',
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js'
|
||||
},
|
||||
{
|
||||
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json',
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/images',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/red/images/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/vendor',
|
||||
src: [
|
||||
'ace/**',
|
||||
'jquery/css/base/**',
|
||||
'font-awesome/**',
|
||||
'monaco/dist/**',
|
||||
'monaco/types/extraLibs.js',
|
||||
'monaco/style.css',
|
||||
'monaco/monaco-bootstrap.js'
|
||||
],
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src',
|
||||
src: [
|
||||
'types/node/**/*.ts',
|
||||
'types/node-red/*.ts',
|
||||
],
|
||||
expand: true,
|
||||
dest: 'packages/node_modules/@node-red/editor-client/public/'
|
||||
},
|
||||
{
|
||||
cwd: 'packages/node_modules/@node-red/editor-client/src/icons',
|
||||
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/'
|
||||
}
|
||||
]
|
||||
files:[{
|
||||
cwd: 'editor/images',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'public/red/images/'
|
||||
},
|
||||
{
|
||||
cwd: 'editor/vendor',
|
||||
src: [
|
||||
'ace/**',
|
||||
//'bootstrap/css/**',
|
||||
'bootstrap/img/**',
|
||||
'jquery/css/**',
|
||||
'font-awesome/**'
|
||||
],
|
||||
expand: true,
|
||||
dest: 'public/vendor/'
|
||||
},
|
||||
{
|
||||
cwd: 'editor/icons',
|
||||
src: '**',
|
||||
expand: true,
|
||||
dest: 'public/icons/'
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
src: ['editor/index.html','editor/favicon.ico'],
|
||||
dest: 'public/',
|
||||
flatten: true
|
||||
}]
|
||||
},
|
||||
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: {
|
||||
@ -468,96 +314,20 @@ module.exports = function(grunt) {
|
||||
mode: '755'
|
||||
},
|
||||
release: {
|
||||
// Target-specific file/dir lists and/or options go here.
|
||||
src: [
|
||||
"packages/node_modules/@node-red/nodes/core/hardware/nrgpio",
|
||||
"packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/node-red-*sh"
|
||||
path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*')
|
||||
]
|
||||
}
|
||||
},
|
||||
'npm-command': {
|
||||
options: {
|
||||
cmd: "pack",
|
||||
cwd: "<%= paths.dist %>/modules"
|
||||
},
|
||||
'node-red': { options: { args: [__dirname+'/packages/node_modules/node-red'] } },
|
||||
'@node-red/editor-api': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-api'] } },
|
||||
'@node-red/editor-client': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-client'] } },
|
||||
'@node-red/nodes': { options: { args: [__dirname+'/packages/node_modules/@node-red/nodes'] } },
|
||||
'@node-red/registry': { options: { args: [__dirname+'/packages/node_modules/@node-red/registry'] } },
|
||||
'@node-red/runtime': { options: { args: [__dirname+'/packages/node_modules/@node-red/runtime'] } },
|
||||
'@node-red/util': { options: { args: [__dirname+'/packages/node_modules/@node-red/util'] } }
|
||||
|
||||
|
||||
},
|
||||
mkdir: {
|
||||
release: {
|
||||
options: {
|
||||
create: ['<%= paths.dist %>/modules']
|
||||
},
|
||||
},
|
||||
},
|
||||
compress: {
|
||||
release: {
|
||||
options: {
|
||||
archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip'
|
||||
},
|
||||
expand: true,
|
||||
cwd: 'packages/node_modules/',
|
||||
src: [
|
||||
'**',
|
||||
'!@node-red/editor-client/src/**'
|
||||
]
|
||||
}
|
||||
},
|
||||
jsdoc : {
|
||||
modules: {
|
||||
src: [
|
||||
'API.md',
|
||||
'packages/node_modules/node-red/lib/red.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/index.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/api/*.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/events.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/hooks.js',
|
||||
'packages/node_modules/@node-red/util/**/*.js',
|
||||
'packages/node_modules/@node-red/editor-api/lib/index.js',
|
||||
'packages/node_modules/@node-red/editor-api/lib/auth/index.js',
|
||||
'packages/node_modules/@node-red/registry/lib/index.js'
|
||||
],
|
||||
options: {
|
||||
destination: 'docs',
|
||||
configure: './jsdoc.json',
|
||||
fred: "hi there"
|
||||
}
|
||||
},
|
||||
_editor: {
|
||||
src: [
|
||||
'packages/node_modules/@node-red/editor-client/src/js'
|
||||
],
|
||||
options: {
|
||||
destination: 'packages/node_modules/@node-red/editor-client/docs',
|
||||
configure: './jsdoc.json'
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
jsdoc2md: {
|
||||
runtimeAPI: {
|
||||
options: {
|
||||
separators: true
|
||||
},
|
||||
src: [
|
||||
'packages/node_modules/@node-red/runtime/lib/index.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/api/*.js',
|
||||
'packages/node_modules/@node-red/runtime/lib/events.js'
|
||||
],
|
||||
dest: 'packages/node_modules/@node-red/runtime/docs/api.md'
|
||||
},
|
||||
nodeREDUtil: {
|
||||
options: {
|
||||
separators: true
|
||||
},
|
||||
src: 'packages/node_modules/@node-red/util/**/*.js',
|
||||
dest: 'packages/node_modules/@node-red/util/docs/api.md'
|
||||
cwd: '<%= paths.dist %>/',
|
||||
src: ['node-red-<%= pkg.version %>/**']
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -570,42 +340,16 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
grunt.loadNpmTasks('grunt-sass');
|
||||
grunt.loadNpmTasks('grunt-nodemon');
|
||||
grunt.loadNpmTasks('grunt-contrib-compress');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-chmod');
|
||||
grunt.loadNpmTasks('grunt-jsonlint');
|
||||
if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.loadNpmTasks('grunt-webdriver');
|
||||
}
|
||||
grunt.loadNpmTasks('grunt-jsdoc');
|
||||
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
|
||||
grunt.loadNpmTasks('grunt-npm-command');
|
||||
grunt.loadNpmTasks('grunt-mkdir');
|
||||
grunt.loadNpmTasks('grunt-simple-nyc');
|
||||
|
||||
grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () {
|
||||
const nodemon = require('nodemon');
|
||||
this.async();
|
||||
const options = this.options();
|
||||
options.script = this.data.script;
|
||||
let callback;
|
||||
if (options.callback) {
|
||||
callback = options.callback;
|
||||
delete options.callback;
|
||||
} else {
|
||||
callback = function(nodemonApp) {
|
||||
nodemonApp.on('log', function (event) {
|
||||
console.log(event.colour);
|
||||
});
|
||||
};
|
||||
}
|
||||
callback(nodemon(options));
|
||||
});
|
||||
|
||||
grunt.registerMultiTask('attachCopyright', function() {
|
||||
var files = this.data.src;
|
||||
var copyright = "/**\n"+
|
||||
" * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+
|
||||
" * Copyright 2013, 2015 IBM Corp.\n"+
|
||||
" *\n"+
|
||||
" * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+
|
||||
" * you may not use this file except in compliance with the License.\n"+
|
||||
@ -621,7 +365,7 @@ module.exports = function(grunt) {
|
||||
" **/\n";
|
||||
|
||||
if (files) {
|
||||
for (var i=0; i<files.length; i++) {
|
||||
for (var i=0;i<files.length;i++) {
|
||||
var file = files[i];
|
||||
if (!grunt.file.exists(file)) {
|
||||
grunt.log.warn('File '+ file + ' not found');
|
||||
@ -642,38 +386,6 @@ module.exports = function(grunt) {
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('verifyPackageDependencies', function() {
|
||||
var done = this.async();
|
||||
var verifyDependencies = require("./scripts/verify-package-dependencies.js");
|
||||
verifyDependencies().then(function(failures) {
|
||||
if (failures.length > 0) {
|
||||
failures.forEach(f => grunt.log.error(f));
|
||||
grunt.fail.fatal("Failed to verify package dependencies");
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('verifyUiTestDependencies', function() {
|
||||
if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.fail.fatal('You need to install the UI test dependencies first.\nUse the script in "scripts/install-ui-test-dependencies.sh"');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
grunt.registerTask('generatePublishScript',
|
||||
'Generates a script to publish build output to npm',
|
||||
function () {
|
||||
const done = this.async();
|
||||
const generatePublishScript = require("./scripts/generate-publish-script.js");
|
||||
generatePublishScript().then(function(output) {
|
||||
grunt.log.writeln(output);
|
||||
|
||||
const filePath = path.join(grunt.config.get('paths.dist'),"modules","publish.sh");
|
||||
grunt.file.write(filePath,output);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('setDevEnv',
|
||||
'Sets NODE_ENV=development so non-minified assets are used',
|
||||
function () {
|
||||
@ -682,42 +394,23 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('default',
|
||||
'Builds editor content then runs code style checks and unit tests on all components',
|
||||
['build','verifyPackageDependencies','jshint:editor','nyc:all']);
|
||||
|
||||
grunt.registerTask('no-coverage',
|
||||
'Builds editor content then runs code style checks and unit tests on all components without code coverage',
|
||||
['build','verifyPackageDependencies','jshint:editor','simplemocha:all']);
|
||||
|
||||
['build','test-core','test-editor','test-nodes']);
|
||||
|
||||
grunt.registerTask('test-core',
|
||||
'Runs code style check and unit tests on core runtime code',
|
||||
['build','nyc:core']);
|
||||
['jshint:core','simplemocha:core']);
|
||||
|
||||
grunt.registerTask('test-editor',
|
||||
'Runs code style check on editor code',
|
||||
['jshint:editor']);
|
||||
|
||||
if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) {
|
||||
grunt.registerTask('test-ui',
|
||||
'Builds editor content then runs unit tests on editor ui',
|
||||
['verifyUiTestDependencies']);
|
||||
} else {
|
||||
grunt.registerTask('test-ui',
|
||||
'Builds editor content then runs unit tests on editor ui',
|
||||
['verifyUiTestDependencies','build','jshint:editor','webdriver:all']);
|
||||
}
|
||||
|
||||
grunt.registerTask('test-nodes',
|
||||
'Runs unit tests on core nodes',
|
||||
['build','nyc:nodes']);
|
||||
['simplemocha:nodes']);
|
||||
|
||||
grunt.registerTask('build',
|
||||
'Builds editor content',
|
||||
['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']);
|
||||
|
||||
grunt.registerTask('build-dev',
|
||||
'Developer mode: build dev version',
|
||||
['clean:build','concat:build','concat:vendor','copy:build','sass:build','setDevEnv']);
|
||||
['clean:build','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy:build','attachCopyright']);
|
||||
|
||||
grunt.registerTask('dev',
|
||||
'Developer mode: run node-red, watch for source changes and build/restart',
|
||||
@ -725,18 +418,6 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.registerTask('release',
|
||||
'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']);
|
||||
};
|
||||
|
1
LICENSE
@ -1,4 +1,3 @@
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
50
README.md
@ -1,69 +1,69 @@
|
||||
# Node-RED
|
||||
|
||||
https://nodered.org
|
||||
http://nodered.org
|
||||
|
||||
[](https://github.com/node-red/node-red/actions?query=branch%3Amaster)
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
Low-code programming for event-driven applications.
|
||||
A visual tool for wiring the Internet of Things.
|
||||
|
||||

|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
Check out https://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
Check out http://nodered.org/docs/getting-started/ for full instructions on getting
|
||||
started.
|
||||
|
||||
1. `sudo npm install -g --unsafe-perm node-red`
|
||||
1. `sudo npm install -g node-red`
|
||||
2. `node-red`
|
||||
3. Open <http://localhost:1880>
|
||||
|
||||
## 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
|
||||
|
||||
If you want to run the latest code from git, here's how to get started:
|
||||
|
||||
1. Clone the code:
|
||||
1. Install grunt, the build tool
|
||||
|
||||
npm install -g grunt-cli
|
||||
|
||||
2. Clone the code:
|
||||
|
||||
git clone https://github.com/node-red/node-red.git
|
||||
cd node-red
|
||||
|
||||
2. Install the node-red dependencies
|
||||
3. Install the node-red dependencies
|
||||
|
||||
npm install
|
||||
|
||||
3. Build the code
|
||||
4. Build the code
|
||||
|
||||
npm run build
|
||||
grunt build
|
||||
|
||||
4. Run
|
||||
5. Run
|
||||
|
||||
npm start
|
||||
node red.js
|
||||
|
||||
## Contributing
|
||||
|
||||
Before raising a pull-request, please read our
|
||||
[contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md).
|
||||
|
||||
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
|
||||
behavior to any of the project's core team at team@nodered.org.
|
||||
|
||||
## Authors
|
||||
|
||||
Node-RED is a project of the [OpenJS Foundation](http://openjsf.org).
|
||||
Node-RED is a creation of [IBM Emerging Technology](http://ibm.com/blogs/et).
|
||||
|
||||
It is maintained by:
|
||||
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
* And many others...
|
||||
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
|
||||
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
|
||||
|
||||
For more open-source projects from IBM, head over [here](http://ibm.github.io).
|
||||
|
||||
## 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).
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly.
|
@ -1,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");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -29,16 +29,15 @@ do
|
||||
done
|
||||
|
||||
# Find the real location of this script
|
||||
CURRENT_PATH=$(pwd)
|
||||
SCRIPT_PATH=$(readlink -f "$0")
|
||||
while [ -h "${SCRIPT_PATH}" ]; do
|
||||
cd "$(dirname "${SCRIPT_PATH}")" || exit 1
|
||||
P=$(basename "${SCRIPT_PATH}")
|
||||
SCRIPT_PATH=$(readlink "${P}")
|
||||
CURRENT_PATH=`pwd`
|
||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
||||
while([ -h "${SCRIPT_PATH}" ]); do
|
||||
cd "`dirname "${SCRIPT_PATH}"`"
|
||||
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
||||
done
|
||||
cd "$(dirname "${SCRIPT_PATH}")" > /dev/null || exit 1
|
||||
SCRIPT_PATH=$(pwd)
|
||||
cd "$CURRENT_PATH" || exit 1
|
||||
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
|
||||
SCRIPT_PATH="`pwd`";
|
||||
cd $CURRENT_PATH
|
||||
|
||||
# Run Node-RED
|
||||
exec /usr/bin/env node ${OPTIONS} "${SCRIPT_PATH}"/../red.js ${ARGS}
|
||||
/usr/bin/env node $OPTIONS $SCRIPT_PATH/../red.js $ARGS
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
BIN
editor/icons/alert.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
editor/icons/arduino.png
Normal file
After Width: | Height: | Size: 603 B |
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
BIN
editor/icons/bluetooth.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
editor/icons/bridge-dash.png
Normal file
After Width: | Height: | Size: 609 B |
BIN
editor/icons/bridge.png
Normal file
After Width: | Height: | Size: 575 B |
BIN
editor/icons/comment.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
editor/icons/db.png
Normal file
After Width: | Height: | Size: 459 B |
BIN
editor/icons/debug.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
editor/icons/envelope.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
editor/icons/feed.png
Normal file
After Width: | Height: | Size: 378 B |
BIN
editor/icons/file.png
Normal file
After Width: | Height: | Size: 255 B |
BIN
editor/icons/function.png
Normal file
After Width: | Height: | Size: 457 B |
BIN
editor/icons/hash.png
Normal file
After Width: | Height: | Size: 502 B |
BIN
editor/icons/inject.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
editor/icons/leveldb.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
editor/icons/light.png
Normal file
After Width: | Height: | Size: 639 B |
BIN
editor/icons/mongodb.png
Normal file
After Width: | Height: | Size: 414 B |
BIN
editor/icons/mouse.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
editor/icons/node-changed.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/icons/node-error.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/icons/range.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
editor/icons/redis.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
editor/icons/rpi.png
Normal file
After Width: | Height: | Size: 482 B |
BIN
editor/icons/serial.png
Normal file
After Width: | Height: | Size: 273 B |
BIN
editor/icons/subflow.png
Normal file
After Width: | Height: | Size: 439 B |
BIN
editor/icons/swap.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
editor/icons/switch.png
Normal file
After Width: | Height: | Size: 509 B |
BIN
editor/icons/template.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
editor/icons/timer.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
editor/icons/trigger.png
Normal file
After Width: | Height: | Size: 258 B |
BIN
editor/icons/twitter.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
editor/icons/watch.png
Normal file
After Width: | Height: | Size: 591 B |
BIN
editor/icons/white-globe.png
Normal file
After Width: | Height: | Size: 707 B |
BIN
editor/images/deploy-flows-o.png
Normal file
After Width: | Height: | Size: 291 B |
BIN
editor/images/deploy-flows.png
Normal file
After Width: | Height: | Size: 386 B |
BIN
editor/images/deploy-full-o.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
editor/images/deploy-full.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
editor/images/deploy-nodes-o.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
editor/images/deploy-nodes.png
Normal file
After Width: | Height: | Size: 392 B |
BIN
editor/images/grip.png
Normal file
After Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
BIN
editor/images/node-red.png
Normal file
After Width: | Height: | Size: 1019 B |
BIN
editor/images/pw_maze_white.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
editor/images/subflow_tab.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
editor/images/typedInput/09.png
Normal file
After Width: | Height: | Size: 638 B |
BIN
editor/images/typedInput/az.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
editor/images/typedInput/bool.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
editor/images/typedInput/json.png
Normal file
After Width: | Height: | Size: 588 B |
BIN
editor/images/typedInput/re.png
Normal file
After Width: | Height: | Size: 502 B |
125
editor/js/comms.js
Normal file
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Copyright 2014, 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.comms = (function() {
|
||||
|
||||
var errornotification = null;
|
||||
var clearErrorTimer = null;
|
||||
|
||||
var subscriptions = {};
|
||||
var ws;
|
||||
var pendingAuth = false;
|
||||
var reconnectAttempts = 0;
|
||||
|
||||
function connectWS() {
|
||||
var path = location.hostname;
|
||||
var port = location.port;
|
||||
if (port.length !== 0) {
|
||||
path = path+":"+port;
|
||||
}
|
||||
path = path+document.location.pathname;
|
||||
path = path+(path.slice(-1) == "/"?"":"/")+"comms";
|
||||
path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path;
|
||||
|
||||
var auth_tokens = RED.settings.get("auth-tokens");
|
||||
pendingAuth = (auth_tokens!=null);
|
||||
|
||||
function completeConnection() {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
ws.send(JSON.stringify({subscribe:t}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ws = new WebSocket(path);
|
||||
ws.onopen = function() {
|
||||
reconnectAttempts = 0;
|
||||
if (errornotification) {
|
||||
clearErrorTimer = setTimeout(function() {
|
||||
errornotification.close();
|
||||
errornotification = null;
|
||||
},1000);
|
||||
}
|
||||
if (pendingAuth) {
|
||||
ws.send(JSON.stringify({auth:auth_tokens.access_token}));
|
||||
} else {
|
||||
completeConnection();
|
||||
}
|
||||
}
|
||||
ws.onmessage = function(event) {
|
||||
var msg = JSON.parse(event.data);
|
||||
if (pendingAuth && msg.auth == "ok") {
|
||||
pendingAuth = false;
|
||||
completeConnection();
|
||||
} else if (msg.topic) {
|
||||
for (var t in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(t)) {
|
||||
var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$");
|
||||
if (re.test(msg.topic)) {
|
||||
var subscribers = subscriptions[t];
|
||||
if (subscribers) {
|
||||
for (var i=0;i<subscribers.length;i++) {
|
||||
subscribers[i](msg.topic,msg.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ws.onclose = function() {
|
||||
if (reconnectAttempts > 5 && errornotification == null) {
|
||||
errornotification = RED.notify(RED._("notification.error",{message:RED._("notification.errors.lostConnection")}),"error",true);
|
||||
} else if (clearErrorTimer) {
|
||||
clearTimeout(clearErrorTimer);
|
||||
clearErrorTimer = null;
|
||||
}
|
||||
reconnectAttempts++;
|
||||
setTimeout(connectWS,1000);
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe(topic,callback) {
|
||||
if (subscriptions[topic] == null) {
|
||||
subscriptions[topic] = [];
|
||||
}
|
||||
subscriptions[topic].push(callback);
|
||||
if (ws && ws.readyState == 1) {
|
||||
ws.send(JSON.stringify({subscribe:topic}));
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribe(topic,callback) {
|
||||
if (subscriptions[topic]) {
|
||||
for (var i=0;i<subscriptions[topic].length;i++) {
|
||||
if (subscriptions[topic][i] === callback) {
|
||||
subscriptions[topic].splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subscriptions[topic].length === 0) {
|
||||
delete subscriptions[topic];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
connect: connectWS,
|
||||
subscribe: subscribe,
|
||||
unsubscribe:unsubscribe
|
||||
}
|
||||
})();
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
* 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.
|
||||
@ -32,23 +32,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
function emit() {
|
||||
var evt = arguments[0]
|
||||
var args = Array.prototype.slice.call(arguments,1);
|
||||
if (RED.events.DEBUG) {
|
||||
console.warn(evt,args);
|
||||
}
|
||||
function emit(evt,arg) {
|
||||
if (handlers[evt]) {
|
||||
let cpyHandlers = [...handlers[evt]];
|
||||
|
||||
for (var i=0;i<cpyHandlers.length;i++) {
|
||||
try {
|
||||
cpyHandlers[i].apply(null, args);
|
||||
} catch(err) {
|
||||
console.warn("RED.events.emit error: ["+evt+"] "+(err.toString()));
|
||||
console.warn(err);
|
||||
}
|
||||
for (var i=0;i<handlers[evt].length;i++) {
|
||||
handlers[evt][i](arg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return {
|
290
editor/js/history.js
Normal file
@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
// 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);
|
||||
});
|
||||
|
||||
if (ev.node.type === 'subflow') {
|
||||
$("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(ev.node.name);
|
||||
}
|
||||
} 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
43
editor/js/i18n.js
Normal 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);
|
||||
}
|
||||
}
|
||||
})();
|
235
editor/js/main.js
Normal file
@ -0,0 +1,235 @@
|
||||
/**
|
||||
* 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.
|
||||
**/
|
||||
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-spinner").hide();
|
||||
$(".palette-scroll").show();
|
||||
$("#palette-search").show();
|
||||
loadFlows();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadFlows() {
|
||||
$.ajax({
|
||||
headers: {
|
||||
"Accept":"application/json"
|
||||
},
|
||||
cache: false,
|
||||
url: 'flows',
|
||||
success: function(nodes) {
|
||||
RED.nodes.import(nodes);
|
||||
RED.nodes.dirty(false);
|
||||
RED.view.redraw(true);
|
||||
RED.comms.subscribe("status/#",function(topic,msg) {
|
||||
var parts = topic.split("/");
|
||||
var node = RED.nodes.node(parts[1]);
|
||||
if (node) {
|
||||
if (msg.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);
|
||||
$.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");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var statusEnabled = false;
|
||||
function toggleStatus(state) {
|
||||
statusEnabled = state;
|
||||
RED.view.status(statusEnabled);
|
||||
}
|
||||
|
||||
function loadEditor() {
|
||||
RED.menu.init({id:"btn-sidemenu",
|
||||
options: [
|
||||
{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-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}
|
||||
]},
|
||||
null,
|
||||
{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:[]}
|
||||
]},
|
||||
{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}
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function(){}},
|
||||
{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},
|
||||
null
|
||||
]},
|
||||
{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},
|
||||
]},
|
||||
null,
|
||||
{id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp},
|
||||
{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")
|
||||
},
|
||||
{id:"menu-item-node-red-version", label:"v"+RED.settings.version}
|
||||
]
|
||||
});
|
||||
|
||||
RED.user.init();
|
||||
|
||||
RED.library.init();
|
||||
RED.palette.init();
|
||||
RED.sidebar.init();
|
||||
RED.subflow.init();
|
||||
RED.workspaces.init();
|
||||
RED.clipboard.init();
|
||||
RED.view.init();
|
||||
RED.editor.init();
|
||||
|
||||
RED.deploy.init(RED.settings.theme("deployButton",null));
|
||||
|
||||
RED.keyboard.add(/* ? */ 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 {
|
||||
};
|
||||
})();
|
1033
editor/js/nodes.js
Normal file
148
editor/js/settings.js
Normal file
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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]];
|
||||
}
|
||||
return v;
|
||||
} catch(err) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
load: load,
|
||||
set: set,
|
||||
get: get,
|
||||
remove: remove,
|
||||
|
||||
theme: theme
|
||||
}
|
||||
})
|
||||
();
|
185
editor/js/ui/clipboard.js
Normal file
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
resizable: false,
|
||||
buttons: [
|
||||
{
|
||||
id: "clipboard-dialog-ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
RED.view.importNodes($("#clipboard-import").val());
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "clipboard-dialog-close",
|
||||
text: RED._("common.label.close"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
|
||||
dialogContainer = dialog.children(".dialog-form");
|
||||
|
||||
exportNodesDialog = '<div class="form-row">'+
|
||||
'<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> '+RED._("clipboard.nodes")+'</label>'+
|
||||
'<textarea readonly 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-export" rows="5"></textarea>'+
|
||||
'</div>'+
|
||||
'<div class="form-tips">'+
|
||||
RED._("clipboard.selectNodes")+
|
||||
'</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>';
|
||||
}
|
||||
|
||||
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));
|
||||
$("#clipboard-dialog-ok").show();
|
||||
$("#clipboard-dialog-cancel").show();
|
||||
$("#clipboard-dialog-close").hide();
|
||||
$("#clipboard-dialog-ok").button("disable");
|
||||
$("#clipboard-import").keyup(validateImport);
|
||||
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
|
||||
|
||||
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
|
||||
}
|
||||
|
||||
function exportNodes() {
|
||||
dialogContainer.empty();
|
||||
dialogContainer.append($(exportNodesDialog));
|
||||
$("#clipboard-dialog-ok").hide();
|
||||
$("#clipboard-dialog-cancel").hide();
|
||||
$("#clipboard-dialog-close").show();
|
||||
var selection = RED.view.selection();
|
||||
if (selection.nodes) {
|
||||
var nns = RED.nodes.createExportableNodeSet(selection.nodes);
|
||||
$("#clipboard-export")
|
||||
.val(JSON.stringify(nns))
|
||||
.focus(function() {
|
||||
var textarea = $(this);
|
||||
textarea.select();
|
||||
textarea.mouseup(function() {
|
||||
textarea.unbind("mouseup");
|
||||
return false;
|
||||
})
|
||||
});
|
||||
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
|
||||
}
|
||||
}
|
||||
|
||||
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(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
|
||||
RED.keyboard.add(/* 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
|
||||
}
|
||||
})();
|
286
editor/js/ui/deploy.js
Normal file
@ -0,0 +1,286 @@
|
||||
/**
|
||||
* 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.deploy = (function() {
|
||||
|
||||
var deploymentTypes = {
|
||||
"full":{img:"red/images/deploy-full-o.png"},
|
||||
"nodes":{img:"red/images/deploy-nodes-o.png"},
|
||||
"flows":{img:"red/images/deploy-flows-o.png"}
|
||||
}
|
||||
|
||||
var ignoreDeployWarnings = {
|
||||
unknown: false,
|
||||
unusedConfig: false,
|
||||
invalid: false
|
||||
}
|
||||
|
||||
var deploymentType = "full";
|
||||
|
||||
function changeDeploymentType(type) {
|
||||
deploymentType = type;
|
||||
$("#btn-deploy img").attr("src",deploymentTypes[type].img);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* options:
|
||||
* type: "default" - Button with drop-down options - no further customisation available
|
||||
* type: "simple" - Button without dropdown. Customisations:
|
||||
* label: the text to display - default: "Deploy"
|
||||
* icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.png"
|
||||
*/
|
||||
function init(options) {
|
||||
options = options || {};
|
||||
var type = options.type || "default";
|
||||
|
||||
if (type == "default") {
|
||||
$('<li><span class="deploy-button-group button-group">'+
|
||||
'<a id="btn-deploy" class="deploy-button disabled" href="#"><img id="btn-deploy-icon" src="red/images/deploy-full-o.png"> <span>'+RED._("deploy.deploy")+'</span></a>'+
|
||||
'<a id="btn-deploy-options" data-toggle="dropdown" class="deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
RED.menu.init({id:"btn-deploy-options",
|
||||
options: [
|
||||
{id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.png",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}},
|
||||
{id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.png",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}},
|
||||
{id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.png",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}}
|
||||
]
|
||||
});
|
||||
} else if (type == "simple") {
|
||||
var label = options.label || RED._("deploy.deploy");
|
||||
var icon = 'red/images/deploy-full-o.png';
|
||||
if (options.hasOwnProperty('icon')) {
|
||||
icon = options.icon;
|
||||
}
|
||||
|
||||
$('<li><span class="deploy-button-group button-group">'+
|
||||
'<a id="btn-deploy" class="deploy-button disabled" href="#">'+
|
||||
(icon?'<img id="btn-deploy-icon" src="'+icon+'"> ':'')+
|
||||
'<span>'+label+'</span></a>'+
|
||||
'</span></li>').prependTo(".header-toolbar");
|
||||
}
|
||||
|
||||
$('#btn-deploy').click(function() { save(); });
|
||||
|
||||
$( "#node-dialog-confirm-deploy" ).dialog({
|
||||
title: "Confirm deploy",
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 550,
|
||||
height: "auto",
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("deploy.confirm.button.confirm"),
|
||||
click: function() {
|
||||
|
||||
var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked");
|
||||
if (ignoreChecked) {
|
||||
ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true;
|
||||
}
|
||||
save(true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("deploy.confirm.button.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
create: function() {
|
||||
$("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane")
|
||||
.prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+
|
||||
'<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+
|
||||
'<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+
|
||||
'<input type="hidden" id="node-dialog-confirm-deploy-type">'+
|
||||
'</div>');
|
||||
}
|
||||
});
|
||||
|
||||
RED.events.on('nodes:change',function(state) {
|
||||
if (state.dirty) {
|
||||
window.onbeforeunload = function() {
|
||||
return RED._("deploy.confirm.undeployedChanges");
|
||||
}
|
||||
$("#btn-deploy").removeClass("disabled");
|
||||
} else {
|
||||
window.onbeforeunload = null;
|
||||
$("#btn-deploy").addClass("disabled");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getNodeInfo(node) {
|
||||
var tabLabel = "";
|
||||
if (node.z) {
|
||||
var tab = RED.nodes.workspace(node.z);
|
||||
if (!tab) {
|
||||
tab = RED.nodes.subflow(node.z);
|
||||
tabLabel = tab.name;
|
||||
} else {
|
||||
tabLabel = tab.label;
|
||||
}
|
||||
}
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
return {tab:tabLabel,type:node.type,label:label};
|
||||
}
|
||||
function sortNodeInfo(A,B) {
|
||||
if (A.tab < B.tab) { return -1;}
|
||||
if (A.tab > B.tab) { return 1;}
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
if (A.name < B.name) { return -1;}
|
||||
if (A.name > B.name) { return 1;}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function save(force) {
|
||||
if (RED.nodes.dirty()) {
|
||||
//$("#debug-tab-clear").click(); // uncomment this to auto clear debug on deploy
|
||||
|
||||
if (!force) {
|
||||
var hasUnknown = false;
|
||||
var hasInvalid = false;
|
||||
var hasUnusedConfig = false;
|
||||
|
||||
var unknownNodes = [];
|
||||
var invalidNodes = [];
|
||||
|
||||
RED.nodes.eachNode(function(node) {
|
||||
hasInvalid = hasInvalid || !node.valid;
|
||||
if (!node.valid) {
|
||||
invalidNodes.push(getNodeInfo(node));
|
||||
}
|
||||
if (node.type === "unknown") {
|
||||
if (unknownNodes.indexOf(node.name) == -1) {
|
||||
unknownNodes.push(node.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
hasUnknown = unknownNodes.length > 0;
|
||||
|
||||
var unusedConfigNodes = [];
|
||||
RED.nodes.eachConfig(function(node) {
|
||||
if (node.users.length === 0) {
|
||||
unusedConfigNodes.push(getNodeInfo(node));
|
||||
hasUnusedConfig = true;
|
||||
}
|
||||
});
|
||||
|
||||
$( "#node-dialog-confirm-deploy-config" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).hide();
|
||||
$( "#node-dialog-confirm-deploy-unused" ).hide();
|
||||
|
||||
var showWarning = false;
|
||||
|
||||
if (hasUnknown && !ignoreDeployWarnings.unknown) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("unknown");
|
||||
$( "#node-dialog-confirm-deploy-unknown" ).show();
|
||||
$( "#node-dialog-confirm-deploy-unknown-list" )
|
||||
.html("<li>"+unknownNodes.join("</li><li>")+"</li>");
|
||||
} else if (hasInvalid && !ignoreDeployWarnings.invalid) {
|
||||
showWarning = true;
|
||||
$( "#node-dialog-confirm-deploy-type" ).val("invalid");
|
||||
$( "#node-dialog-confirm-deploy-config" ).show();
|
||||
invalidNodes.sort(sortNodeInfo);
|
||||
$( "#node-dialog-confirm-deploy-invalid-list" )
|
||||
.html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
|
||||
} else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) {
|
||||
// showWarning = true;
|
||||
// $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig");
|
||||
// $( "#node-dialog-confirm-deploy-unused" ).show();
|
||||
//
|
||||
// unusedConfigNodes.sort(sortNodeInfo);
|
||||
// $( "#node-dialog-confirm-deploy-unused-list" )
|
||||
// .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>");
|
||||
}
|
||||
if (showWarning) {
|
||||
$( "#node-dialog-confirm-deploy-hide" ).prop("checked",false);
|
||||
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var nns = RED.nodes.createCompleteNodeSet();
|
||||
|
||||
$("#btn-deploy-icon").removeClass('fa-download');
|
||||
$("#btn-deploy-icon").addClass('spinner');
|
||||
RED.nodes.dirty(false);
|
||||
|
||||
$.ajax({
|
||||
url:"flows",
|
||||
type: "POST",
|
||||
data: JSON.stringify(nns),
|
||||
contentType: "application/json; charset=utf-8",
|
||||
headers: {
|
||||
"Node-RED-Deployment-Type":deploymentType
|
||||
}
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
if (hasUnusedConfig) {
|
||||
RED.notify(
|
||||
'<p>'+RED._("deploy.successfulDeploy")+'</p>'+
|
||||
'<p>'+RED._("deploy.unusedConfigNodes")+' <a href="#" onclick="RED.sidebar.config.show(true); return false;">'+RED._("deploy.unusedConfigNodesLink")+'</a></p>',"success",false,6000);
|
||||
} else {
|
||||
RED.notify(RED._("deploy.successfulDeploy"),"success");
|
||||
}
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if (node.changed) {
|
||||
node.dirty = true;
|
||||
node.changed = false;
|
||||
}
|
||||
if(node.credentials) {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function (confNode) {
|
||||
if (confNode.credentials) {
|
||||
delete confNode.credentials;
|
||||
}
|
||||
});
|
||||
// Once deployed, cannot undo back to a clean state
|
||||
RED.history.markAllDirty();
|
||||
RED.view.redraw();
|
||||
RED.events.emit("deploy");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.nodes.dirty(true);
|
||||
if (xhr.responseText) {
|
||||
RED.notify(RED._("notification.error",{message:xhr.responseText}),"error");
|
||||
} else {
|
||||
RED.notify(RED._("notification.error",{message:RED._("deploy.errors.noResponse")}),"error");
|
||||
}
|
||||
}).always(function() {
|
||||
$("#btn-deploy-icon").removeClass('spinner');
|
||||
$("#btn-deploy-icon").addClass('fa-download');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: init
|
||||
}
|
||||
})();
|
1224
editor/js/ui/editor.js
Normal file
121
editor/js/ui/keyboard.js
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright 2013 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.keyboard = (function() {
|
||||
|
||||
var active = true;
|
||||
var handlers = {};
|
||||
|
||||
d3.select(window).on("keydown",function() {
|
||||
if (!active) { return; }
|
||||
var handler = handlers[d3.event.keyCode];
|
||||
if (handler && handler.ondown) {
|
||||
if (!handler.modifiers ||
|
||||
((!handler.modifiers.shift || d3.event.shiftKey) &&
|
||||
(!handler.modifiers.ctrl || d3.event.ctrlKey || d3.event.metaKey) &&
|
||||
(!handler.modifiers.alt || d3.event.altKey) )) {
|
||||
handler.ondown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
d3.select(window).on("keyup",function() {
|
||||
if (!active) { return; }
|
||||
var handler = handlers[d3.event.keyCode];
|
||||
if (handler && handler.onup) {
|
||||
if (!handler.modifiers ||
|
||||
((!handler.modifiers.shift || d3.event.shiftKey) &&
|
||||
(!handler.modifiers.ctrl || d3.event.ctrlKey || d3.event.metaKey) &&
|
||||
(!handler.modifiers.alt || d3.event.altKey) )) {
|
||||
handler.onup();
|
||||
}
|
||||
}
|
||||
});
|
||||
function addHandler(key,modifiers,ondown,onup) {
|
||||
var mod = modifiers;
|
||||
var cbdown = ondown;
|
||||
var cbup = onup;
|
||||
if (typeof modifiers == "function") {
|
||||
mod = {};
|
||||
cbdown = modifiers;
|
||||
cbup = ondown;
|
||||
}
|
||||
handlers[key] = {modifiers:mod, ondown:cbdown, onup:cbup};
|
||||
}
|
||||
function removeHandler(key) {
|
||||
delete handlers[key];
|
||||
}
|
||||
|
||||
|
||||
var dialog = null;
|
||||
|
||||
function showKeyboardHelp() {
|
||||
if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) {
|
||||
return;
|
||||
}
|
||||
if (!dialog) {
|
||||
dialog = $('<div id="keyboard-help-dialog" class="hide">'+
|
||||
'<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+
|
||||
'<table class="keyboard-shortcuts">'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">a</span></td><td>'+RED._("keyboard.selectAll")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Shift</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.selectAllConnected")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.addRemoveNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteSelected")+'</td></tr>'+
|
||||
'<tr><td> </td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">i</span></td><td>'+RED._("keyboard.importNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">e</span></td><td>'+RED._("keyboard.exportNode")+'</td></tr>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+
|
||||
'<table class="keyboard-shortcuts">'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Space</span></td><td>'+RED._("keyboard.toggleSidebar")+'</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteNode")+'</td></tr>'+
|
||||
'<tr><td></td><td></td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">c</span></td><td>'+RED._("keyboard.copyNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">x</span></td><td>'+RED._("keyboard.cutNode")+'</td></tr>'+
|
||||
'<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">v</span></td><td>'+RED._("keyboard.pasteNode")+'</td></tr>'+
|
||||
'</table>'+
|
||||
'</div>'+
|
||||
'</div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: "800",
|
||||
title:"Keyboard shortcuts",
|
||||
resizable: false,
|
||||
open: function() {
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function() {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dialog.dialog("open");
|
||||
}
|
||||
|
||||
return {
|
||||
add: addHandler,
|
||||
remove: removeHandler,
|
||||
disable: function(){ active = false;},
|
||||
enable: function(){ active = true; },
|
||||
|
||||
showHelp: showKeyboardHelp
|
||||
}
|
||||
|
||||
})();
|
469
editor/js/ui/library.js
Normal file
@ -0,0 +1,469 @@
|
||||
/**
|
||||
* 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.library = (function() {
|
||||
|
||||
|
||||
var exportToLibraryDialog;
|
||||
|
||||
function loadFlowLibrary() {
|
||||
$.getJSON("library/flows",function(data) {
|
||||
//console.log(data);
|
||||
|
||||
var buildMenu = function(data,root) {
|
||||
var i;
|
||||
var li;
|
||||
var a;
|
||||
var ul = document.createElement("ul");
|
||||
ul.id = "menu-item-import-library-submenu";
|
||||
ul.className = "dropdown-menu";
|
||||
if (data.d) {
|
||||
for (i in data.d) {
|
||||
if (data.d.hasOwnProperty(i)) {
|
||||
li = document.createElement("li");
|
||||
li.className = "dropdown-submenu pull-left";
|
||||
a = document.createElement("a");
|
||||
a.href="#";
|
||||
a.innerHTML = i;
|
||||
li.appendChild(a);
|
||||
li.appendChild(buildMenu(data.d[i],root+(root!==""?"/":"")+i));
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.f) {
|
||||
for (i in data.f) {
|
||||
if (data.f.hasOwnProperty(i)) {
|
||||
li = document.createElement("li");
|
||||
a = document.createElement("a");
|
||||
a.href="#";
|
||||
a.innerHTML = data.f[i];
|
||||
a.flowName = root+(root!==""?"/":"")+data.f[i];
|
||||
a.onclick = function() {
|
||||
$.get('library/flows/'+this.flowName, function(data) {
|
||||
RED.view.importNodes(data);
|
||||
});
|
||||
};
|
||||
li.appendChild(a);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ul;
|
||||
};
|
||||
var menu = buildMenu(data,"");
|
||||
//TODO: need an api in RED.menu for this
|
||||
$("#menu-item-import-library-submenu").replaceWith(menu);
|
||||
});
|
||||
}
|
||||
|
||||
function createUI(options) {
|
||||
var libraryData = {};
|
||||
var selectedLibraryItem = null;
|
||||
var libraryEditor = null;
|
||||
|
||||
// Orion editor has set/getText
|
||||
// ACE editor has set/getValue
|
||||
// normalise to set/getValue
|
||||
if (options.editor.setText) {
|
||||
// Orion doesn't like having pos passed in, so proxy the call to drop it
|
||||
options.editor.setValue = function(text,pos) {
|
||||
options.editor.setText.call(options.editor,text);
|
||||
}
|
||||
}
|
||||
if (options.editor.getText) {
|
||||
options.editor.getValue = options.editor.getText;
|
||||
}
|
||||
|
||||
function buildFileListItem(item) {
|
||||
var li = document.createElement("li");
|
||||
li.onmouseover = function(e) { $(this).addClass("list-hover"); };
|
||||
li.onmouseout = function(e) { $(this).removeClass("list-hover"); };
|
||||
return li;
|
||||
}
|
||||
|
||||
function buildFileList(root,data) {
|
||||
var ul = document.createElement("ul");
|
||||
var li;
|
||||
for (var i=0;i<data.length;i++) {
|
||||
var v = data[i];
|
||||
if (typeof v === "string") {
|
||||
// directory
|
||||
li = buildFileListItem(v);
|
||||
li.onclick = (function () {
|
||||
var dirName = v;
|
||||
return function(e) {
|
||||
var bcli = $('<li class="active"><span class="divider">/</span> <a href="#">'+dirName+'</a></li>');
|
||||
$("a",bcli).click(function(e) {
|
||||
$(this).parent().nextAll().remove();
|
||||
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
||||
});
|
||||
e.stopPropagation();
|
||||
});
|
||||
var bc = $("#node-dialog-library-breadcrumbs");
|
||||
$(".active",bc).removeClass("active");
|
||||
bc.append(bcli);
|
||||
$.getJSON("library/"+options.url+root+dirName,function(data) {
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data));
|
||||
});
|
||||
}
|
||||
})();
|
||||
li.innerHTML = '<i class="fa fa-folder"></i> '+v+"</i>";
|
||||
ul.appendChild(li);
|
||||
} else {
|
||||
// file
|
||||
li = buildFileListItem(v);
|
||||
li.innerHTML = v.name;
|
||||
li.onclick = (function() {
|
||||
var item = v;
|
||||
return function(e) {
|
||||
$(".list-selected",ul).removeClass("list-selected");
|
||||
$(this).addClass("list-selected");
|
||||
$.get("library/"+options.url+root+item.fn, function(data) {
|
||||
selectedLibraryItem = item;
|
||||
libraryEditor.setValue(data,-1);
|
||||
});
|
||||
}
|
||||
})();
|
||||
ul.appendChild(li);
|
||||
}
|
||||
}
|
||||
return ul;
|
||||
}
|
||||
|
||||
$('#node-input-name').css("width","60%").after(
|
||||
'<div class="btn-group" style="margin-left: 5px;">'+
|
||||
'<a id="node-input-'+options.type+'-lookup" class="editor-button" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></a>'+
|
||||
'<ul class="dropdown-menu pull-right" role="menu">'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">'+RED._("library.openLibrary")+'</a></li>'+
|
||||
'<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">'+RED._("library.saveToLibrary")+'</a></li>'+
|
||||
'</ul></div>'
|
||||
);
|
||||
|
||||
|
||||
|
||||
$('#node-input-'+options.type+'-menu-open-library').click(function(e) {
|
||||
$("#node-select-library").children().remove();
|
||||
var bc = $("#node-dialog-library-breadcrumbs");
|
||||
bc.children().first().nextAll().remove();
|
||||
libraryEditor.setValue('',-1);
|
||||
|
||||
$.getJSON("library/"+options.url,function(data) {
|
||||
$("#node-select-library").append(buildFileList("/",data));
|
||||
$("#node-dialog-library-breadcrumbs a").click(function(e) {
|
||||
$(this).parent().nextAll().remove();
|
||||
$("#node-select-library").children().first().replaceWith(buildFileList("/",data));
|
||||
e.stopPropagation();
|
||||
});
|
||||
$( "#node-dialog-library-lookup" ).dialog( "open" );
|
||||
});
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$('#node-input-'+options.type+'-menu-save-library').click(function(e) {
|
||||
//var found = false;
|
||||
var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
|
||||
//var buildPathList = function(data,root) {
|
||||
// var paths = [];
|
||||
// if (data.d) {
|
||||
// for (var i in data.d) {
|
||||
// var dn = root+(root==""?"":"/")+i;
|
||||
// var d = {
|
||||
// label:dn,
|
||||
// files:[]
|
||||
// };
|
||||
// for (var f in data.d[i].f) {
|
||||
// d.files.push(data.d[i].f[f].fn.split("/").slice(-1)[0]);
|
||||
// }
|
||||
// paths.push(d);
|
||||
// paths = paths.concat(buildPathList(data.d[i],root+(root==""?"":"/")+i));
|
||||
// }
|
||||
// }
|
||||
// return paths;
|
||||
//};
|
||||
$("#node-dialog-library-save-folder").attr("value","");
|
||||
|
||||
var filename = name.replace(/[^\w-]/g,"-");
|
||||
if (filename === "") {
|
||||
filename = "unnamed-"+options.type;
|
||||
}
|
||||
$("#node-dialog-library-save-filename").attr("value",filename+".js");
|
||||
|
||||
//var paths = buildPathList(libraryData,"");
|
||||
//$("#node-dialog-library-save-folder").autocomplete({
|
||||
// minLength: 0,
|
||||
// source: paths,
|
||||
// select: function( event, ui ) {
|
||||
// $("#node-dialog-library-save-filename").autocomplete({
|
||||
// minLength: 0,
|
||||
// source: ui.item.files
|
||||
// });
|
||||
// }
|
||||
//});
|
||||
|
||||
$( "#node-dialog-library-save" ).dialog( "open" );
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
libraryEditor = ace.edit('node-select-library-text');
|
||||
libraryEditor.setTheme("ace/theme/tomorrow");
|
||||
if (options.mode) {
|
||||
libraryEditor.getSession().setMode(options.mode);
|
||||
}
|
||||
libraryEditor.setOptions({
|
||||
readOnly: true,
|
||||
highlightActiveLine: false,
|
||||
highlightGutterLine: false
|
||||
});
|
||||
libraryEditor.renderer.$cursorLayer.element.style.opacity=0;
|
||||
libraryEditor.$blockScrolling = Infinity;
|
||||
|
||||
$( "#node-dialog-library-lookup" ).dialog({
|
||||
title: RED._("library.typeLibrary", {type:options.type}),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 800,
|
||||
height: 450,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
if (selectedLibraryItem) {
|
||||
for (var i=0;i<options.fields.length;i++) {
|
||||
var field = options.fields[i];
|
||||
$("#node-input-"+field).val(selectedLibraryItem[field]);
|
||||
}
|
||||
options.editor.setValue(libraryEditor.getValue(),-1);
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
var form = $("form",this);
|
||||
form.height(form.parent().height()-30);
|
||||
$("#node-select-library-text").height("100%");
|
||||
$(".form-row:last-child",form).children().height(form.height()-60);
|
||||
},
|
||||
resize: function(e) {
|
||||
var form = $("form",this);
|
||||
form.height(form.parent().height()-30);
|
||||
$(".form-row:last-child",form).children().height(form.height()-60);
|
||||
}
|
||||
});
|
||||
|
||||
function saveToLibrary(overwrite) {
|
||||
var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
if (name === "") {
|
||||
name = RED._("library.unnamedType",{type:options.type});
|
||||
}
|
||||
var filename = $("#node-dialog-library-save-filename").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
var pathname = $("#node-dialog-library-save-folder").val().replace(/(^\s*)|(\s*$)/g,"");
|
||||
if (filename === "" || !/.+\.js$/.test(filename)) {
|
||||
RED.notify(RED._("library.invalidFilename"),"warning");
|
||||
return;
|
||||
}
|
||||
var fullpath = pathname+(pathname===""?"":"/")+filename;
|
||||
if (!overwrite) {
|
||||
//var pathnameParts = pathname.split("/");
|
||||
//var exists = false;
|
||||
//var ds = libraryData;
|
||||
//for (var pnp in pathnameParts) {
|
||||
// if (ds.d && pathnameParts[pnp] in ds.d) {
|
||||
// ds = ds.d[pathnameParts[pnp]];
|
||||
// } else {
|
||||
// ds = null;
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//if (ds && ds.f) {
|
||||
// for (var f in ds.f) {
|
||||
// if (ds.f[f].fn == fullpath) {
|
||||
// exists = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//if (exists) {
|
||||
// $("#node-dialog-library-save-content").html(RED._("library.dialogSaveOverwrite",{libraryType:options.type,libraryName:fullpath}));
|
||||
// $("#node-dialog-library-save-confirm").dialog( "open" );
|
||||
// return;
|
||||
//}
|
||||
}
|
||||
var queryArgs = [];
|
||||
var data = {};
|
||||
for (var i=0;i<options.fields.length;i++) {
|
||||
var field = options.fields[i];
|
||||
if (field == "name") {
|
||||
data.name = name;
|
||||
} else {
|
||||
data[field] = $("#node-input-"+field).val();
|
||||
}
|
||||
}
|
||||
|
||||
data.text = options.editor.getValue();
|
||||
$.ajax({
|
||||
url:"library/"+options.url+'/'+fullpath,
|
||||
type: "POST",
|
||||
data: JSON.stringify(data),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function(data,textStatus,xhr) {
|
||||
RED.notify(RED._("library.savedType", {type:options.type}),"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
||||
});
|
||||
}
|
||||
$( "#node-dialog-library-save-confirm" ).dialog({
|
||||
title: RED._("library.saveToLibrary"),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
saveToLibrary(true);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
$( "#node-dialog-library-save" ).dialog({
|
||||
title: RED._("library.saveToLibrary"),
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 530,
|
||||
height: 230,
|
||||
buttons: [
|
||||
{
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
saveToLibrary(false);
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function exportFlow() {
|
||||
//TODO: don't rely on the main dialog
|
||||
var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes);
|
||||
$("#node-input-library-filename").attr('nodes',JSON.stringify(nns));
|
||||
exportToLibraryDialog.dialog( "open" );
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
if (RED.settings.theme("menu.menu-item-import-library") !== false) {
|
||||
loadFlowLibrary();
|
||||
}
|
||||
|
||||
exportToLibraryDialog = $('<div id="library-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
|
||||
.appendTo("body")
|
||||
.dialog({
|
||||
modal: true,
|
||||
autoOpen: false,
|
||||
width: 500,
|
||||
resizable: false,
|
||||
title: RED._("library.exportToLibrary"),
|
||||
buttons: [
|
||||
{
|
||||
id: "library-dialog-ok",
|
||||
text: RED._("common.label.ok"),
|
||||
click: function() {
|
||||
//TODO: move this to RED.library
|
||||
var flowName = $("#node-input-library-filename").val();
|
||||
if (!/^\s*$/.test(flowName)) {
|
||||
$.ajax({
|
||||
url:'library/flows/'+flowName,
|
||||
type: "POST",
|
||||
data: $("#node-input-library-filename").attr('nodes'),
|
||||
contentType: "application/json; charset=utf-8"
|
||||
}).done(function() {
|
||||
RED.library.loadFlowLibrary();
|
||||
RED.notify(RED._("library.savedNodes"),"success");
|
||||
}).fail(function(xhr,textStatus,err) {
|
||||
RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error");
|
||||
});
|
||||
}
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "library-dialog-cancel",
|
||||
text: RED._("common.label.cancel"),
|
||||
click: function() {
|
||||
$( this ).dialog( "close" );
|
||||
}
|
||||
}
|
||||
],
|
||||
open: function(e) {
|
||||
$(this).parent().find(".ui-dialog-titlebar-close").hide();
|
||||
RED.keyboard.disable();
|
||||
},
|
||||
close: function(e) {
|
||||
RED.keyboard.enable();
|
||||
}
|
||||
});
|
||||
exportToLibraryDialog.children(".dialog-form").append($(
|
||||
'<div class="form-row">'+
|
||||
'<label for="node-input-library-filename" data-i18n="[append]editor:library.filename"><i class="fa fa-file"></i> </label>'+
|
||||
'<input type="text" id="node-input-library-filename" data-i18n="[placeholder]editor:library.fullFilenamePlaceholder">'+
|
||||
'<input type="text" style="display: none;" />'+ // Second hidden input to prevent submit on Enter
|
||||
'</div>'
|
||||
));
|
||||
},
|
||||
create: createUI,
|
||||
loadFlowLibrary: loadFlowLibrary,
|
||||
|
||||
export: exportFlow
|
||||
}
|
||||
})();
|
257
editor/js/ui/menu.js
Normal file
@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Copyright 2014 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">'+opt.label+'</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
|
||||
}
|
||||
})();
|
65
editor/js/ui/notifications.js
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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.notify = (function() {
|
||||
var currentNotifications = [];
|
||||
var c = 0;
|
||||
return function(msg,type,fixed,timeout) {
|
||||
if (currentNotifications.length > 4) {
|
||||
var ll = currentNotifications.length;
|
||||
for (var i = 0;ll > 4 && i<currentNotifications.length;i+=1) {
|
||||
var notifiction = currentNotifications[i];
|
||||
if (!notifiction.fixed) {
|
||||
window.clearTimeout(notifiction.timeoutid);
|
||||
notifiction.close();
|
||||
ll -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
var n = document.createElement("div");
|
||||
n.id="red-notification-"+c;
|
||||
n.className = "notification";
|
||||
n.fixed = fixed;
|
||||
if (type) {
|
||||
n.className = "notification notification-"+type;
|
||||
}
|
||||
n.style.display = "none";
|
||||
n.innerHTML = msg;
|
||||
$("#notifications").append(n);
|
||||
$(n).slideDown(300);
|
||||
n.close = (function() {
|
||||
var nn = n;
|
||||
return function() {
|
||||
currentNotifications.splice(currentNotifications.indexOf(nn),1);
|
||||
$(nn).slideUp(300, function() {
|
||||
nn.parentNode.removeChild(nn);
|
||||
});
|
||||
};
|
||||
})();
|
||||
if (!fixed) {
|
||||
$(n).click((function() {
|
||||
var nn = n;
|
||||
return function() {
|
||||
nn.close();
|
||||
window.clearTimeout(nn.timeoutid);
|
||||
};
|
||||
})());
|
||||
n.timeoutid = window.setTimeout(n.close,timeout||3000);
|
||||
}
|
||||
currentNotifications.push(n);
|
||||
c+=1;
|
||||
return n;
|
||||
}
|
||||
})();
|
451
editor/js/ui/palette.js
Normal file
@ -0,0 +1,451 @@
|
||||
/**
|
||||
* 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.palette = (function() {
|
||||
|
||||
var exclusion = ['config','unknown','deprecated'];
|
||||
var core = ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced'];
|
||||
|
||||
var categoryContainers = {};
|
||||
|
||||
function createCategoryContainer(category, label){
|
||||
label = label || category.replace("_", " ");
|
||||
var catDiv = $('<div id="palette-container-'+category+'" class="palette-category palette-close hide">'+
|
||||
'<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-angle-down"></i><span>'+label+'</span></div>'+
|
||||
'<div class="palette-content" id="palette-base-category-'+category+'">'+
|
||||
'<div id="palette-'+category+'-input"></div>'+
|
||||
'<div id="palette-'+category+'-output"></div>'+
|
||||
'<div id="palette-'+category+'-function"></div>'+
|
||||
'</div>'+
|
||||
'</div>').appendTo("#palette-container");
|
||||
|
||||
categoryContainers[category] = {
|
||||
container: catDiv,
|
||||
close: function() {
|
||||
catDiv.removeClass("palette-open");
|
||||
catDiv.addClass("palette-closed");
|
||||
$("#palette-base-category-"+category).slideUp();
|
||||
$("#palette-header-"+category+" i").removeClass("expanded");
|
||||
},
|
||||
open: function() {
|
||||
catDiv.addClass("palette-open");
|
||||
catDiv.removeClass("palette-closed");
|
||||
$("#palette-base-category-"+category).slideDown();
|
||||
$("#palette-header-"+category+" i").addClass("expanded");
|
||||
},
|
||||
toggle: function() {
|
||||
if (catDiv.hasClass("palette-open")) {
|
||||
categoryContainers[category].close();
|
||||
} else {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$("#palette-header-"+category).on('click', function(e) {
|
||||
categoryContainers[category].toggle();
|
||||
});
|
||||
}
|
||||
|
||||
function setLabel(type, el,label, info) {
|
||||
var nodeWidth = 82;
|
||||
var nodeHeight = 25;
|
||||
var lineHeight = 20;
|
||||
var portHeight = 10;
|
||||
|
||||
var words = label.split(" ");
|
||||
|
||||
var displayLines = [];
|
||||
|
||||
var currentLine = words[0];
|
||||
var currentLineWidth = RED.view.calculateTextWidth(currentLine, "palette_label", 0);
|
||||
|
||||
for (var i=1;i<words.length;i++) {
|
||||
var newWidth = RED.view.calculateTextWidth(currentLine+" "+words[i], "palette_label", 0);
|
||||
if (newWidth < nodeWidth) {
|
||||
currentLine += " "+words[i];
|
||||
currentLineWidth = newWidth;
|
||||
} else {
|
||||
displayLines.push(currentLine);
|
||||
currentLine = words[i];
|
||||
currentLineWidth = RED.view.calculateTextWidth(currentLine, "palette_label", 0);
|
||||
}
|
||||
}
|
||||
displayLines.push(currentLine);
|
||||
|
||||
var lines = displayLines.join("<br/>");
|
||||
var multiLineNodeHeight = 8+(lineHeight*displayLines.length);
|
||||
el.css({height:multiLineNodeHeight+"px"});
|
||||
|
||||
var labelElement = el.find(".palette_label");
|
||||
labelElement.html(lines);
|
||||
|
||||
el.find(".palette_port").css({top:(multiLineNodeHeight/2-5)+"px"});
|
||||
|
||||
var popOverContent;
|
||||
try {
|
||||
var l = "<p><b>"+label+"</b></p>";
|
||||
if (label != type) {
|
||||
l = "<p><b>"+label+"</b><br/><i>"+type+"</i></p>";
|
||||
}
|
||||
popOverContent = $(l+(info?info:$("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim())
|
||||
.filter(function(n) {
|
||||
return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0)
|
||||
}).slice(0,2);
|
||||
} catch(err) {
|
||||
// Malformed HTML may cause errors. TODO: need to understand what can break
|
||||
// NON-NLS: internal debug
|
||||
console.log("Error generating pop-over label for ",type);
|
||||
console.log(err.toString());
|
||||
popOverContent = "<p><b>"+label+"</b></p><p>"+RED._("palette.noInfo")+"</p>";
|
||||
}
|
||||
|
||||
el.data('popover').setContent(popOverContent);
|
||||
}
|
||||
|
||||
function escapeNodeType(nt) {
|
||||
return nt.replace(" ","_").replace(".","_").replace(":","_");
|
||||
}
|
||||
|
||||
function addNodeType(nt,def) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
if ($("#palette_node_"+nodeTypeId).length) {
|
||||
return;
|
||||
}
|
||||
if (exclusion.indexOf(def.category)===-1) {
|
||||
|
||||
var category = def.category.replace(" ","_");
|
||||
var rootCategory = category.split("-")[0];
|
||||
|
||||
var d = document.createElement("div");
|
||||
d.id = "palette_node_"+nodeTypeId;
|
||||
d.type = nt;
|
||||
|
||||
var label;
|
||||
|
||||
if (typeof def.paletteLabel === "undefined") {
|
||||
label = /^(.*?)([ -]in|[ -]out)?$/.exec(nt)[1];
|
||||
} else {
|
||||
label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||"";
|
||||
}
|
||||
|
||||
|
||||
$('<div/>',{class:"palette_label"+(def.align=="right"?" palette_label_right":"")}).appendTo(d);
|
||||
|
||||
d.className="palette_node";
|
||||
|
||||
|
||||
if (def.icon) {
|
||||
var icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon);
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d);
|
||||
$('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
|
||||
}
|
||||
|
||||
d.style.backgroundColor = def.color;
|
||||
|
||||
if (def.outputs > 0) {
|
||||
var portOut = document.createElement("div");
|
||||
portOut.className = "palette_port palette_port_output";
|
||||
d.appendChild(portOut);
|
||||
}
|
||||
|
||||
if (def.inputs > 0) {
|
||||
var portIn = document.createElement("div");
|
||||
portIn.className = "palette_port palette_port_input";
|
||||
d.appendChild(portIn);
|
||||
}
|
||||
|
||||
if ($("#palette-base-category-"+rootCategory).length === 0) {
|
||||
if(core.indexOf(rootCategory) !== -1){
|
||||
createCategoryContainer(rootCategory, RED._("node-red:palette.label."+rootCategory, {defaultValue:rootCategory}));
|
||||
} else {
|
||||
var ns = def.set.id;
|
||||
createCategoryContainer(rootCategory, RED._(ns+":palette.label."+rootCategory, {defaultValue:rootCategory}));
|
||||
}
|
||||
}
|
||||
$("#palette-container-"+rootCategory).show();
|
||||
|
||||
if ($("#palette-"+category).length === 0) {
|
||||
$("#palette-base-category-"+rootCategory).append('<div id="palette-'+category+'"></div>');
|
||||
}
|
||||
|
||||
$("#palette-"+category).append(d);
|
||||
d.onmousedown = function(e) { e.preventDefault(); };
|
||||
|
||||
RED.popover.create({
|
||||
target:$(d),
|
||||
content: "hi",
|
||||
delay: { show: 750, hide: 50 }
|
||||
});
|
||||
|
||||
// $(d).popover({
|
||||
// title:d.type,
|
||||
// placement:"right",
|
||||
// trigger: "hover",
|
||||
// delay: { show: 750, hide: 50 },
|
||||
// html: true,
|
||||
// container:'body'
|
||||
// });
|
||||
$(d).click(function() {
|
||||
RED.view.focus();
|
||||
var helpText;
|
||||
if (nt.indexOf("subflow:") === 0) {
|
||||
helpText = marked(RED.nodes.subflow(nt.substring(8)).info||"");
|
||||
} else {
|
||||
helpText = $("script[data-help-name|='"+d.type+"']").html()||"";
|
||||
}
|
||||
var help = '<div class="node-help">'+helpText+"</div>";
|
||||
RED.sidebar.info.set(help);
|
||||
});
|
||||
var chart = $("#chart");
|
||||
var chartOffset = chart.offset();
|
||||
var chartSVG = $("#chart>svg").get(0);
|
||||
var activeSpliceLink;
|
||||
var mouseX;
|
||||
var mouseY;
|
||||
var spliceTimer;
|
||||
$(d).draggable({
|
||||
helper: 'clone',
|
||||
appendTo: 'body',
|
||||
revert: true,
|
||||
revertDuration: 50,
|
||||
start: function() {RED.view.focus();},
|
||||
stop: function() { d3.select('.link_splice').classed('link_splice',false); if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null;}},
|
||||
drag: function(e,ui) {
|
||||
// TODO: this is the margin-left of palette node. Hard coding
|
||||
// it here makes me sad
|
||||
ui.position.left += 17.5;
|
||||
if (def.inputs > 0 && def.outputs > 0) {
|
||||
mouseX = e.clientX - chartOffset.left+chart.scrollLeft();
|
||||
mouseY = e.clientY-chartOffset.top +chart.scrollTop();
|
||||
|
||||
if (!spliceTimer) {
|
||||
spliceTimer = setTimeout(function() {
|
||||
var nodes = [];
|
||||
var bestDistance = Infinity;
|
||||
var bestLink = null;
|
||||
if (chartSVG.getIntersectionList) {
|
||||
var svgRect = chartSVG.createSVGRect();
|
||||
svgRect.x = mouseX;
|
||||
svgRect.y = mouseY;
|
||||
svgRect.width = 1;
|
||||
svgRect.height = 1;
|
||||
nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
|
||||
mouseX /= RED.view.scale();
|
||||
mouseY /= RED.view.scale();
|
||||
} else {
|
||||
// Firefox doesn't do getIntersectionList and that
|
||||
// makes us sad
|
||||
mouseX /= RED.view.scale();
|
||||
mouseY /= RED.view.scale();
|
||||
nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
|
||||
}
|
||||
for (var i=0;i<nodes.length;i++) {
|
||||
if (d3.select(nodes[i]).classed('link_background')) {
|
||||
var length = nodes[i].getTotalLength();
|
||||
for (var j=0;j<length;j+=10) {
|
||||
var p = nodes[i].getPointAtLength(j);
|
||||
var d2 = ((p.x-mouseX)*(p.x-mouseX))+((p.y-mouseY)*(p.y-mouseY));
|
||||
if (d2 < 200 && d2 < bestDistance) {
|
||||
bestDistance = d2;
|
||||
bestLink = nodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeSpliceLink && activeSpliceLink !== bestLink) {
|
||||
d3.select(activeSpliceLink.parentNode).classed('link_splice',false);
|
||||
}
|
||||
if (bestLink) {
|
||||
d3.select(bestLink.parentNode).classed('link_splice',true)
|
||||
} else {
|
||||
d3.select('.link_splice').classed('link_splice',false);
|
||||
}
|
||||
if (activeSpliceLink !== bestLink) {
|
||||
if (bestLink) {
|
||||
$(ui.helper).data('splice',d3.select(bestLink).data()[0]);
|
||||
} else {
|
||||
$(ui.helper).removeData('splice');
|
||||
}
|
||||
}
|
||||
activeSpliceLink = bestLink;
|
||||
spliceTimer = null;
|
||||
},200);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var nodeInfo = null;
|
||||
if (def.category == "subflows") {
|
||||
$(d).dblclick(function(e) {
|
||||
RED.workspaces.show(nt.substring(8));
|
||||
e.preventDefault();
|
||||
});
|
||||
nodeInfo = marked(def.info||"");
|
||||
}
|
||||
setLabel(nt,$(d),label,nodeInfo);
|
||||
|
||||
var categoryNode = $("#palette-container-"+category);
|
||||
if (categoryNode.find(".palette_node").length === 1) {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function removeNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
var paletteNode = $("#palette_node_"+nodeTypeId);
|
||||
var categoryNode = paletteNode.closest(".palette-category");
|
||||
paletteNode.remove();
|
||||
if (categoryNode.find(".palette_node").length === 0) {
|
||||
if (categoryNode.find("i").hasClass("expanded")) {
|
||||
categoryNode.find(".palette-content").slideToggle();
|
||||
categoryNode.find("i").toggleClass("expanded");
|
||||
}
|
||||
}
|
||||
}
|
||||
function hideNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
$("#palette_node_"+nodeTypeId).hide();
|
||||
}
|
||||
|
||||
function showNodeType(nt) {
|
||||
var nodeTypeId = escapeNodeType(nt);
|
||||
$("#palette_node_"+nodeTypeId).show();
|
||||
}
|
||||
|
||||
function refreshNodeTypes() {
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var paletteNode = $("#palette_node_subflow_"+sf.id.replace(".","_"));
|
||||
var portInput = paletteNode.find(".palette_port_input");
|
||||
var portOutput = paletteNode.find(".palette_port_output");
|
||||
|
||||
if (portInput.length === 0 && sf.in.length > 0) {
|
||||
var portIn = document.createElement("div");
|
||||
portIn.className = "palette_port palette_port_input";
|
||||
paletteNode.append(portIn);
|
||||
} else if (portInput.length !== 0 && sf.in.length === 0) {
|
||||
portInput.remove();
|
||||
}
|
||||
|
||||
if (portOutput.length === 0 && sf.out.length > 0) {
|
||||
var portOut = document.createElement("div");
|
||||
portOut.className = "palette_port palette_port_output";
|
||||
paletteNode.append(portOut);
|
||||
} else if (portOutput.length !== 0 && sf.out.length === 0) {
|
||||
portOutput.remove();
|
||||
}
|
||||
setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||""));
|
||||
});
|
||||
}
|
||||
|
||||
function filterChange() {
|
||||
var val = $("#palette-search-input").val();
|
||||
if (val === "") {
|
||||
$("#palette-search-clear").hide();
|
||||
} else {
|
||||
$("#palette-search-clear").show();
|
||||
}
|
||||
|
||||
var re = new RegExp(val,'i');
|
||||
$("#palette-container .palette_node").each(function(i,el) {
|
||||
var currentLabel = $(el).find(".palette_label").text();
|
||||
if (val === "" || re.test(el.id) || re.test(currentLabel)) {
|
||||
$(this).show();
|
||||
} else {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
|
||||
for (var category in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(category)) {
|
||||
if (categoryContainers[category].container
|
||||
.find(".palette_node")
|
||||
.filter(function() { return $(this).css('display') !== 'none'}).length === 0) {
|
||||
categoryContainers[category].close();
|
||||
} else {
|
||||
categoryContainers[category].open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
$(".palette-spinner").show();
|
||||
if (RED.settings.paletteCategories) {
|
||||
RED.settings.paletteCategories.forEach(function(category){
|
||||
createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category}));
|
||||
});
|
||||
} else {
|
||||
core.forEach(function(category){
|
||||
createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category}));
|
||||
});
|
||||
}
|
||||
|
||||
$("#palette-search-input").focus(function(e) {
|
||||
RED.keyboard.disable();
|
||||
});
|
||||
$("#palette-search-input").blur(function(e) {
|
||||
RED.keyboard.enable();
|
||||
});
|
||||
|
||||
$("#palette-search-clear").on("click",function(e) {
|
||||
e.preventDefault();
|
||||
$("#palette-search-input").val("");
|
||||
filterChange();
|
||||
$("#palette-search-input").focus();
|
||||
});
|
||||
|
||||
$("#palette-search-input").val("");
|
||||
$("#palette-search-input").on("keyup",function() {
|
||||
filterChange();
|
||||
});
|
||||
|
||||
$("#palette-search-input").on("focus",function() {
|
||||
$("body").one("mousedown",function() {
|
||||
$("#palette-search-input").blur();
|
||||
});
|
||||
});
|
||||
|
||||
$("#palette-collapse-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(cat)) {
|
||||
categoryContainers[cat].close();
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#palette-expand-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categoryContainers) {
|
||||
if (categoryContainers.hasOwnProperty(cat)) {
|
||||
categoryContainers[cat].open();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
add:addNodeType,
|
||||
remove:removeNodeType,
|
||||
hide:hideNodeType,
|
||||
show:showNodeType,
|
||||
refresh:refreshNodeTypes
|
||||
};
|
||||
})();
|
79
editor/js/ui/popover.js
Normal 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();
|
||||
div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:targetPos.left+targetWidth+17});
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
})();
|
214
editor/js/ui/sidebar.js
Normal file
@ -0,0 +1,214 @@
|
||||
/**
|
||||
* 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.sidebar = (function() {
|
||||
|
||||
//$('#sidebar').tabs();
|
||||
var sidebar_tabs = RED.tabs.create({
|
||||
id:"sidebar-tabs",
|
||||
onchange:function(tab) {
|
||||
$("#sidebar-content").children().hide();
|
||||
$("#sidebar-footer").children().hide();
|
||||
if (tab.onchange) {
|
||||
tab.onchange.call(tab);
|
||||
}
|
||||
$(tab.content).show();
|
||||
if (tab.toolbar) {
|
||||
$(tab.toolbar).show();
|
||||
}
|
||||
},
|
||||
onremove: function(tab) {
|
||||
$(tab.content).hide();
|
||||
if (tab.onremove) {
|
||||
tab.onremove.call(tab);
|
||||
}
|
||||
},
|
||||
minimumActiveTabWidth: 110
|
||||
});
|
||||
|
||||
var knownTabs = {
|
||||
|
||||
};
|
||||
|
||||
function addTab(title,content,closeable,visible) {
|
||||
var options;
|
||||
if (typeof title === "string") {
|
||||
// TODO: legacy support in case anyone uses this...
|
||||
options = {
|
||||
id: content.id,
|
||||
label: title,
|
||||
name: title,
|
||||
content: content,
|
||||
closeable: closeable,
|
||||
visible: visible
|
||||
}
|
||||
} else if (typeof title === "object") {
|
||||
options = title;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$("#sidebar-content").append(options.content);
|
||||
$(options.content).hide();
|
||||
if (options.toolbar) {
|
||||
$("#sidebar-footer").append(options.toolbar);
|
||||
$(options.toolbar).hide();
|
||||
}
|
||||
$(options.content).hide();
|
||||
var id = options.id;
|
||||
|
||||
RED.menu.addItem("menu-item-view-menu",{
|
||||
id:"menu-item-view-menu-"+options.id,
|
||||
label:options.name,
|
||||
onselect:function() {
|
||||
showSidebar(options.id);
|
||||
},
|
||||
group: "sidebar-tabs"
|
||||
});
|
||||
|
||||
knownTabs[options.id] = options;
|
||||
|
||||
if (options.visible !== false) {
|
||||
sidebar_tabs.addTab(knownTabs[options.id]);
|
||||
}
|
||||
}
|
||||
|
||||
function removeTab(id) {
|
||||
sidebar_tabs.removeTab(id);
|
||||
$(knownTabs[id].content).remove();
|
||||
delete knownTabs[id];
|
||||
RED.menu.removeItem("menu-item-view-menu-"+id);
|
||||
}
|
||||
|
||||
var sidebarSeparator = {};
|
||||
$("#sidebar-separator").draggable({
|
||||
axis: "x",
|
||||
start:function(event,ui) {
|
||||
sidebarSeparator.closing = false;
|
||||
sidebarSeparator.opening = false;
|
||||
var winWidth = $(window).width();
|
||||
sidebarSeparator.start = ui.position.left;
|
||||
sidebarSeparator.chartWidth = $("#workspace").width();
|
||||
sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2;
|
||||
|
||||
|
||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
||||
sidebarSeparator.opening = true;
|
||||
var newChartRight = 7;
|
||||
$("#sidebar").addClass("closing");
|
||||
$("#workspace").css("right",newChartRight);
|
||||
$("#sidebar").width(0);
|
||||
RED.menu.setSelected("menu-item-sidebar",true);
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
sidebarSeparator.width = $("#sidebar").width();
|
||||
},
|
||||
drag: function(event,ui) {
|
||||
var d = ui.position.left-sidebarSeparator.start;
|
||||
var newSidebarWidth = sidebarSeparator.width-d;
|
||||
if (sidebarSeparator.opening) {
|
||||
newSidebarWidth -= 3;
|
||||
}
|
||||
|
||||
if (newSidebarWidth > 150) {
|
||||
if (sidebarSeparator.chartWidth+d < 200) {
|
||||
ui.position.left = 200+sidebarSeparator.start-sidebarSeparator.chartWidth;
|
||||
d = ui.position.left-sidebarSeparator.start;
|
||||
newSidebarWidth = sidebarSeparator.width-d;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSidebarWidth < 150) {
|
||||
if (!sidebarSeparator.closing) {
|
||||
$("#sidebar").addClass("closing");
|
||||
sidebarSeparator.closing = true;
|
||||
}
|
||||
if (!sidebarSeparator.opening) {
|
||||
newSidebarWidth = 150;
|
||||
ui.position.left = sidebarSeparator.width-(150 - sidebarSeparator.start);
|
||||
d = ui.position.left-sidebarSeparator.start;
|
||||
}
|
||||
} else if (newSidebarWidth > 150 && (sidebarSeparator.closing || sidebarSeparator.opening)) {
|
||||
sidebarSeparator.closing = false;
|
||||
$("#sidebar").removeClass("closing");
|
||||
}
|
||||
|
||||
var newChartRight = sidebarSeparator.chartRight-d;
|
||||
$("#workspace").css("right",newChartRight);
|
||||
$("#sidebar").width(newSidebarWidth);
|
||||
|
||||
sidebar_tabs.resize();
|
||||
RED.events.emit("sidebar:resize");
|
||||
},
|
||||
stop:function(event,ui) {
|
||||
if (sidebarSeparator.closing) {
|
||||
$("#sidebar").removeClass("closing");
|
||||
RED.menu.setSelected("menu-item-sidebar",false);
|
||||
if ($("#sidebar").width() < 180) {
|
||||
$("#sidebar").width(180);
|
||||
$("#workspace").css("right",187);
|
||||
}
|
||||
}
|
||||
$("#sidebar-separator").css("left","auto");
|
||||
$("#sidebar-separator").css("right",($("#sidebar").width()+2)+"px");
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
});
|
||||
|
||||
function toggleSidebar(state) {
|
||||
if (!state) {
|
||||
$("#main-container").addClass("sidebar-closed");
|
||||
} else {
|
||||
$("#main-container").removeClass("sidebar-closed");
|
||||
sidebar_tabs.resize();
|
||||
}
|
||||
RED.events.emit("sidebar:resize");
|
||||
}
|
||||
|
||||
function showSidebar(id) {
|
||||
if (id) {
|
||||
if (!containsTab(id)) {
|
||||
sidebar_tabs.addTab(knownTabs[id]);
|
||||
}
|
||||
sidebar_tabs.activateTab(id);
|
||||
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
||||
RED.menu.setSelected("menu-item-sidebar",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function containsTab(id) {
|
||||
return sidebar_tabs.contains(id);
|
||||
}
|
||||
|
||||
function init () {
|
||||
RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("menu-item-sidebar",!RED.menu.isSelected("menu-item-sidebar"));d3.event.preventDefault();});
|
||||
showSidebar();
|
||||
RED.sidebar.info.init();
|
||||
RED.sidebar.config.init();
|
||||
// hide info bar at start if screen rather narrow...
|
||||
if ($(window).width() < 600) { toggleSidebar(); }
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
addTab: addTab,
|
||||
removeTab: removeTab,
|
||||
show: showSidebar,
|
||||
containsTab: containsTab,
|
||||
toggleSidebar: toggleSidebar,
|
||||
}
|
||||
|
||||
})();
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
* Copyright 2013 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -13,11 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
@import "colors";
|
||||
@import "mixins";
|
||||
|
||||
@import "forms";
|
||||
@import "jquery";
|
||||
@import "typedInput";
|
||||
@import "editableList";
|
||||
RED.state = {
|
||||
DEFAULT: 0,
|
||||
MOVING: 1,
|
||||
JOINING: 2,
|
||||
MOVING_ACTIVE: 3,
|
||||
ADDING: 4,
|
||||
EDITING: 5,
|
||||
EXPORT: 6,
|
||||
IMPORT: 7,
|
||||
IMPORT_DRAGGING: 8
|
||||
}
|
608
editor/js/ui/subflow.js
Normal file
@ -0,0 +1,608 @@
|
||||
/**
|
||||
* 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.subflow = (function() {
|
||||
|
||||
|
||||
function getSubflow() {
|
||||
return RED.nodes.subflow(RED.workspaces.active());
|
||||
}
|
||||
|
||||
function findAvailableSubflowIOPosition(subflow,isInput) {
|
||||
var pos = {x:50,y:30};
|
||||
if (!isInput) {
|
||||
pos.x += 110;
|
||||
}
|
||||
for (var i=0;i<subflow.out.length+subflow.in.length;i++) {
|
||||
var port;
|
||||
if (i < subflow.out.length) {
|
||||
port = subflow.out[i];
|
||||
} else {
|
||||
port = subflow.in[i-subflow.out.length];
|
||||
}
|
||||
if (port.x == pos.x && port.y == pos.y) {
|
||||
pos.x += 55;
|
||||
i=0;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
function addSubflowInput() {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow.in.length === 1) {
|
||||
return;
|
||||
}
|
||||
var position = findAvailableSubflowIOPosition(subflow,true);
|
||||
var newInput = {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
z:subflow.id,
|
||||
i:subflow.in.length,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
id:RED.nodes.id()
|
||||
};
|
||||
var oldInCount = subflow.in.length;
|
||||
subflow.in.push(newInput);
|
||||
subflow.dirty = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
var result = refresh(true);
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
inputCount: oldInCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-input-add").addClass("active");
|
||||
$("#workspace-subflow-input-remove").removeClass("active");
|
||||
}
|
||||
|
||||
function removeSubflowInput() {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.in.length === 0) {
|
||||
return;
|
||||
}
|
||||
var removedInput = activeSubflow.in[0];
|
||||
var removedInputLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.source.type == "subflow" && l.source.z == activeSubflow.id && l.source.i == removedInput.i) {
|
||||
removedInputLinks.push(l);
|
||||
} else if (l.target.type == "subflow:"+activeSubflow.id) {
|
||||
removedInputLinks.push(l);
|
||||
}
|
||||
});
|
||||
removedInputLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
activeSubflow.in = [];
|
||||
$("#workspace-subflow-input-add").removeClass("active");
|
||||
$("#workspace-subflow-input-remove").addClass("active");
|
||||
activeSubflow.changed = true;
|
||||
return {subflowInputs: [ removedInput ], links:removedInputLinks};
|
||||
}
|
||||
|
||||
function addSubflowOutput(id) {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
var position = findAvailableSubflowIOPosition(subflow,false);
|
||||
|
||||
var newOutput = {
|
||||
type:"subflow",
|
||||
direction:"out",
|
||||
z:subflow.id,
|
||||
i:subflow.out.length,
|
||||
x:position.x,
|
||||
y:position.y,
|
||||
id:RED.nodes.id()
|
||||
};
|
||||
var oldOutCount = subflow.out.length;
|
||||
subflow.out.push(newOutput);
|
||||
subflow.dirty = true;
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = subflow.changed;
|
||||
subflow.changed = true;
|
||||
|
||||
var result = refresh(true);
|
||||
|
||||
var historyEvent = {
|
||||
t:'edit',
|
||||
node:subflow,
|
||||
dirty:wasDirty,
|
||||
changed:wasChanged,
|
||||
subflow: {
|
||||
outputCount: oldOutCount,
|
||||
instances: result.instances
|
||||
}
|
||||
};
|
||||
RED.history.push(historyEvent);
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
$("#workspace-subflow-output .spinner-value").html(subflow.out.length);
|
||||
}
|
||||
|
||||
function removeSubflowOutput(removedSubflowOutputs) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (activeSubflow.out.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (typeof removedSubflowOutputs === "undefined") {
|
||||
removedSubflowOutputs = [activeSubflow.out[activeSubflow.out.length-1]];
|
||||
}
|
||||
var removedLinks = [];
|
||||
removedSubflowOutputs.sort(function(a,b) { return b.i-a.i});
|
||||
for (i=0;i<removedSubflowOutputs.length;i++) {
|
||||
var output = removedSubflowOutputs[i];
|
||||
activeSubflow.out.splice(output.i,1);
|
||||
var subflowRemovedLinks = [];
|
||||
var subflowMovedLinks = [];
|
||||
RED.nodes.eachLink(function(l) {
|
||||
if (l.target.type == "subflow" && l.target.z == activeSubflow.id && l.target.i == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
}
|
||||
if (l.source.type == "subflow:"+activeSubflow.id) {
|
||||
if (l.sourcePort == output.i) {
|
||||
subflowRemovedLinks.push(l);
|
||||
} else if (l.sourcePort > output.i) {
|
||||
subflowMovedLinks.push(l);
|
||||
}
|
||||
}
|
||||
});
|
||||
subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)});
|
||||
subflowMovedLinks.forEach(function(l) { l.sourcePort--; });
|
||||
|
||||
removedLinks = removedLinks.concat(subflowRemovedLinks);
|
||||
for (var j=output.i;j<activeSubflow.out.length;j++) {
|
||||
activeSubflow.out[j].i--;
|
||||
activeSubflow.out[j].dirty = true;
|
||||
}
|
||||
}
|
||||
activeSubflow.changed = true;
|
||||
|
||||
return {subflowOutputs: removedSubflowOutputs, links: removedLinks}
|
||||
}
|
||||
|
||||
function refresh(markChange) {
|
||||
var activeSubflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
refreshToolbar(activeSubflow);
|
||||
var subflowInstances = [];
|
||||
if (activeSubflow) {
|
||||
RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
|
||||
subflowInstances.push({
|
||||
id: n.id,
|
||||
changed: n.changed
|
||||
});
|
||||
if (markChange) {
|
||||
n.changed = true;
|
||||
}
|
||||
n.inputs = activeSubflow.in.length;
|
||||
n.outputs = activeSubflow.out.length;
|
||||
while (n.outputs < n.ports.length) {
|
||||
n.ports.pop();
|
||||
}
|
||||
n.resize = true;
|
||||
n.dirty = true;
|
||||
RED.editor.updateNodeProperties(n);
|
||||
});
|
||||
RED.editor.validateNode(activeSubflow);
|
||||
return {
|
||||
instances: subflowInstances
|
||||
}
|
||||
}
|
||||
}
|
||||
function refreshToolbar(activeSubflow) {
|
||||
if (activeSubflow) {
|
||||
$("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0);
|
||||
$("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0);
|
||||
|
||||
$("#workspace-subflow-output .spinner-value").html(activeSubflow.out.length);
|
||||
}
|
||||
}
|
||||
|
||||
function showWorkspaceToolbar(activeSubflow) {
|
||||
var toolbar = $("#workspace-toolbar");
|
||||
toolbar.empty();
|
||||
|
||||
$('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar);
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+
|
||||
'<div style="display: inline-block;" class="button-group">'+
|
||||
'<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+
|
||||
'<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
$('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+
|
||||
'<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+
|
||||
'<div class="spinner-value">3</div>'+
|
||||
'<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+
|
||||
'</div>').appendTo(toolbar);
|
||||
|
||||
// $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
// $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar);
|
||||
$('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar);
|
||||
toolbar.i18n();
|
||||
|
||||
|
||||
$("#workspace-subflow-output-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
var result = removeSubflowOutput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
subflowOutputs: result.subflowOutputs,
|
||||
changed: wasChanged,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
$("#workspace-subflow-output-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowOutput();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-input-add").click(function(event) {
|
||||
event.preventDefault();
|
||||
addSubflowInput();
|
||||
});
|
||||
$("#workspace-subflow-input-remove").click(function(event) {
|
||||
event.preventDefault();
|
||||
var wasDirty = RED.nodes.dirty();
|
||||
var wasChanged = activeSubflow.changed;
|
||||
activeSubflow.changed = true;
|
||||
var result = removeSubflowInput();
|
||||
if (result) {
|
||||
var inst = refresh(true);
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
links:result.links,
|
||||
changed: wasChanged,
|
||||
subflowInputs: result.subflowInputs,
|
||||
dirty:wasDirty,
|
||||
subflow: {
|
||||
instances: inst.instances
|
||||
}
|
||||
});
|
||||
RED.view.select();
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#workspace-subflow-edit").click(function(event) {
|
||||
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$("#workspace-subflow-delete").click(function(event) {
|
||||
event.preventDefault();
|
||||
var removedNodes = [];
|
||||
var removedLinks = [];
|
||||
var startDirty = RED.nodes.dirty();
|
||||
|
||||
var activeSubflow = getSubflow();
|
||||
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type == "subflow:"+activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
RED.nodes.eachConfig(function(n) {
|
||||
if (n.z == activeSubflow.id) {
|
||||
removedNodes.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
var removedConfigNodes = [];
|
||||
for (var i=0;i<removedNodes.length;i++) {
|
||||
var removedEntities = RED.nodes.remove(removedNodes[i].id);
|
||||
removedLinks = removedLinks.concat(removedEntities.links);
|
||||
removedConfigNodes = removedConfigNodes.concat(removedEntities.nodes);
|
||||
}
|
||||
// TODO: this whole delete logic should be in RED.nodes.removeSubflow..
|
||||
removedNodes = removedNodes.concat(removedConfigNodes);
|
||||
|
||||
RED.nodes.removeSubflow(activeSubflow);
|
||||
|
||||
RED.history.push({
|
||||
t:'delete',
|
||||
nodes:removedNodes,
|
||||
links:removedLinks,
|
||||
subflow: {
|
||||
subflow: activeSubflow
|
||||
},
|
||||
dirty:startDirty
|
||||
});
|
||||
|
||||
RED.workspaces.remove(activeSubflow);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
refreshToolbar(activeSubflow);
|
||||
|
||||
$("#chart").css({"margin-top": "40px"});
|
||||
$("#workspace-toolbar").show();
|
||||
}
|
||||
function hideWorkspaceToolbar() {
|
||||
$("#workspace-toolbar").hide().empty();
|
||||
$("#chart").css({"margin-top": "0"});
|
||||
}
|
||||
|
||||
|
||||
function init() {
|
||||
RED.events.on("workspace:change",function(event) {
|
||||
var activeSubflow = RED.nodes.subflow(event.workspace);
|
||||
if (activeSubflow) {
|
||||
showWorkspaceToolbar(activeSubflow);
|
||||
} else {
|
||||
hideWorkspaceToolbar();
|
||||
}
|
||||
});
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (!selection.nodes) {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",true);
|
||||
} else {
|
||||
RED.menu.setDisabled("menu-item-subflow-convert",false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function createSubflow() {
|
||||
var lastIndex = 0;
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name);
|
||||
if (m) {
|
||||
lastIndex = Math.max(lastIndex,m[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var name = "Subflow "+(lastIndex+1);
|
||||
|
||||
var subflowId = RED.nodes.id();
|
||||
var subflow = {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
info:"",
|
||||
in: [],
|
||||
out: []
|
||||
};
|
||||
RED.nodes.addSubflow(subflow);
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
subflow: {
|
||||
subflow:subflow
|
||||
},
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
RED.workspaces.show(subflowId);
|
||||
RED.nodes.dirty(true);
|
||||
}
|
||||
|
||||
function convertToSubflow() {
|
||||
var selection = RED.view.selection();
|
||||
if (!selection.nodes) {
|
||||
RED.notify(RED._("subflow.errors.noNodesSelected"),"error");
|
||||
return;
|
||||
}
|
||||
var i;
|
||||
var nodes = {};
|
||||
var new_links = [];
|
||||
var removedLinks = [];
|
||||
|
||||
var candidateInputs = [];
|
||||
var candidateOutputs = [];
|
||||
var candidateInputNodes = {};
|
||||
|
||||
|
||||
var boundingBox = [selection.nodes[0].x,
|
||||
selection.nodes[0].y,
|
||||
selection.nodes[0].x,
|
||||
selection.nodes[0].y];
|
||||
|
||||
for (i=0;i<selection.nodes.length;i++) {
|
||||
var n = selection.nodes[i];
|
||||
nodes[n.id] = {n:n,outputs:{}};
|
||||
boundingBox = [
|
||||
Math.min(boundingBox[0],n.x),
|
||||
Math.min(boundingBox[1],n.y),
|
||||
Math.max(boundingBox[2],n.x),
|
||||
Math.max(boundingBox[3],n.y)
|
||||
]
|
||||
}
|
||||
|
||||
var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2];
|
||||
|
||||
RED.nodes.eachLink(function(link) {
|
||||
if (nodes[link.source.id] && nodes[link.target.id]) {
|
||||
// A link wholely within the selection
|
||||
}
|
||||
|
||||
if (nodes[link.source.id] && !nodes[link.target.id]) {
|
||||
// An outbound link from the selection
|
||||
candidateOutputs.push(link);
|
||||
removedLinks.push(link);
|
||||
}
|
||||
if (!nodes[link.source.id] && nodes[link.target.id]) {
|
||||
// An inbound link
|
||||
candidateInputs.push(link);
|
||||
candidateInputNodes[link.target.id] = link.target;
|
||||
removedLinks.push(link);
|
||||
}
|
||||
});
|
||||
|
||||
var outputs = {};
|
||||
candidateOutputs = candidateOutputs.filter(function(v) {
|
||||
if (outputs[v.source.id+":"+v.sourcePort]) {
|
||||
outputs[v.source.id+":"+v.sourcePort].targets.push(v.target);
|
||||
return false;
|
||||
}
|
||||
v.targets = [];
|
||||
v.targets.push(v.target);
|
||||
outputs[v.source.id+":"+v.sourcePort] = v;
|
||||
return true;
|
||||
});
|
||||
candidateOutputs.sort(function(a,b) { return a.source.y-b.source.y});
|
||||
|
||||
if (Object.keys(candidateInputNodes).length > 1) {
|
||||
RED.notify(RED._("subflow.errors.multipleInputsToSelection"),"error");
|
||||
return;
|
||||
}
|
||||
|
||||
var lastIndex = 0;
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name);
|
||||
if (m) {
|
||||
lastIndex = Math.max(lastIndex,m[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var name = "Subflow "+(lastIndex+1);
|
||||
|
||||
var subflowId = RED.nodes.id();
|
||||
var subflow = {
|
||||
type:"subflow",
|
||||
id:subflowId,
|
||||
name:name,
|
||||
info:"",
|
||||
in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80,
|
||||
y:candidateInputNodes[v].y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:candidateInputNodes[v].id}]
|
||||
}}),
|
||||
out: candidateOutputs.map(function(v,i) { var index = i; return {
|
||||
type:"subflow",
|
||||
direction:"in",
|
||||
x:v.source.x+(v.source.w/2)+80,
|
||||
y:v.source.y,
|
||||
z:subflowId,
|
||||
i:index,
|
||||
id:RED.nodes.id(),
|
||||
wires:[{id:v.source.id,port:v.sourcePort}]
|
||||
}})
|
||||
};
|
||||
|
||||
RED.nodes.addSubflow(subflow);
|
||||
|
||||
var subflowInstance = {
|
||||
id:RED.nodes.id(),
|
||||
type:"subflow:"+subflow.id,
|
||||
x: center[0],
|
||||
y: center[1],
|
||||
z: RED.workspaces.active(),
|
||||
inputs: subflow.in.length,
|
||||
outputs: subflow.out.length,
|
||||
h: Math.max(30/*node_height*/,(subflow.out.length||0) * 15),
|
||||
changed:true
|
||||
}
|
||||
subflowInstance._def = RED.nodes.getType(subflowInstance.type);
|
||||
RED.editor.validateNode(subflowInstance);
|
||||
RED.nodes.add(subflowInstance);
|
||||
|
||||
candidateInputs.forEach(function(l) {
|
||||
var link = {source:l.source, sourcePort:l.sourcePort, target: subflowInstance};
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
|
||||
candidateOutputs.forEach(function(output,i) {
|
||||
output.targets.forEach(function(target) {
|
||||
var link = {source:subflowInstance, sourcePort:i, target: target};
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
|
||||
subflow.in.forEach(function(input) {
|
||||
input.wires.forEach(function(wire) {
|
||||
var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) }
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
subflow.out.forEach(function(output,i) {
|
||||
output.wires.forEach(function(wire) {
|
||||
var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output }
|
||||
new_links.push(link);
|
||||
RED.nodes.addLink(link);
|
||||
});
|
||||
});
|
||||
|
||||
for (i=0;i<removedLinks.length;i++) {
|
||||
RED.nodes.removeLink(removedLinks[i]);
|
||||
}
|
||||
|
||||
for (i=0;i<selection.nodes.length;i++) {
|
||||
selection.nodes[i].z = subflow.id;
|
||||
}
|
||||
|
||||
RED.history.push({
|
||||
t:'createSubflow',
|
||||
nodes:[subflowInstance.id],
|
||||
links:new_links,
|
||||
subflow: {
|
||||
subflow: subflow
|
||||
},
|
||||
|
||||
activeWorkspace: RED.workspaces.active(),
|
||||
removedLinks: removedLinks,
|
||||
|
||||
dirty:RED.nodes.dirty()
|
||||
});
|
||||
|
||||
RED.editor.validateNode(subflow);
|
||||
RED.nodes.dirty(true);
|
||||
RED.view.redraw(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return {
|
||||
init: init,
|
||||
createSubflow: createSubflow,
|
||||
convertToSubflow: convertToSubflow,
|
||||
refresh: refresh,
|
||||
removeInput: removeSubflowInput,
|
||||
removeOutput: removeSubflowOutput
|
||||
}
|
||||
})();
|
293
editor/js/ui/tab-config.js
Normal file
@ -0,0 +1,293 @@
|
||||
/**
|
||||
* 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.sidebar.config = (function() {
|
||||
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.className = "sidebar-node-config"
|
||||
|
||||
$('<div class="button-group sidebar-header">'+
|
||||
'<a class="sidebar-header-button-toggle selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+
|
||||
'<a class="sidebar-header-button-toggle" id="workspace-config-node-filter-unused" href="#"><span data-i18n="sidebar.config.filterUnused"></span></a> '+
|
||||
'</div>'
|
||||
).appendTo(content);
|
||||
|
||||
|
||||
var toolbar = $('<div>'+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-collapse-all" href="#"><i class="fa fa-angle-double-up"></i></a> '+
|
||||
'<a class="sidebar-footer-button" id="workspace-config-node-expand-all" href="#"><i class="fa fa-angle-double-down"></i></a>'+
|
||||
'</div>');
|
||||
|
||||
var globalCategories = $("<div>").appendTo(content);
|
||||
var flowCategories = $("<div>").appendTo(content);
|
||||
var subflowCategories = $("<div>").appendTo(content);
|
||||
|
||||
var showUnusedOnly = false;
|
||||
|
||||
var categories = {};
|
||||
|
||||
function getOrCreateCategory(name,parent,label) {
|
||||
name = name.replace(/\./i,"-");
|
||||
if (!categories[name]) {
|
||||
var container = $('<div class="palette-category workspace-config-node-category" id="workspace-config-node-category-'+name+'"></div>').appendTo(parent);
|
||||
var header = $('<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
|
||||
if (label) {
|
||||
$('<span class="config-node-label"/>').text(label).appendTo(header);
|
||||
} else {
|
||||
$('<span class="config-node-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
|
||||
}
|
||||
$('<span class="config-node-filter-info"></span>').appendTo(header);
|
||||
category = $('<ul class="palette-content config-node-list"></ul>').appendTo(container);
|
||||
container.i18n();
|
||||
var icon = header.find("i");
|
||||
var result = {
|
||||
label: label,
|
||||
list: category,
|
||||
size: function() {
|
||||
return result.list.find("li:not(.config_node_none)").length
|
||||
},
|
||||
open: function(snap) {
|
||||
if (!icon.hasClass("expanded")) {
|
||||
icon.addClass("expanded");
|
||||
if (snap) {
|
||||
result.list.show();
|
||||
} else {
|
||||
result.list.slideDown();
|
||||
}
|
||||
}
|
||||
},
|
||||
close: function(snap) {
|
||||
if (icon.hasClass("expanded")) {
|
||||
icon.removeClass("expanded");
|
||||
if (snap) {
|
||||
result.list.hide();
|
||||
} else {
|
||||
result.list.slideUp();
|
||||
}
|
||||
}
|
||||
},
|
||||
isOpen: function() {
|
||||
return icon.hasClass("expanded");
|
||||
}
|
||||
};
|
||||
|
||||
header.on('click', function(e) {
|
||||
if (result.isOpen()) {
|
||||
result.close();
|
||||
} else {
|
||||
result.open();
|
||||
}
|
||||
});
|
||||
categories[name] = result;
|
||||
} else {
|
||||
if (categories[name].label !== label) {
|
||||
categories[name].list.parent().find('.config-node-label').text(label);
|
||||
categories[name].label = label;
|
||||
}
|
||||
}
|
||||
return categories[name];
|
||||
}
|
||||
|
||||
function createConfigNodeList(id,nodes) {
|
||||
var category = getOrCreateCategory(id.replace(/\./i,"-"))
|
||||
var list = category.list;
|
||||
|
||||
nodes.sort(function(A,B) {
|
||||
if (A.type < B.type) { return -1;}
|
||||
if (A.type > B.type) { return 1;}
|
||||
return 0;
|
||||
});
|
||||
if (showUnusedOnly) {
|
||||
var hiddenCount = nodes.length;
|
||||
nodes = nodes.filter(function(n) {
|
||||
return n.users.length === 0;
|
||||
})
|
||||
hiddenCount = hiddenCount - nodes.length;
|
||||
if (hiddenCount > 0) {
|
||||
list.parent().find('.config-node-filter-info').text(RED._('sidebar.config.filtered',{count:hiddenCount})).show();
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
} else {
|
||||
list.parent().find('.config-node-filter-info').hide();
|
||||
}
|
||||
list.empty();
|
||||
if (nodes.length === 0) {
|
||||
$('<li class="config_node_none" data-i18n="sidebar.config.none">NONE</li>').i18n().appendTo(list);
|
||||
category.close(true);
|
||||
} else {
|
||||
var currentType = "";
|
||||
nodes.forEach(function(node) {
|
||||
var label = "";
|
||||
if (typeof node._def.label == "function") {
|
||||
label = node._def.label.call(node);
|
||||
} else {
|
||||
label = node._def.label;
|
||||
}
|
||||
label = label || node.id;
|
||||
if (node.type != currentType) {
|
||||
$('<li class="config_node_type">'+node.type+'</li>').appendTo(list);
|
||||
currentType = node.type;
|
||||
}
|
||||
|
||||
var entry = $('<li class="palette_node config_node"></li>').appendTo(list);
|
||||
$('<div class="palette_label"></div>').text(label).appendTo(entry);
|
||||
|
||||
var iconContainer = $('<div/>',{class:"palette_icon_container palette_icon_container_right"}).text(node.users.length).appendTo(entry);
|
||||
if (node.users.length === 0) {
|
||||
entry.addClass("config_node_unused");
|
||||
}
|
||||
entry.on('click',function(e) {
|
||||
RED.sidebar.info.refresh(node);
|
||||
});
|
||||
entry.on('dblclick',function(e) {
|
||||
RED.editor.editConfig("", node.type, node.id);
|
||||
});
|
||||
var userArray = node.users.map(function(n) { return n.id });
|
||||
entry.on('mouseover',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if( userArray.indexOf(node.id) != -1) {
|
||||
node.highlighted = true;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
|
||||
entry.on('mouseout',function(e) {
|
||||
RED.nodes.eachNode(function(node) {
|
||||
if(node.highlighted) {
|
||||
node.highlighted = false;
|
||||
node.dirty = true;
|
||||
}
|
||||
});
|
||||
RED.view.redraw();
|
||||
});
|
||||
});
|
||||
category.open(true);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshConfigNodeList() {
|
||||
var validList = {"global":true};
|
||||
|
||||
getOrCreateCategory("global",globalCategories);
|
||||
|
||||
RED.nodes.eachWorkspace(function(ws) {
|
||||
validList[ws.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(ws.id,flowCategories,ws.label);
|
||||
})
|
||||
RED.nodes.eachSubflow(function(sf) {
|
||||
validList[sf.id.replace(/\./g,"-")] = true;
|
||||
getOrCreateCategory(sf.id,subflowCategories,sf.name);
|
||||
})
|
||||
$(".workspace-config-node-category").each(function() {
|
||||
var id = $(this).attr('id').substring("workspace-config-node-category-".length);
|
||||
if (!validList[id]) {
|
||||
$(this).remove();
|
||||
delete categories[id];
|
||||
}
|
||||
})
|
||||
var globalConfigNodes = [];
|
||||
var configList = {};
|
||||
RED.nodes.eachConfig(function(cn) {
|
||||
if (cn.z) {//} == RED.workspaces.active()) {
|
||||
configList[cn.z.replace(/\./g,"-")] = configList[cn.z.replace(/\./g,"-")]||[];
|
||||
configList[cn.z.replace(/\./g,"-")].push(cn);
|
||||
} else if (!cn.z) {
|
||||
globalConfigNodes.push(cn);
|
||||
}
|
||||
});
|
||||
for (var id in validList) {
|
||||
if (validList.hasOwnProperty(id)) {
|
||||
createConfigNodeList(id,configList[id]||[]);
|
||||
}
|
||||
}
|
||||
createConfigNodeList('global',globalConfigNodes);
|
||||
}
|
||||
|
||||
function init() {
|
||||
RED.sidebar.addTab({
|
||||
id: "config",
|
||||
label: RED._("sidebar.config.label"),
|
||||
name: RED._("sidebar.config.name"),
|
||||
content: content,
|
||||
toolbar: toolbar,
|
||||
closeable: true,
|
||||
visible: false,
|
||||
onchange: function() { refreshConfigNodeList(); }
|
||||
});
|
||||
|
||||
RED.menu.setAction('menu-item-config-nodes',function() {
|
||||
RED.sidebar.show('config');
|
||||
})
|
||||
|
||||
$("#workspace-config-node-collapse-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
categories[cat].close();
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#workspace-config-node-expand-all").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
for (var cat in categories) {
|
||||
if (categories.hasOwnProperty(cat)) {
|
||||
if (categories[cat].size() > 0) {
|
||||
categories[cat].open();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-all').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-unused').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
$('#workspace-config-node-filter-unused').on("click",function(e) {
|
||||
e.preventDefault();
|
||||
if (!showUnusedOnly) {
|
||||
$(this).addClass('selected');
|
||||
$('#workspace-config-node-filter-all').removeClass('selected');
|
||||
showUnusedOnly = !showUnusedOnly;
|
||||
refreshConfigNodeList();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
function show(unused) {
|
||||
if (unused !== undefined) {
|
||||
if (unused) {
|
||||
$('#workspace-config-node-filter-unused').click();
|
||||
} else {
|
||||
$('#workspace-config-node-filter-all').click();
|
||||
}
|
||||
}
|
||||
refreshConfigNodeList();
|
||||
RED.sidebar.show("config");
|
||||
}
|
||||
return {
|
||||
init:init,
|
||||
show:show,
|
||||
refresh:refreshConfigNodeList
|
||||
}
|
||||
})();
|
205
editor/js/ui/tab-info.js
Normal file
@ -0,0 +1,205 @@
|
||||
/**
|
||||
* 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.sidebar.info = (function() {
|
||||
|
||||
marked.setOptions({
|
||||
renderer: new marked.Renderer(),
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
|
||||
var content = document.createElement("div");
|
||||
content.style.paddingTop = "4px";
|
||||
content.style.paddingLeft = "4px";
|
||||
content.style.paddingRight = "4px";
|
||||
content.className = "sidebar-node-info"
|
||||
|
||||
var propertiesExpanded = false;
|
||||
|
||||
function init() {
|
||||
RED.sidebar.addTab({
|
||||
id: "info",
|
||||
label: RED._("sidebar.info.label"),
|
||||
name: RED._("sidebar.info.name"),
|
||||
content: content
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function show() {
|
||||
RED.sidebar.show("info");
|
||||
}
|
||||
|
||||
function jsonFilter(key,value) {
|
||||
if (key === "") {
|
||||
return value;
|
||||
}
|
||||
var t = typeof value;
|
||||
if ($.isArray(value)) {
|
||||
return "[array:"+value.length+"]";
|
||||
} else if (t === "object") {
|
||||
return "[object]"
|
||||
} else if (t === "string") {
|
||||
if (value.length > 30) {
|
||||
return value.substring(0,30)+" ...";
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function refresh(node) {
|
||||
var table = '<table class="node-info"><tbody>';
|
||||
table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>';
|
||||
if (node.type != "subflow" && node.name) {
|
||||
table += "<tr><td>"+RED._("common.label.name")+"</td><td> "+node.name+"</td></tr>";
|
||||
}
|
||||
table += "<tr><td>"+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>";
|
||||
table += "<tr><td>"+RED._("sidebar.info.id")+"</td><td> "+node.id+"</td></tr>";
|
||||
|
||||
var m = /^subflow(:(.+))?$/.exec(node.type);
|
||||
var subflowNode;
|
||||
if (m) {
|
||||
if (m[2]) {
|
||||
subflowNode = RED.nodes.subflow(m[2]);
|
||||
} else {
|
||||
subflowNode = node;
|
||||
}
|
||||
|
||||
table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.subflow")+'</td></tr>';
|
||||
|
||||
var userCount = 0;
|
||||
var subflowType = "subflow:"+subflowNode.id;
|
||||
RED.nodes.eachNode(function(n) {
|
||||
if (n.type === subflowType) {
|
||||
userCount++;
|
||||
}
|
||||
});
|
||||
table += "<tr><td>"+RED._("common.label.name")+"</td><td>"+subflowNode.name+"</td></tr>";
|
||||
table += "<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>";
|
||||
}
|
||||
|
||||
if (!m && node.type != "subflow" && node.type != "comment") {
|
||||
table += '<tr class="blank"><td colspan="2"><a href="#" class="node-info-property-header"><i style="width: 10px; text-align: center;" class="fa fa-caret-'+(propertiesExpanded?"down":"right")+'"></i> '+RED._("sidebar.info.properties")+'</a></td></tr>';
|
||||
if (node._def) {
|
||||
for (var n in node._def.defaults) {
|
||||
if (n != "name" && node._def.defaults.hasOwnProperty(n)) {
|
||||
var val = node[n]||"";
|
||||
var type = typeof val;
|
||||
if (type === "string") {
|
||||
if (val.length === 0) {
|
||||
val += '<span style="font-style: italic; color: #ccc;">'+RED._("sidebar.info.blank")+'</span>';
|
||||
} else {
|
||||
if (val.length > 30) {
|
||||
val = val.substring(0,30)+" ...";
|
||||
}
|
||||
val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
} else if (type === "number") {
|
||||
val = val.toString();
|
||||
} else if ($.isArray(val)) {
|
||||
val = "[<br/>";
|
||||
for (var i=0;i<Math.min(node[n].length,10);i++) {
|
||||
var vv = JSON.stringify(node[n][i],jsonFilter," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
val += " "+i+": "+vv+"<br/>";
|
||||
}
|
||||
if (node[n].length > 10) {
|
||||
val += " ... "+RED._("sidebar.info.arrayItems",{count:node[n].length})+"<br/>";
|
||||
}
|
||||
val += "]";
|
||||
} else {
|
||||
val = JSON.stringify(val,jsonFilter," ");
|
||||
val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
|
||||
table += '<tr class="node-info-property-row'+(propertiesExpanded?"":" hide")+'"><td>'+n+"</td><td>"+val+"</td></tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table += "</tbody></table><hr/>";
|
||||
if (!subflowNode && node.type != "comment") {
|
||||
var helpText = $("script[data-help-name|='"+node.type+"']").html()||"";
|
||||
table += '<div class="node-help">'+helpText+"</div>";
|
||||
}
|
||||
if (subflowNode) {
|
||||
table += '<div class="node-help">'+marked(subflowNode.info||"")+'</div>';
|
||||
} else if (node._def && node._def.info) {
|
||||
var info = node._def.info;
|
||||
table += '<div class="node-help">'+marked(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
//table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>';
|
||||
}
|
||||
|
||||
$(content).html(table);
|
||||
|
||||
$(".node-info-property-header").click(function(e) {
|
||||
var icon = $(this).find("i");
|
||||
if (icon.hasClass("fa-caret-right")) {
|
||||
icon.removeClass("fa-caret-right");
|
||||
icon.addClass("fa-caret-down");
|
||||
$(".node-info-property-row").show();
|
||||
propertiesExpanded = true;
|
||||
} else {
|
||||
icon.addClass("fa-caret-right");
|
||||
icon.removeClass("fa-caret-down");
|
||||
$(".node-info-property-row").hide();
|
||||
propertiesExpanded = false;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
function clear() {
|
||||
$(content).html("");
|
||||
}
|
||||
|
||||
function set(html) {
|
||||
$(content).html(html);
|
||||
}
|
||||
|
||||
RED.events.on("view:selection-changed",function(selection) {
|
||||
if (selection.nodes) {
|
||||
if (selection.nodes.length == 1) {
|
||||
var node = selection.nodes[0];
|
||||
if (node.type === "subflow" && node.direction) {
|
||||
refresh(RED.nodes.subflow(node.z));
|
||||
} else {
|
||||
refresh(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var subflow = RED.nodes.subflow(RED.workspaces.active());
|
||||
if (subflow) {
|
||||
refresh(subflow);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
init: init,
|
||||
show: show,
|
||||
refresh:refresh,
|
||||
clear: clear,
|
||||
set: set
|
||||
}
|
||||
})();
|
169
editor/js/ui/tabs.js
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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.tabs = (function() {
|
||||
|
||||
|
||||
function createTabs(options) {
|
||||
var tabs = {};
|
||||
var currentTabWidth;
|
||||
var currentActiveTabWidth = 0;
|
||||
|
||||
var ul = $("#"+options.id)
|
||||
ul.addClass("red-ui-tabs");
|
||||
ul.children().first().addClass("active");
|
||||
ul.children().addClass("red-ui-tab");
|
||||
|
||||
function onTabClick() {
|
||||
activateTab($(this));
|
||||
return false;
|
||||
}
|
||||
|
||||
function onTabDblClick() {
|
||||
if (options.ondblclick) {
|
||||
options.ondblclick(tabs[$(this).attr('href').slice(1)]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function activateTab(link) {
|
||||
if (typeof link === "string") {
|
||||
link = ul.find("a[href='#"+link+"']");
|
||||
}
|
||||
if (!link.parent().hasClass("active")) {
|
||||
ul.children().removeClass("active");
|
||||
ul.children().css({"transition": "width 100ms"});
|
||||
link.parent().addClass("active");
|
||||
if (options.onchange) {
|
||||
options.onchange(tabs[link.attr('href').slice(1)]);
|
||||
}
|
||||
updateTabWidths();
|
||||
setTimeout(function() {
|
||||
ul.children().css({"transition": ""});
|
||||
},100);
|
||||
}
|
||||
}
|
||||
|
||||
function updateTabWidths() {
|
||||
var tabs = ul.find("li.red-ui-tab");
|
||||
var width = ul.width();
|
||||
var tabCount = tabs.size();
|
||||
var tabWidth = (width-12-(tabCount*6))/tabCount;
|
||||
currentTabWidth = 100*tabWidth/width;
|
||||
currentActiveTabWidth = currentTabWidth+"%";
|
||||
|
||||
if (options.hasOwnProperty("minimumActiveTabWidth")) {
|
||||
if (tabWidth < options.minimumActiveTabWidth) {
|
||||
tabCount -= 1;
|
||||
tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount;
|
||||
currentTabWidth = 100*tabWidth/width;
|
||||
currentActiveTabWidth = options.minimumActiveTabWidth+"px";
|
||||
} else {
|
||||
currentActiveTabWidth = 0;
|
||||
}
|
||||
}
|
||||
tabs.css({width:currentTabWidth+"%"});
|
||||
if (tabWidth < 50) {
|
||||
ul.find(".red-ui-tab-close").hide();
|
||||
ul.find(".red-ui-tab-icon").hide();
|
||||
} else {
|
||||
ul.find(".red-ui-tab-close").show();
|
||||
ul.find(".red-ui-tab-icon").show();
|
||||
}
|
||||
if (currentActiveTabWidth !== 0) {
|
||||
ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth});
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-close").show();
|
||||
ul.find("li.red-ui-tab.active .red-ui-tab-icon").show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick);
|
||||
updateTabWidths();
|
||||
|
||||
|
||||
function removeTab(id) {
|
||||
var li = ul.find("a[href='#"+id+"']").parent();
|
||||
if (li.hasClass("active")) {
|
||||
var tab = li.prev();
|
||||
if (tab.size() === 0) {
|
||||
tab = li.next();
|
||||
}
|
||||
activateTab(tab.find("a"));
|
||||
}
|
||||
li.remove();
|
||||
if (options.onremove) {
|
||||
options.onremove(tabs[id]);
|
||||
}
|
||||
delete tabs[id];
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
return {
|
||||
addTab: function(tab) {
|
||||
tabs[tab.id] = tab;
|
||||
var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
|
||||
var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li);
|
||||
if (tab.icon) {
|
||||
$('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link);
|
||||
}
|
||||
$('<span/>').text(tab.label).appendTo(link);
|
||||
|
||||
link.on("click",onTabClick);
|
||||
link.on("dblclick",onTabDblClick);
|
||||
if (tab.closeable) {
|
||||
var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li);
|
||||
closeLink.append('<i class="fa fa-times" />');
|
||||
|
||||
closeLink.on("click",function(event) {
|
||||
removeTab(tab.id);
|
||||
});
|
||||
}
|
||||
updateTabWidths();
|
||||
if (options.onadd) {
|
||||
options.onadd(tab);
|
||||
}
|
||||
link.attr("title",tab.label);
|
||||
if (ul.find("li.red-ui-tab").size() == 1) {
|
||||
activateTab(link);
|
||||
}
|
||||
},
|
||||
removeTab: removeTab,
|
||||
activateTab: activateTab,
|
||||
resize: updateTabWidths,
|
||||
count: function() {
|
||||
return ul.find("li.red-ui-tab").size();
|
||||
},
|
||||
contains: function(id) {
|
||||
return ul.find("a[href='#"+id+"']").length > 0;
|
||||
},
|
||||
renameTab: function(id,label) {
|
||||
tabs[id].label = label;
|
||||
var tab = ul.find("a[href='#"+id+"']");
|
||||
tab.attr("title",label);
|
||||
tab.find("span").text(label);
|
||||
updateTabWidths();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
create: createTabs
|
||||
}
|
||||
})();
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
* Copyright 2014 IBM Corp.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -27,35 +27,65 @@ RED.touch.radialMenu = (function() {
|
||||
function createRadial(obj,pos,options) {
|
||||
isActive = true;
|
||||
try {
|
||||
touchMenu = d3.select("body").append("div").classed("red-ui-editor-radial-menu",true)
|
||||
var w = $("body").width();
|
||||
var h = $("body").height();
|
||||
|
||||
touchMenu = d3.select("body").append("div")
|
||||
.style({
|
||||
position:"absolute",
|
||||
top: 0,
|
||||
left:0,
|
||||
bottom:0,
|
||||
right:0,
|
||||
"z-index": 1000
|
||||
})
|
||||
.on('touchstart',function() {
|
||||
hide();
|
||||
d3.event.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
var menu = touchMenu.append("div")
|
||||
.style({
|
||||
top: (pos[1]-80)+"px",
|
||||
left:(pos[0]-80)+"px",
|
||||
position: "absolute",
|
||||
top: (pos[1]-80)+"px",
|
||||
left:(pos[0]-80)+"px",
|
||||
"border-radius": "80px",
|
||||
width: "160px",
|
||||
height: "160px",
|
||||
background: "rgba(255,255,255,0.6)",
|
||||
border: "1px solid #666"
|
||||
});
|
||||
|
||||
var menuOpts = [];
|
||||
var createMenuOpt = function(x,y,opt) {
|
||||
opt.el = menu.append("div").classed("red-ui-editor-radial-menu-opt",true)
|
||||
opt.el = menu.append("div")
|
||||
.style({
|
||||
position: "absolute",
|
||||
top: (y+80-25)+"px",
|
||||
left:(x+80-25)+"px"
|
||||
})
|
||||
.classed("red-ui-editor-radial-menu-opt-disabled",!!opt.disabled)
|
||||
left:(x+80-25)+"px",
|
||||
"border-radius": "20px",
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
background: "#fff",
|
||||
border: "2px solid #666",
|
||||
"text-align": "center",
|
||||
"line-height":"50px"
|
||||
});
|
||||
|
||||
opt.el.html(opt.name);
|
||||
|
||||
if (opt.disabled) {
|
||||
opt.el.style({"border-color":"#ccc",color:"#ccc"});
|
||||
}
|
||||
opt.x = x;
|
||||
opt.y = y;
|
||||
menuOpts.push(opt);
|
||||
|
||||
opt.el.on('touchstart',function() {
|
||||
opt.el.classed("red-ui-editor-radial-menu-opt-active",true)
|
||||
opt.el.style("background","#999");
|
||||
d3.event.preventDefault();
|
||||
d3.event.stopPropagation();
|
||||
});
|
||||
@ -103,6 +133,8 @@ RED.touch.radialMenu = (function() {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
obj.on('touchmove.radial',function() {
|
||||
try {
|
||||
var touch0 = d3.event.touches.item(0);
|
||||
@ -112,14 +144,14 @@ RED.touch.radialMenu = (function() {
|
||||
if (!opt.disabled) {
|
||||
if (p[0]>opt.x-30 && p[0]<opt.x+30 && p[1]>opt.y-30 && p[1]<opt.y+30) {
|
||||
if (opt !== activeOption) {
|
||||
opt.el.classed("selected",true);
|
||||
opt.el.style("background","#999");
|
||||
activeOption = opt;
|
||||
}
|
||||
} else if (opt === activeOption) {
|
||||
opt.el.style("background","#fff");
|
||||
activeOption = null;
|
||||
} else {
|
||||
if (opt === activeOption) {
|
||||
activeOption = null;
|
||||
}
|
||||
opt.el.classed("selected",false);
|
||||
opt.el.style("background","#fff");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,3 +181,4 @@ RED.touch.radialMenu = (function() {
|
||||
}
|
||||
|
||||
})();
|
||||
|