Compare commits

..

5 Commits

Author SHA1 Message Date
Nick O'Leary
3ca64057c3 Add breakpoint editing to debugger 2016-11-22 12:57:30 +00:00
Nick O'Leary
127b4f0226 Add RED.utils.getNodeLabel utility function 2016-11-22 12:57:08 +00:00
Nick O'Leary
629c63e0c9 Add initial debugger panel to debug tab 2016-11-18 12:53:05 +00:00
Nick O'Leary
416d5190bc Flow debugger initial pass 2016-11-16 15:12:30 +00:00
Nick O'Leary
cebddc0237 Add message router component 2016-11-16 15:12:30 +00:00
933 changed files with 26089 additions and 108306 deletions

1
.gitattributes vendored
View File

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

View File

@@ -1,34 +0,0 @@
<!--
## Before you hit that Submit button....
This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes.
If your issue is:
- a general 'how-to' type question,
- a feature request or suggestion for a change,
- or problems with 3rd party (`node-red-contrib-`) nodes
please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack).
You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`.
That way the whole Node-RED user community can help, rather than rely on the core development team.
## So you have a real issue to raise...
To help us understand the issue, please fill-in as much of the following information as you can:
-->
### What are the steps to reproduce?
### What happens?
### What do you expect to happen?
### Please tell us about your environment:
- [ ] Node-RED version:
- [ ] node.js version:
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:

View File

@@ -1,39 +0,0 @@
---
name: Bug report
about: Reproducable software issues in the core of Node-RED
title: ''
labels: ''
assignees: ''
---
<!--
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:
-->
### What are the steps to reproduce?
### What happens?
### What do you expect to happen?
### Please tell us about your environment:
- [ ] Node-RED version:
- [ ] node.js version:
- [ ] npm version:
- [ ] Platform/OS:
- [ ] Browser:

View File

@@ -1,14 +0,0 @@
---
name: Anything Else
about: Something that is not a bug report
title: ''
labels: ''
assignees: ''
---
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.

View File

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

5
.gitignore vendored
View File

@@ -17,8 +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

View File

@@ -2,9 +2,6 @@
"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
@@ -12,8 +9,6 @@
//"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": 6 // allow es6
"proto": true // allow setting of __proto__ in node < v0.12
}

View File

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

7
.npmignore Normal file
View File

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

View File

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

15
API.md
View File

@@ -1,15 +0,0 @@
Node-RED Modules
---
Node-RED provides a set of node modules that implement different parts of the
application.
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 | the internal node registry
@node-red/nodes | the default set of core nodes
@node-red/editor-client | the client-side resources of the Node-RED editor application

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ We welcome contributions, but request you follow these guidelines.
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
By participating, you are expected to uphold this code. Please report unacceptable
behavior to the project's core team at team@nodered.org.
behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
## Raising issues
@@ -30,21 +30,29 @@ At a minimum, please include:
## 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, it may well get rejected if you haven't discussed it on
the [forum](https://discourse.nodered.org) first.
the [mailing list](https://groups.google.com/forum/#!forum/node-red) first.
All contributors need to sign the JS Foundation's Contributor License Agreement.
It is an online process and quick to do. You can read the details of the agreement
here: https://cla.js.foundation/node-red/node-red.
### Contributor License Agreement
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.
You can download the CLAs here:
- [individual](http://nodered.org/cla/node-red-cla-individual.pdf)
- [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf)
If you are an IBMer, please contact us directly as the contribution process is
slightly different.
### Coding standards

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,6 @@
**/
var path = require("path");
var fs = require("fs-extra");
module.exports = function(grunt) {
@@ -25,10 +24,6 @@ module.exports = function(grunt) {
nodemonArgs.push(flowFile);
}
var nonHeadless = grunt.option('non-headless');
if (nonHeadless) {
process.env.NODE_RED_NON_HEADLESS = 'true';
}
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
paths: {
@@ -43,26 +38,7 @@ module.exports = function(grunt) {
reporter: 'spec'
},
all: { src: ['test/**/*_spec.js'] },
core: { src: ["test/_spec.js","test/unit/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
},
webdriver: {
all: {
configFile: 'test/editor/wdio.conf.js'
}
},
mocha_istanbul: {
options: {
globals: ['expect'],
timeout: 3000,
ignoreLeaks: false,
ui: 'bdd',
reportFormats: ['lcov','html'],
print: 'both',
istanbulOptions: ['--no-default-excludes', '-i','**/packages/node_modules/**']
},
all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] },
core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]},
core: { src: ["test/_spec.js","test/red/**/*_spec.js"]},
nodes: { src: ["test/nodes/**/*_spec.js"]}
},
jshint: {
@@ -82,14 +58,16 @@ module.exports = function(grunt) {
all: [
'Gruntfile.js',
'red.js',
'packages/**/*.js'
'red/**/*.js',
'nodes/core/*/*.js',
'editor/js/**/*.js'
],
core: {
files: {
src: [
'Gruntfile.js',
'red.js',
'packages/**/*.js',
'red/**/*.js'
]
}
},
@@ -120,86 +98,61 @@ module.exports = function(grunt) {
src: [
// Ensure editor source files are concatenated in
// the right order
"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/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/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/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/actions.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/deploy.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/workspaces.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/view.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-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/*.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/typeSearch.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/subflow.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"
"editor/js/red.js",
"editor/js/events.js",
"editor/js/i18n.js",
"editor/js/settings.js",
"editor/js/user.js",
"editor/js/comms.js",
"editor/js/text/bidi.js",
"editor/js/text/format.js",
"editor/js/ui/state.js",
"editor/js/nodes.js",
"editor/js/history.js",
"editor/js/validators.js",
"editor/js/debugger.js",
"editor/js/ui/common/editableList.js",
"editor/js/ui/common/menu.js",
"editor/js/ui/common/popover.js",
"editor/js/ui/common/searchBox.js",
"editor/js/ui/common/tabs.js",
"editor/js/ui/common/typedInput.js",
"editor/js/ui/utils.js",
"editor/js/ui/deploy.js",
"editor/js/ui/keyboard.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/palette-editor.js",
"editor/js/ui/editor.js",
"editor/js/ui/tray.js",
"editor/js/ui/clipboard.js",
"editor/js/ui/library.js",
"editor/js/ui/notifications.js",
"editor/js/ui/search.js",
"editor/js/ui/typeSearch.js",
"editor/js/ui/subflow.js",
"editor/js/ui/touch/radialMenu.js"
],
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
dest: "public/red/red.js"
},
vendor: {
files: {
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-1.11.3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/bootstrap/js/bootstrap.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/marked/marked.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/i18next/i18next.min.js"
"public/vendor/vendor.js": [
"editor/vendor/jquery/js/jquery-1.11.3.min.js",
"editor/vendor/bootstrap/js/bootstrap.min.js",
"editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js",
"editor/vendor/jquery/js/jquery.ui.touch-punch.min.js",
"editor/vendor/marked/marked.min.js",
"editor/vendor/d3/d3.v3.min.js",
"editor/vendor/i18next/i18next.min.js"
],
"packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [
"public/vendor/vendor.css": [
// TODO: resolve relative resource paths in
// bootstrap/FA/jquery
],
"packages/node_modules/@node-red/editor-client/public/vendor/jsonata/jsonata.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/public/vendor/ace/worker-jsonata.js": [
"node_modules/jsonata/jsonata-es5.min.js",
"packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js"
]
}
}
@@ -207,10 +160,8 @@ module.exports = function(grunt) {
uglify: {
build: {
files: {
'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js',
'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js',
'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js'
'public/red/red.min.js': 'public/red/red.js',
'public/red/main.min.js': 'public/red/main.js'
}
}
},
@@ -220,50 +171,45 @@ module.exports = function(grunt) {
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: 'packages/node_modules/@node-red/editor-client/public/vendor/bootstrap/css/bootstrap.min.css',
src: 'packages/node_modules/@node-red/editor-client/src/vendor/bootstrap/css/bootstrap.css'
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',
'public/red/main.min.js'
]
},
css: {
src: [
'packages/node_modules/@node-red/editor-client/public/red/style.min.css'
'public/red/style.min.css'
]
}
},
clean: {
build: {
src: [
"packages/node_modules/@node-red/editor-client/public/red",
"packages/node_modules/@node-red/editor-client/public/index.html",
"packages/node_modules/@node-red/editor-client/public/favicon.ico",
"packages/node_modules/@node-red/editor-client/public/icons",
"packages/node_modules/@node-red/editor-client/public/vendor"
"public/red",
"public/index.html",
"public/favicon.ico",
"public/icons",
"public/vendor"
]
},
release: {
@@ -275,30 +221,24 @@ 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']
},
misc: {
files: [
'CHANGELOG.md'
@@ -310,13 +250,12 @@ module.exports = function(grunt) {
nodemon: {
/* uses .nodemonignore */
dev: {
script: 'packages/node_modules/node-red/red.js',
script: 'red.js',
options: {
args: nodemonArgs,
ext: 'js,html,json',
watch: [
'packages/node_modules',
'!packages/node_modules/@node-red/editor-client'
'red','nodes'
]
}
}
@@ -335,21 +274,17 @@ module.exports = function(grunt) {
build: {
files:[
{
src: 'packages/node_modules/@node-red/editor-client/src/js/main.js',
dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js'
src: 'editor/js/main.js',
dest: 'public/red/main.js'
},
{
src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json',
dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/images',
cwd: 'editor/images',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/red/images/'
dest: 'public/red/images/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/vendor',
cwd: 'editor/vendor',
src: [
'ace/**',
//'bootstrap/css/**',
@@ -358,35 +293,46 @@ module.exports = function(grunt) {
'font-awesome/**'
],
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/'
dest: 'public/vendor/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/icons',
cwd: 'editor/icons',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/icons/'
dest: 'public/icons/'
},
{
expand: true,
src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'],
dest: 'packages/node_modules/@node-red/editor-client/public/',
src: ['editor/index.html','editor/favicon.ico'],
dest: 'public/',
flatten: true
},
{
src: 'CHANGELOG.md',
dest: 'packages/node_modules/@node-red/editor-client/public/red/about'
},
{
src: 'CHANGELOG.md',
dest: 'packages/node_modules/node-red/'
},
{
cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/',
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/'
dest: 'public/red/about'
}
]
},
release: {
files: [{
mode: true,
expand: true,
src: [
'*.md',
'LICENSE',
'package.json',
'settings.js',
'red.js',
'lib/.gitignore',
'nodes/*.demo',
'nodes/core/**',
'red/**',
'public/**',
'editor/templates/**',
'bin/**'
],
dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>')
}]
}
},
chmod: {
@@ -394,93 +340,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/util/**/*.js',
'packages/node_modules/@node-red/editor-api/lib/index.js',
'packages/node_modules/@node-red/editor-api/lib/auth/index.js'
],
options: {
destination: 'docs',
configure: './jsdoc.json'
}
},
_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 %>/**']
}
}
});
@@ -498,17 +371,11 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-chmod');
grunt.loadNpmTasks('grunt-jsonlint');
grunt.loadNpmTasks('grunt-mocha-istanbul');
grunt.loadNpmTasks('grunt-webdriver');
grunt.loadNpmTasks('grunt-jsdoc');
grunt.loadNpmTasks('grunt-jsdoc-to-markdown');
grunt.loadNpmTasks('grunt-npm-command');
grunt.loadNpmTasks('grunt-mkdir');
grunt.registerMultiTask('attachCopyright', function() {
var files = this.data.src;
var copyright = "/**\n"+
" * Copyright JS Foundation and other contributors, http://js.foundation\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"+
@@ -524,7 +391,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');
@@ -545,25 +412,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", "chromedriver"))) {
grunt.fail.fatal('You need to run "npm install chromedriver@2" before running UI test.');
return false;
}
});
grunt.registerTask('setDevEnv',
'Sets NODE_ENV=development so non-minified assets are used',
function () {
@@ -572,27 +420,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','mocha_istanbul:all']);
['build','test-core','test-editor','test-nodes']);
grunt.registerTask('test-core',
'Runs code style check and unit tests on core runtime code',
['build','mocha_istanbul:core']);
['jshint:core','simplemocha:core']);
grunt.registerTask('test-editor',
'Runs code style check on editor code',
['jshint:editor']);
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','mocha_istanbul:nodes']);
['simplemocha:nodes']);
grunt.registerTask('build',
'Builds editor content',
['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']);
['clean:build','concat:build','concat:vendor','copy:build','uglify:build','sass:build','jsonlint:messages','attachCopyright']);
grunt.registerTask('dev',
'Developer mode: run node-red, watch for source changes and build/restart',
@@ -600,18 +444,6 @@ module.exports = function(grunt) {
grunt.registerTask('release',
'Create distribution zip file',
['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules']);
['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','mocha_istanbul:all']);
grunt.registerTask('docs',
'Generates API documentation',
['jsdoc']);
};

View File

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

View File

@@ -2,7 +2,7 @@
http://nodered.org
[![Build Status](https://travis-ci.org/node-red/node-red.svg?branch=master)](https://travis-ci.org/node-red/node-red)
[![Build Status](https://travis-ci.org/node-red/node-red.svg)](https://travis-ci.org/node-red/node-red)
[![Coverage Status](https://coveralls.io/repos/node-red/node-red/badge.svg?branch=master)](https://coveralls.io/r/node-red/node-red?branch=master)
A visual tool for wiring the Internet of Things.
@@ -14,7 +14,7 @@ A visual tool for wiring the Internet of Things.
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>
@@ -22,7 +22,8 @@ started.
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
@@ -44,6 +45,9 @@ If you want to run the latest code from git, here's how to get started:
4. Run
npm start
or
node red.js
## Contributing
@@ -52,19 +56,17 @@ Before raising a pull-request, please read our
This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/).
By participating, you are expected to uphold this code. Please report unacceptable
behavior to any of the project's core team at team@nodered.org.
behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core).
## Authors
Node-RED is a project of the [JS Foundation](http://js.foundation).
It was created by [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
Node-RED is a creation of [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/).
* Nick O'Leary [@knolleary](http://twitter.com/knolleary)
* Dave Conway-Jones [@ceejay](http://twitter.com/ceejay)
For more open-source projects from IBM, head over [here](http://ibm.github.io).
## Copyright and license
Copyright JS Foundation and other contributors, http://js.foundation under [the Apache 2.0 license](LICENSE).
Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE).

View File

@@ -1,6 +1,6 @@
#!/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.
@@ -31,7 +31,7 @@ done
# Find the real location of this script
CURRENT_PATH=`pwd`
SCRIPT_PATH="${BASH_SOURCE[0]}";
while [ -h "${SCRIPT_PATH}" ]; do
while([ -h "${SCRIPT_PATH}" ]); do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 308 B

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

View File

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 508 B

View File

Before

Width:  |  Height:  |  Size: 575 B

After

Width:  |  Height:  |  Size: 575 B

View File

Before

Width:  |  Height:  |  Size: 493 B

After

Width:  |  Height:  |  Size: 493 B

View File

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 601 B

View File

Before

Width:  |  Height:  |  Size: 459 B

After

Width:  |  Height:  |  Size: 459 B

View File

Before

Width:  |  Height:  |  Size: 218 B

After

Width:  |  Height:  |  Size: 218 B

View File

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 324 B

View File

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

View File

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 255 B

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 502 B

View File

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 449 B

View File

Before

Width:  |  Height:  |  Size: 253 B

After

Width:  |  Height:  |  Size: 253 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 639 B

After

Width:  |  Height:  |  Size: 639 B

View File

Before

Width:  |  Height:  |  Size: 402 B

After

Width:  |  Height:  |  Size: 402 B

View File

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 414 B

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 413 B

View File

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

View File

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 467 B

View File

Before

Width:  |  Height:  |  Size: 393 B

After

Width:  |  Height:  |  Size: 393 B

View File

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 423 B

View File

Before

Width:  |  Height:  |  Size: 360 B

After

Width:  |  Height:  |  Size: 360 B

View File

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 736 B

View File

Before

Width:  |  Height:  |  Size: 482 B

After

Width:  |  Height:  |  Size: 482 B

View File

Before

Width:  |  Height:  |  Size: 273 B

After

Width:  |  Height:  |  Size: 273 B

View File

Before

Width:  |  Height:  |  Size: 256 B

After

Width:  |  Height:  |  Size: 256 B

View File

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

View File

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

View File

Before

Width:  |  Height:  |  Size: 509 B

After

Width:  |  Height:  |  Size: 509 B

View File

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View File

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 628 B

View File

Before

Width:  |  Height:  |  Size: 258 B

After

Width:  |  Height:  |  Size: 258 B

View File

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 404 B

View File

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 591 B

View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 386 B

View File

Before

Width:  |  Height:  |  Size: 289 B

After

Width:  |  Height:  |  Size: 289 B

View File

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 368 B

View File

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 290 B

View File

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 392 B

View File

Before

Width:  |  Height:  |  Size: 192 B

After

Width:  |  Height:  |  Size: 192 B

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1019 B

After

Width:  |  Height:  |  Size: 1019 B

View File

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View File

Before

Width:  |  Height:  |  Size: 410 B

After

Width:  |  Height:  |  Size: 410 B

View File

Before

Width:  |  Height:  |  Size: 638 B

After

Width:  |  Height:  |  Size: 638 B

View File

Before

Width:  |  Height:  |  Size: 546 B

After

Width:  |  Height:  |  Size: 546 B

View File

Before

Width:  |  Height:  |  Size: 646 B

After

Width:  |  Height:  |  Size: 646 B

View File

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 588 B

View File

Before

Width:  |  Height:  |  Size: 502 B

After

Width:  |  Height:  |  Size: 502 B

View File

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

4
editor/js/debugger.js Normal file
View File

@@ -0,0 +1,4 @@
RED.debugger = (function() {
})();

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2015, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

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

@@ -0,0 +1,306 @@
/**
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.history = (function() {
var undo_history = [];
return {
//TODO: this function is a placeholder until there is a 'save' event that can be listened to
markAllDirty: function() {
for (var i=0;i<undo_history.length;i++) {
undo_history[i].dirty = true;
}
},
list: function() {
return undo_history
},
depth: function() {
return undo_history.length;
},
push: function(ev) {
undo_history.push(ev);
},
pop: function() {
var ev = undo_history.pop();
var i;
var node;
var subflow;
var modifiedTabs = {};
if (ev) {
if (ev.t == 'add') {
if (ev.nodes) {
for (i=0;i<ev.nodes.length;i++) {
node = RED.nodes.node(ev.nodes[i]);
if (node.z) {
modifiedTabs[node.z] = true;
}
RED.nodes.remove(ev.nodes[i]);
}
}
if (ev.links) {
for (i=0;i<ev.links.length;i++) {
RED.nodes.removeLink(ev.links[i]);
}
}
if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.removeWorkspace(ev.workspaces[i].id);
RED.workspaces.remove(ev.workspaces[i]);
}
}
if (ev.subflows) {
for (i=0;i<ev.subflows.length;i++) {
RED.nodes.removeSubflow(ev.subflows[i]);
RED.workspaces.remove(ev.subflows[i]);
}
}
if (ev.subflow) {
if (ev.subflow.instances) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
}
if (ev.subflow.hasOwnProperty('changed')) {
subflow = RED.nodes.subflow(ev.subflow.id);
if (subflow) {
subflow.changed = ev.subflow.changed;
}
}
}
if (ev.removedLinks) {
for (i=0;i<ev.removedLinks.length;i++) {
RED.nodes.addLink(ev.removedLinks[i]);
}
}
} else if (ev.t == "delete") {
if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.addWorkspace(ev.workspaces[i]);
RED.workspaces.add(ev.workspaces[i]);
}
}
if (ev.subflow && ev.subflow.subflow) {
RED.nodes.addSubflow(ev.subflow.subflow);
}
if (ev.subflowInputs && ev.subflowInputs.length > 0) {
subflow = RED.nodes.subflow(ev.subflowInputs[0].z);
subflow.in.push(ev.subflowInputs[0]);
subflow.in[0].dirty = true;
}
if (ev.subflowOutputs && ev.subflowOutputs.length > 0) {
subflow = RED.nodes.subflow(ev.subflowOutputs[0].z);
ev.subflowOutputs.sort(function(a,b) { return a.i-b.i});
for (i=0;i<ev.subflowOutputs.length;i++) {
var output = ev.subflowOutputs[i];
subflow.out.splice(output.i,0,output);
for (var j=output.i+1;j<subflow.out.length;j++) {
subflow.out[j].i++;
subflow.out[j].dirty = true;
}
RED.nodes.eachLink(function(l) {
if (l.source.type == "subflow:"+subflow.id) {
if (l.sourcePort >= output.i) {
l.sourcePort++;
}
}
});
}
}
if (ev.subflow && ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
}
if (subflow) {
RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) {
n.inputs = subflow.in.length;
n.outputs = subflow.out.length;
while (n.outputs > n.ports.length) {
n.ports.push(n.ports.length);
}
n.resize = true;
n.dirty = true;
});
}
if (ev.nodes) {
for (i=0;i<ev.nodes.length;i++) {
RED.nodes.add(ev.nodes[i]);
modifiedTabs[ev.nodes[i].z] = true;
}
}
if (ev.links) {
for (i=0;i<ev.links.length;i++) {
RED.nodes.addLink(ev.links[i]);
}
}
if (ev.changes) {
for (i in ev.changes) {
if (ev.changes.hasOwnProperty(i)) {
node = RED.nodes.node(i);
if (node) {
for (var d in ev.changes[i]) {
if (ev.changes[i].hasOwnProperty(d)) {
node[d] = ev.changes[i][d];
}
}
node.dirty = true;
}
}
}
}
} else if (ev.t == "move") {
for (i=0;i<ev.nodes.length;i++) {
var n = ev.nodes[i];
n.n.x = n.ox;
n.n.y = n.oy;
n.n.dirty = true;
n.n.changed = n.changed;
}
// A move could have caused a link splice
if (ev.links) {
for (i=0;i<ev.links.length;i++) {
RED.nodes.removeLink(ev.links[i]);
}
}
if (ev.removedLinks) {
for (i=0;i<ev.removedLinks.length;i++) {
RED.nodes.addLink(ev.removedLinks[i]);
}
}
} else if (ev.t == "edit") {
for (i in ev.changes) {
if (ev.changes.hasOwnProperty(i)) {
if (ev.node._def.defaults[i].type) {
// This is a config node property
var currentConfigNode = RED.nodes.node(ev.node[i]);
if (currentConfigNode) {
currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1);
}
var newConfigNode = RED.nodes.node(ev.changes[i]);
if (newConfigNode) {
newConfigNode.users.push(ev.node);
}
}
ev.node[i] = ev.changes[i];
}
}
if (ev.subflow) {
if (ev.subflow.hasOwnProperty('inputCount')) {
if (ev.node.in.length > ev.subflow.inputCount) {
ev.node.in.splice(ev.subflow.inputCount);
} else if (ev.subflow.inputs.length > 0) {
ev.node.in = ev.node.in.concat(ev.subflow.inputs);
}
}
if (ev.subflow.hasOwnProperty('outputCount')) {
if (ev.node.out.length > ev.subflow.outputCount) {
ev.node.out.splice(ev.subflow.outputCount);
} else if (ev.subflow.outputs.length > 0) {
ev.node.out = ev.node.out.concat(ev.subflow.outputs);
}
}
if (ev.subflow.hasOwnProperty('instances')) {
ev.subflow.instances.forEach(function(n) {
var node = RED.nodes.node(n.id);
if (node) {
node.changed = n.changed;
node.dirty = true;
}
});
}
RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) {
n.inputs = ev.node.in.length;
n.outputs = ev.node.out.length;
RED.editor.updateNodeProperties(n);
});
} else {
var outputMap;
if (ev.outputMap) {
outputMap = {};
for (var port in ev.outputMap) {
if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== -1) {
outputMap[ev.outputMap[port]] = port;
}
}
}
RED.editor.updateNodeProperties(ev.node,outputMap);
RED.editor.validateNode(ev.node);
}
if (ev.links) {
for (i=0;i<ev.links.length;i++) {
RED.nodes.addLink(ev.links[i]);
}
}
ev.node.dirty = true;
ev.node.changed = ev.changed;
} else if (ev.t == "createSubflow") {
if (ev.nodes) {
RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) {
n.z = ev.activeWorkspace;
n.dirty = true;
});
for (i=0;i<ev.nodes.length;i++) {
RED.nodes.remove(ev.nodes[i]);
}
}
if (ev.links) {
for (i=0;i<ev.links.length;i++) {
RED.nodes.removeLink(ev.links[i]);
}
}
RED.nodes.removeSubflow(ev.subflow.subflow);
RED.workspaces.remove(ev.subflow.subflow);
if (ev.removedLinks) {
for (i=0;i<ev.removedLinks.length;i++) {
RED.nodes.addLink(ev.removedLinks[i]);
}
}
} else if (ev.t == "reorder") {
if (ev.order) {
RED.workspaces.order(ev.order);
}
}
Object.keys(modifiedTabs).forEach(function(id) {
var subflow = RED.nodes.subflow(id);
if (subflow) {
RED.editor.validateNode(subflow);
}
});
RED.nodes.dirty(ev.dirty);
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
}
},
peek: function() {
return undo_history[undo_history.length-1];
}
}
})();

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

@@ -0,0 +1,43 @@
/**
* Copyright 2013, 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.i18n = (function() {
return {
init: function(done) {
i18n.init({
resGetPath: 'locales/__ns__',
dynamicLoad: false,
load:'current',
ns: {
namespaces: ["editor","node-red"],
defaultNs: "editor"
},
fallbackLng: ['en-US'],
useCookie: false
},function() {
done();
});
RED["_"] = function() {
return i18n.t.apply(null,arguments);
}
},
loadCatalog: function(namespace,done) {
i18n.loadNamespace(namespace,done);
}
}
})();

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

@@ -0,0 +1,264 @@
/**
* 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.
**/
(function() {
function loadNodeList() {
$.ajax({
headers: {
"Accept":"application/json"
},
cache: false,
url: 'nodes',
success: function(data) {
RED.nodes.setNodeList(data);
var nsCount = 0;
for (var i=0;i<data.length;i++) {
var ns = data[i];
if (ns.module != "node-red") {
nsCount++;
RED.i18n.loadCatalog(ns.id, function() {
nsCount--;
if (nsCount === 0) {
loadNodes();
}
});
}
}
if (nsCount === 0) {
loadNodes();
}
}
});
}
function loadNodes() {
$.ajax({
headers: {
"Accept":"text/html"
},
cache: false,
url: 'nodes',
success: function(data) {
$("body").append(data);
$("body").i18n();
$("#palette > .palette-spinner").hide();
$(".palette-scroll").removeClass("hide");
$("#palette-search").removeClass("hide");
loadFlows();
}
});
}
function loadFlows() {
$.ajax({
headers: {
"Accept":"application/json",
},
cache: false,
url: 'flows',
success: function(nodes) {
var currentHash = window.location.hash;
RED.nodes.version(nodes.rev);
RED.nodes.import(nodes.flows);
RED.nodes.dirty(false);
RED.view.redraw(true);
if (/^#flow\/.+$/.test(currentHash)) {
RED.workspaces.show(currentHash.substring(6));
}
RED.comms.subscribe("status/#",function(topic,msg) {
var parts = topic.split("/");
var node = RED.nodes.node(parts[1]);
if (node) {
if (msg.hasOwnProperty("text")) {
msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()});
}
node.status = msg;
if (statusEnabled) {
node.dirty = true;
RED.view.redraw();
}
}
});
RED.comms.subscribe("node/#",function(topic,msg) {
var i,m;
var typeList;
var info;
if (topic == "node/added") {
var addedTypes = [];
for (i=0;i<msg.length;i++) {
m = msg[i];
var id = m.id;
RED.nodes.addNodeSet(m);
addedTypes = addedTypes.concat(m.types);
RED.i18n.loadCatalog(id, function() {
$.get('nodes/'+id, function(data) {
$("body").append(data);
});
});
}
if (addedTypes.length) {
typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success");
}
} else if (topic == "node/removed") {
for (i=0;i<msg.length;i++) {
m = msg[i];
info = RED.nodes.removeNodeSet(m.id);
if (info.added) {
typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success");
}
}
} else if (topic == "node/enabled") {
if (msg.types) {
info = RED.nodes.getNodeSet(msg.id);
if (info.added) {
RED.nodes.enableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success");
} else {
$.get('nodes/'+msg.id, function(data) {
$("body").append(data);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success");
});
}
}
} else if (topic == "node/disabled") {
if (msg.types) {
RED.nodes.disableNodeSet(msg.id);
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
}
}
// Refresh flow library to ensure any examples are updated
RED.library.loadFlowLibrary();
});
}
});
}
function showAbout() {
$.get('red/about', function(data) {
var aboutHeader = '<div style="text-align:center;">'+
'<img width="50px" src="red/images/node-red-icon.svg" />'+
'</div>';
RED.sidebar.info.set(aboutHeader+marked(data));
RED.sidebar.info.show();
});
}
var statusEnabled = false;
function toggleStatus(state) {
statusEnabled = state;
RED.view.status(statusEnabled);
}
function loadEditor() {
var menuOptions = [];
menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[
{id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid},
{id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid},
{id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true},
null,
// {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[
// {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}},
// {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("ltr")}}},
// {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("rtl")}}},
// {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}}
// ]},
// null,
{id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}
]});
menuOptions.push(null);
menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[
{id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import},
{id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]}
]});
menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[
{id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export},
{id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export}
]});
menuOptions.push(null);
menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:RED.search.show});
menuOptions.push(null);
menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function() {}});
menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[
{id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add},
{id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit},
{id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove}
]});
menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [
{id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow},
{id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow},
]});
menuOptions.push(null);
if (RED.settings.theme('palette.editable') !== false) {
RED.palette.editor.init();
menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:RED.palette.editor.show});
menuOptions.push(null);
}
menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp});
menuOptions.push({id:"menu-item-help",
label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"),
href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs")
});
menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: showAbout });
RED.menu.init({id:"btn-sidemenu",options: menuOptions});
RED.user.init();
RED.library.init();
RED.palette.init();
RED.sidebar.init();
RED.subflow.init();
RED.workspaces.init();
RED.clipboard.init();
RED.search.init();
RED.view.init();
RED.editor.init();
RED.deploy.init(RED.settings.theme("deployButton",null));
RED.keyboard.add("workspace", /* ? */ 191,{shift:true},function() {RED.keyboard.showHelp();d3.event.preventDefault();});
RED.comms.connect();
$("#main-container").show();
$(".header-toolbar").show();
loadNodeList();
}
$(function() {
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
document.title = document.title+" : "+window.location.hostname;
}
ace.require("ace/ext/language_tools");
RED.i18n.init(function() {
RED.settings.init(loadEditor);
})
});
})();

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,13 +24,21 @@ RED.nodes = (function() {
var workspacesOrder =[];
var subflows = {};
var loadedFlowVersion = null;
var initialLoad;
var pending = {
deleted: {},
added: {}
};
var dirty = false;
function setDirty(d) {
dirty = d;
if (!d) {
pending = {
deleted: {},
added: {}
};
}
RED.events.emit("nodes:change",{dirty:dirty});
}
@@ -40,22 +48,8 @@ RED.nodes = (function() {
var nodeSets = {};
var typeToId = {};
var nodeDefinitions = {};
var iconSets = {};
nodeDefinitions['tab'] = {
defaults: {
label: {value:""},
disabled: {value: false},
info: {value: ""}
}
};
var exports = {
setModulePendingUpdated: function(module,version) {
moduleList[module].pending_version = version;
RED.events.emit("registry:module-updated",{module:module,version:version});
},
getModule: function(module) {
return moduleList[module];
},
@@ -92,9 +86,6 @@ RED.nodes = (function() {
local:ns.local,
sets:{}
};
if (ns.pending_version) {
moduleList[ns.module].pending_version = ns.pending_version;
}
moduleList[ns.module].sets[ns.name] = ns;
RED.events.emit("registry:node-set-added",ns);
},
@@ -132,8 +123,7 @@ RED.nodes = (function() {
},
registerNodeType: function(nt,def) {
nodeDefinitions[nt] = def;
def.type = nt;
if (nt.substring(0,8) != "subflow:") {
if (def.category != "subflows") {
def.set = nodeSets[typeToId[nt]];
nodeSets[typeToId[nt]].added = true;
nodeSets[typeToId[nt]].enabled = true;
@@ -146,15 +136,10 @@ RED.nodes = (function() {
}
def["_"] = function() {
var args = Array.prototype.slice.call(arguments, 0);
var original = args[0];
if (args[0].indexOf(":") === -1) {
args[0] = ns+":"+args[0];
}
var result = RED._.apply(null,args);
if (result === args[0]) {
result = original;
}
return result;
return RED._.apply(null,args);
}
// TODO: too tightly coupled into palette UI
@@ -171,13 +156,6 @@ RED.nodes = (function() {
},
getNodeType: function(nt) {
return nodeDefinitions[nt];
},
setIconSets: function(sets) {
iconSets = sets;
iconSets["font-awesome"] = RED.nodes.fontAwesome.getIconList();
},
getIconSets: function() {
return iconSets;
}
};
return exports;
@@ -190,14 +168,11 @@ RED.nodes = (function() {
function addNode(n) {
if (n.type.indexOf("subflow") !== 0) {
n["_"] = n._def._;
} else {
n["_"] = RED._;
}
if (n._def.category == "config") {
configNodes[n.id] = n;
} else {
n.ports = [];
if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; }
if (n.outputs) {
for (var i=0;i<n.outputs;i++) {
n.ports.push(i);
@@ -214,6 +189,8 @@ RED.nodes = (function() {
}
nodes.push(n);
}
delete pending.deleted[n.id];
pending.added[n.id] = true;
RED.events.emit('nodes:add',n);
}
function addLink(l) {
@@ -273,21 +250,18 @@ RED.nodes = (function() {
if (updatedConfigNode) {
RED.workspaces.refresh();
}
try {
if (node._def.oneditdelete) {
node._def.oneditdelete.call(node);
}
} catch(err) {
console.log("oneditdelete",node.id,node.type,err.toString());
}
RED.events.emit('nodes:remove',node);
}
}
if (node && node._def.onremove) {
// Deprecated: never documented but used by some early nodes
console.log("Deprecated API warning: node type ",node.type," has an onremove function - should be oneditremove - please report");
node._def.onremove.call(n);
}
delete pending.added[id];
pending.deleted[id] = true;
removedNodes.forEach(function(node) {
delete pending.added[node.id];
pending.deleted[node.id] = true;
});
return {links:removedLinks,nodes:removedNodes};
}
@@ -298,14 +272,17 @@ RED.nodes = (function() {
}
}
function addWorkspace(ws,targetIndex) {
function addWorkspace(ws) {
workspaces[ws.id] = ws;
ws._def = RED.nodes.getType('tab');
if (targetIndex === undefined) {
workspacesOrder.push(ws.id);
} else {
workspacesOrder.splice(targetIndex,0,ws.id);
}
pending.added[ws.id] = true;
delete pending.deleted[ws.id];
ws._def = {
defaults: {
label: {value:""}
}
};
workspacesOrder.push(ws.id);
}
function getWorkspace(id) {
return workspaces[id];
@@ -336,6 +313,8 @@ RED.nodes = (function() {
var result = removeNode(removedNodes[n].id);
removedLinks = removedLinks.concat(result.links);
}
pending.deleted[id] = true;
delete pending.added[id]
return {nodes:removedNodes,links:removedLinks};
}
@@ -356,43 +335,42 @@ RED.nodes = (function() {
});
sf.name = subflowName;
}
sf._def = {
defaults:{},
icon:"subflow.png",
category: "subflows",
color: "#da9",
inputs: sf.in.length,
outputs: sf.out.length
}
subflows[sf.id] = sf;
delete pending.deleted[sf.id];
pending.added[sf.id] = true;
RED.nodes.registerType("subflow:"+sf.id, {
defaults:{
name:{value:""},
env:{value:[]}
},
icon: function() { return sf.icon||"subflow.png" },
category: sf.category || "subflows",
defaults:{name:{value:""}},
info: sf.info,
icon:"subflow.png",
category: "subflows",
inputs: sf.in.length,
outputs: sf.out.length,
color: "#da9",
label: function() { return this.name||RED.nodes.subflow(sf.id).name },
labelStyle: function() { return this.name?"node_label_italic":""; },
paletteLabel: function() { return RED.nodes.subflow(sf.id).name },
inputLabels: function(i) { return sf.inputLabels?sf.inputLabels[i]:null },
outputLabels: function(i) { return sf.outputLabels?sf.outputLabels[i]:null },
oneditresize: function(size) {
var rows = $("#dialog-form>div:not(.node-input-env-container-row)");
var height = size.height;
for (var i=0; i<rows.size(); i++) {
height -= $(rows[i]).outerHeight(true);
}
var editorRow = $("#dialog-form>div.node-input-env-container-row");
height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
$("#node-input-env-container").editableList('height',height-80);
},
set:{
module: "node-red"
}
});
sf._def = RED.nodes.getType("subflow:"+sf.id);
}
function getSubflow(id) {
return subflows[id];
}
function removeSubflow(sf) {
delete subflows[sf.id];
delete pending.added[sf.id];
pending.deleted[sf.id] = true;
registry.removeNodeType("subflow:"+sf.id);
}
@@ -463,7 +441,6 @@ RED.nodes = (function() {
node.id = n.id;
node.type = n.type;
node.z = n.z;
if (node.type == "unknown") {
for (var p in n._orig) {
if (n._orig.hasOwnProperty(p)) {
@@ -508,33 +485,9 @@ RED.nodes = (function() {
for (var j=0;j<wires.length;j++) {
var w = wires[j];
if (w.target.type != "subflow") {
if (w.sourcePort < node.wires.length) {
node.wires[w.sourcePort].push(w.target.id);
}
node.wires[w.sourcePort].push(w.target.id);
}
}
if (n.inputs > 0 && n.inputLabels && !/^\s*$/.test(n.inputLabels.join(""))) {
node.inputLabels = n.inputLabels.slice();
}
if (n.outputs > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
node.outputLabels = n.outputLabels.slice();
}
if ((!n._def.defaults || !n._def.defaults.hasOwnProperty("icon")) && n.icon) {
var defIcon = RED.utils.getDefaultNodeIcon(n._def, n);
if (n.icon !== defIcon.module+"/"+defIcon.file) {
node.icon = n.icon;
}
}
if ((!n._def.defaults || !n._def.defaults.hasOwnProperty("l")) && n.hasOwnProperty('l')) {
var isLink = /^link (in|out)$/.test(node.type);
if (isLink == n.l) {
node.l = n.l;
}
}
}
if (n.info) {
node.info = n.info;
}
return node;
}
@@ -545,10 +498,8 @@ RED.nodes = (function() {
node.type = n.type;
node.name = n.name;
node.info = n.info;
node.category = n.category;
node.in = [];
node.out = [];
node.env = n.env;
n.in.forEach(function(p) {
var nIn = {x:p.x,y:p.y,wires:[]};
@@ -574,39 +525,16 @@ RED.nodes = (function() {
node.out.push(nOut);
});
if (node.in.length > 0 && n.inputLabels && !/^\s*$/.test(n.inputLabels.join(""))) {
node.inputLabels = n.inputLabels.slice();
}
if (node.out.length > 0 && n.outputLabels && !/^\s*$/.test(n.outputLabels.join(""))) {
node.outputLabels = n.outputLabels.slice();
}
if (n.icon) {
if (n.icon !== "node-red/subflow.png") {
node.icon = n.icon;
}
}
if (n.status) {
node.status = {x: n.status.x, y: n.status.y, wires:[]};
links.forEach(function(d) {
if (d.target === n.status) {
if (d.source.type != "subflow") {
node.status.wires.push({id:d.source.id, port:d.sourcePort})
} else {
node.status.wires.push({id:n.id, port:0})
}
}
});
}
return node;
}
/**
* Converts the current node selection to an exportable JSON Object
**/
function createExportableNodeSet(set, exportedSubflows, exportedConfigNodes) {
function createExportableNodeSet(set) {
var nns = [];
exportedConfigNodes = exportedConfigNodes || {};
exportedSubflows = exportedSubflows || {};
var exportedConfigNodes = {};
var exportedSubflows = {};
for (var n=0;n<set.length;n++) {
var node = set[n];
if (node.type.substring(0,8) == "subflow:") {
@@ -620,7 +548,7 @@ RED.nodes = (function() {
subflowSet.push(n);
}
});
var exportableSubflow = createExportableNodeSet(subflowSet, exportedSubflows, exportedConfigNodes);
var exportableSubflow = createExportableNodeSet(subflowSet);
nns = exportableSubflow.concat(nns);
}
}
@@ -679,39 +607,42 @@ RED.nodes = (function() {
}
function checkForMatchingSubflow(subflow,subflowNodes) {
subflowNodes = subflowNodes || [];
var i;
var match = null;
RED.nodes.eachSubflow(function(sf) {
if (sf.name != subflow.name ||
sf.info != subflow.info ||
sf.in.length != subflow.in.length ||
sf.out.length != subflow.out.length) {
try {
RED.nodes.eachSubflow(function(sf) {
if (sf.name != subflow.name ||
sf.info != subflow.info ||
sf.in.length != subflow.in.length ||
sf.out.length != subflow.out.length) {
return;
}
var sfNodes = RED.nodes.filterNodes({z:sf.id});
if (sfNodes.length != subflowNodes.length) {
return;
}
var sfNodes = RED.nodes.filterNodes({z:sf.id});
if (sfNodes.length != subflowNodes.length) {
return;
}
}
var subflowNodeSet = [subflow].concat(subflowNodes);
var sfNodeSet = [sf].concat(sfNodes);
var subflowNodeSet = [subflow].concat(subflowNodes);
var sfNodeSet = [sf].concat(sfNodes);
var exportableSubflowNodes = JSON.stringify(subflowNodeSet);
var exportableSFNodes = JSON.stringify(createExportableNodeSet(sfNodeSet));
var nodeMap = {};
for (i=0;i<sfNodes.length;i++) {
exportableSubflowNodes = exportableSubflowNodes.replace(new RegExp("\""+subflowNodes[i].id+"\"","g"),'"'+sfNodes[i].id+'"');
}
exportableSubflowNodes = exportableSubflowNodes.replace(new RegExp("\""+subflow.id+"\"","g"),'"'+sf.id+'"');
var exportableSubflowNodes = JSON.stringify(subflowNodeSet);
var exportableSFNodes = JSON.stringify(createExportableNodeSet(sfNodeSet));
var nodeMap = {};
for (i=0;i<sfNodes.length;i++) {
exportableSubflowNodes = exportableSubflowNodes.replace(new RegExp("\""+subflowNodes[i].id+"\"","g"),'"'+sfNodes[i].id+'"');
}
exportableSubflowNodes = exportableSubflowNodes.replace(new RegExp("\""+subflow.id+"\"","g"),'"'+sf.id+'"');
if (exportableSubflowNodes !== exportableSFNodes) {
return;
}
if (exportableSubflowNodes !== exportableSFNodes) {
return;
}
match = sf;
return false;
});
match = sf;
throw new Error();
});
} catch(err) {
console.log(err.stack);
}
return match;
}
function compareNodes(nodeA,nodeB,idMustMatch) {
@@ -766,25 +697,6 @@ RED.nodes = (function() {
if (!$.isArray(newNodes)) {
newNodes = [newNodes];
}
// Scan for any duplicate nodes and remove them. This is a temporary
// fix to help resolve corrupted flows caused by 0.20.0 where multiple
// copies of the flow would get loaded at the same time.
// If the user hit deploy they would have saved those duplicates.
var seenIds = {};
newNodes = newNodes.filter(function(n) {
if (seenIds[n.id]) {
return false;
}
seenIds[n.id] = true;
return true;
})
var isInitialLoad = false;
if (!initialLoad) {
isInitialLoad = true;
initialLoad = JSON.parse(JSON.stringify(newNodes));
}
var unknownTypes = [];
for (i=0;i<newNodes.length;i++) {
n = newNodes[i];
@@ -803,25 +715,24 @@ RED.nodes = (function() {
}
}
if (!isInitialLoad && unknownTypes.length > 0) {
if (unknownTypes.length > 0) {
var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
RED.notify("<p>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</p>"+typeList,"error",false,10000);
var type = "type"+(unknownTypes.length > 1?"s":"");
RED.notify("<strong>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</strong>"+typeList,"error",false,10000);
}
var activeWorkspace = RED.workspaces.active();
//TODO: check the z of the subflow instance and check _that_ if it exists
var activeSubflow = getSubflow(activeWorkspace);
for (i=0;i<newNodes.length;i++) {
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
if (m) {
var subflowId = m[1];
var parent = getSubflow(newNodes[i].z || activeWorkspace);
if (parent) {
if (activeSubflow) {
for (i=0;i<newNodes.length;i++) {
var m = /^subflow:(.+)$/.exec(newNodes[i].type);
if (m) {
var subflowId = m[1];
var err;
if (subflowId === parent.id) {
if (subflowId === activeSubflow.id) {
err = new Error(RED._("notification.errors.cannotAddSubflowToItself"));
}
if (subflowContains(subflowId,parent.id)) {
if (subflowContains(m[1],activeSubflow.id)) {
err = new Error(RED._("notification.errors.cannotAddCircularReference"));
}
if (err) {
@@ -891,12 +802,6 @@ RED.nodes = (function() {
output.i = i;
output.id = getID();
});
if (n.status) {
n.status.type = "subflow";
n.status.direction = "status";
n.status.z = n.id;
n.status.id = getID();
}
new_subflows.push(n);
addSubflow(n,createNewIds);
}
@@ -905,7 +810,7 @@ RED.nodes = (function() {
// Add a tab if there isn't one there already
if (defaultWorkspace == null) {
defaultWorkspace = { type:"tab", id:getID(), disabled: false, info:"", label:RED._('workspace.defaultName',{number:1})};
defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})};
addWorkspace(defaultWorkspace);
RED.workspaces.add(defaultWorkspace);
new_workspaces.push(defaultWorkspace);
@@ -958,15 +863,8 @@ RED.nodes = (function() {
}
if (!existingConfigNode || existingConfigNode._def.exclusive) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode.z !== n.z) {
configNode = {
id:n.id,
z:n.z,
type:n.type,
info: n.info,
users:[],
_config:{}
};
if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) {
configNode = {id:n.id, z:n.z, type:n.type, users:[], _config:{}};
for (d in def.defaults) {
if (def.defaults.hasOwnProperty(d)) {
configNode[d] = n[d];
@@ -1000,22 +898,7 @@ RED.nodes = (function() {
if (n.type !== "workspace" && n.type !== "tab" && n.type !== "subflow") {
def = registry.getNodeType(n.type);
if (!def || def.category != "config") {
var node = {
x:parseFloat(n.x || 0),
y:parseFloat(n.y || 0),
z:n.z,
type:0,
wires:n.wires||[],
inputLabels: n.inputLabels,
outputLabels: n.outputLabels,
icon: n.icon,
info: n.info,
changed:false,
_config:{}
};
if (n.hasOwnProperty('l')) {
node.l = n.l;
}
var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false,_config:{}};
if (createNewIds) {
if (subflow_blacklist[n.z]) {
continue;
@@ -1064,7 +947,6 @@ RED.nodes = (function() {
node.name = n.name;
node.outputs = subflow.out.length;
node.inputs = subflow.in.length;
node.env = n.env;
} else {
if (!node._def) {
if (node.x && node.y) {
@@ -1082,13 +964,6 @@ RED.nodes = (function() {
set: registry.getNodeSet("node-red/unknown")
};
node.users = [];
// This is a config node, so delete the default
// non-config node properties
delete node.x;
delete node.y;
delete node.wires;
delete node.inputLabels;
delete node.outputLabels;
}
var orig = {};
for (var p in n) {
@@ -1101,31 +976,10 @@ RED.nodes = (function() {
node.type = "unknown";
}
if (node._def.category != "config") {
if (n.hasOwnProperty('inputs')) {
node.inputs = n.inputs;
node._config.inputs = JSON.stringify(n.inputs);
} else {
node.inputs = node._def.inputs;
}
if (n.hasOwnProperty('outputs')) {
node.outputs = n.outputs;
node._config.outputs = JSON.stringify(n.outputs);
} else {
node.outputs = node._def.outputs;
}
if (node.hasOwnProperty('wires') && node.wires.length > node.outputs) {
if (!node._def.defaults.hasOwnProperty("outputs") || !isNaN(parseInt(n.outputs))) {
// If 'wires' is longer than outputs, clip wires
console.log("Warning: node.wires longer than node.outputs - trimming wires:",node.id," wires:",node.wires.length," outputs:",node.outputs);
node.wires = node.wires.slice(0,node.outputs);
} else {
// The node declares outputs in its defaults, but has not got a valid value
// Defer to the length of the wires array
node.outputs = node.wires.length;
}
}
node.inputs = n.inputs||node._def.inputs;
node.outputs = n.outputs||node._def.outputs;
for (d in node._def.defaults) {
if (node._def.defaults.hasOwnProperty(d) && d !== 'inputs' && d !== 'outputs') {
if (node._def.defaults.hasOwnProperty(d)) {
node[d] = n[d];
node._config[d] = JSON.stringify(n[d]);
}
@@ -1145,9 +999,7 @@ RED.nodes = (function() {
addNode(node);
RED.editor.validateNode(node);
node_map[n.id] = node;
// If an 'unknown' config node, it will not have been caught by the
// proper config node handling, so needs adding to new_nodes here
if (node.type === "unknown" || node._def.category !== "config") {
if (node._def.category != "config") {
new_nodes.push(node);
}
}
@@ -1162,6 +1014,7 @@ RED.nodes = (function() {
"link out":"links"
}
// Remap all wires and config node references
for (i=0;i<new_nodes.length;i++) {
n = new_nodes[i];
@@ -1169,14 +1022,10 @@ RED.nodes = (function() {
for (var w1=0;w1<n.wires.length;w1++) {
var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
for (var w2=0;w2<wires.length;w2++) {
if (node_map.hasOwnProperty(wires[w2])) {
if (n.z === node_map[wires[w2]].z) {
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
addLink(link);
new_links.push(link);
} else {
console.log("Warning: dropping link that crosses tabs:",n.id,"->",node_map[wires[w2]].id);
}
if (wires[w2] in node_map) {
var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
addLink(link);
new_links.push(link);
}
}
}
@@ -1236,19 +1085,6 @@ RED.nodes = (function() {
});
delete output.wires;
});
if (n.status) {
n.status.wires.forEach(function(wire) {
var link;
if (subflow_map[wire.id] && subflow_map[wire.id].id == n.id) {
link = {source:n.in[wire.port], sourcePort:wire.port,target:n.status};
} else {
link = {source:node_map[wire.id]||subflow_map[wire.id], sourcePort:wire.port,target:n.status};
}
addLink(link);
new_links.push(link);
});
delete n.status.wires;
}
}
RED.workspaces.refresh();
@@ -1328,98 +1164,7 @@ RED.nodes = (function() {
}
}
function clear() {
nodes = [];
links = [];
configNodes = {};
workspacesOrder = [];
var subflowIds = Object.keys(subflows);
subflowIds.forEach(function(id) {
RED.subflow.removeSubflow(id)
});
var workspaceIds = Object.keys(workspaces);
workspaceIds.forEach(function(id) {
RED.workspaces.remove(workspaces[id]);
});
defaultWorkspace = null;
initialLoad = null;
RED.nodes.dirty(false);
RED.view.redraw(true);
RED.palette.refresh();
RED.workspaces.refresh();
RED.sidebar.config.refresh();
RED.sidebar.info.refresh();
// var node_defs = {};
// var nodes = [];
// var configNodes = {};
// var links = [];
// var defaultWorkspace;
// var workspaces = {};
// var workspacesOrder =[];
// var subflows = {};
// var loadedFlowVersion = null;
}
return {
init: function() {
RED.events.on("registry:node-type-added",function(type) {
var def = registry.getNodeType(type);
var replaced = false;
var replaceNodes = {};
RED.nodes.eachNode(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes[n.id] = n;
}
});
RED.nodes.eachConfig(function(n) {
if (n.type === "unknown" && n.name === type) {
replaceNodes[n.id] = n;
}
});
var replaceNodeIds = Object.keys(replaceNodes);
if (replaceNodeIds.length > 0) {
var reimportList = [];
replaceNodeIds.forEach(function(id) {
var n = replaceNodes[id];
if (configNodes.hasOwnProperty(n.id)) {
delete configNodes[n.id];
} else {
nodes.splice(nodes.indexOf(n),1);
}
reimportList.push(convertNode(n));
});
// Remove any links between nodes that are going to be reimported.
// This prevents a duplicate link from being added.
var removeLinks = [];
RED.nodes.eachLink(function(l) {
if (replaceNodes.hasOwnProperty(l.source.id) && replaceNodes.hasOwnProperty(l.target.id)) {
removeLinks.push(l);
}
});
removeLinks.forEach(removeLink);
RED.view.redraw(true);
var result = importNodes(reimportList,false);
var newNodeMap = {};
result[0].forEach(function(n) {
newNodeMap[n.id] = n;
});
RED.nodes.eachLink(function(l) {
if (newNodeMap.hasOwnProperty(l.source.id)) {
l.source = newNodeMap[l.source.id];
}
if (newNodeMap.hasOwnProperty(l.target.id)) {
l.target = newNodeMap[l.target.id];
}
});
RED.view.redraw(true);
}
});
},
registry:registry,
setNodeList: registry.setNodeList,
@@ -1429,16 +1174,12 @@ RED.nodes = (function() {
enableNodeSet: registry.enableNodeSet,
disableNodeSet: registry.disableNodeSet,
setIconSets: registry.setIconSets,
getIconSets: registry.getIconSets,
registerType: registry.registerNodeType,
getType: registry.getNodeType,
convertNode: convertNode,
add: addNode,
remove: removeNode,
clear: clear,
addLink: addLink,
removeLink: removeLink,
@@ -1456,60 +1197,45 @@ RED.nodes = (function() {
eachNode: function(cb) {
for (var n=0;n<nodes.length;n++) {
if (cb(nodes[n]) === false) {
break;
}
cb(nodes[n]);
}
},
eachLink: function(cb) {
for (var l=0;l<links.length;l++) {
if (cb(links[l]) === false) {
break;
}
cb(links[l]);
}
},
eachConfig: function(cb) {
for (var id in configNodes) {
if (configNodes.hasOwnProperty(id)) {
if (cb(configNodes[id]) === false) {
break;
}
cb(configNodes[id]);
}
}
},
eachSubflow: function(cb) {
for (var id in subflows) {
if (subflows.hasOwnProperty(id)) {
if (cb(subflows[id]) === false) {
break;
}
cb(subflows[id]);
}
}
},
eachWorkspace: function(cb) {
for (var i=0;i<workspacesOrder.length;i++) {
if (cb(workspaces[workspacesOrder[i]]) === false) {
break;
}
cb(workspaces[workspacesOrder[i]]);
}
},
node: getNode,
version: flowVersion,
originalFlow: function(flow) {
if (flow === undefined) {
return initialLoad;
} else {
initialLoad = flow;
}
},
filterNodes: filterNodes,
filterLinks: filterLinks,
import: importNodes,
pending: function() { return pending },
getAllFlowNodes: getAllFlowNodes,
createExportableNodeSet: createExportableNodeSet,
createCompleteNodeSet: createCompleteNodeSet,

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2013, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,5 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
module.exports = false
var RED = {};

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* 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.
@@ -18,9 +18,6 @@
RED.settings = (function () {
var loadedSettings = {};
var userSettings = {};
var settingsDirty = false;
var pendingSave;
var hasLocalStorage = function () {
try {
@@ -34,12 +31,7 @@ RED.settings = (function () {
if (!hasLocalStorage()) {
return;
}
if (key === "auth-tokens") {
localStorage.setItem(key, JSON.stringify(value));
} else {
userSettings[key] = value;
saveUserSettings();
}
localStorage.setItem(key, JSON.stringify(value));
};
/**
@@ -52,23 +44,14 @@ RED.settings = (function () {
if (!hasLocalStorage()) {
return undefined;
}
if (key === "auth-tokens") {
return JSON.parse(localStorage.getItem(key));
} else {
return userSettings[key];
}
return JSON.parse(localStorage.getItem(key));
};
var remove = function (key) {
if (!hasLocalStorage()) {
return;
}
if (key === "auth-tokens") {
localStorage.removeItem(key);
} else {
delete userSettings[key];
saveUserSettings();
}
localStorage.removeItem(key);
};
var setProperties = function(data) {
@@ -85,26 +68,18 @@ RED.settings = (function () {
loadedSettings = data;
};
var setUserSettings = function(data) {
userSettings = data;
}
var init = function (options, done) {
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 = "";
}
RED.settings.apiRootUrl = options.apiRootUrl;
$.ajaxSetup({
beforeSend: function(jqXHR,settings) {
// Only attach auth header for requests to relative paths
if (!/^\s*(https?:|\/|\.)/.test(settings.url)) {
if (options.apiRootUrl) {
settings.url = options.apiRootUrl+settings.url;
}
var auth_tokens = RED.settings.get("auth-tokens");
if (auth_tokens) {
jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token);
@@ -127,11 +102,11 @@ RED.settings = (function () {
url: 'settings',
success: function (data) {
setProperties(data);
if (!RED.settings.user || RED.settings.user.anonymous) {
if (RED.settings.user && RED.settings.user.anonymous) {
RED.settings.remove("auth-tokens");
}
console.log("Node-RED: " + data.version);
loadUserSettings(done);
done();
},
error: function(jqXHR,textStatus,errorThrown) {
if (jqXHR.status === 401) {
@@ -140,52 +115,12 @@ RED.settings = (function () {
}
RED.user.login(function() { load(done); });
} else {
console.log("Unexpected error loading settings:",jqXHR.status,textStatus);
console.log("Unexpected error:",jqXHR.status,textStatus);
}
}
});
};
function loadUserSettings(done) {
$.ajax({
headers: {
"Accept": "application/json"
},
dataType: "json",
cache: false,
url: 'settings/user',
success: function (data) {
setUserSettings(data);
done();
},
error: function(jqXHR,textStatus,errorThrown) {
console.log("Unexpected error loading user settings:",jqXHR.status,textStatus);
}
});
}
function saveUserSettings() {
if (RED.user.hasPermission("settings.write")) {
if (pendingSave) {
clearTimeout(pendingSave);
}
pendingSave = setTimeout(function() {
pendingSave = null;
$.ajax({
method: 'POST',
contentType: 'application/json',
url: 'settings/user',
data: JSON.stringify(userSettings),
success: function (data) {
},
error: function(jqXHR,textStatus,errorThrown) {
console.log("Unexpected error saving user settings:",jqXHR.status,textStatus);
}
});
},300);
}
}
function theme(property,defaultValue) {
if (!RED.settings.editorTheme) {
return defaultValue;
@@ -208,10 +143,10 @@ RED.settings = (function () {
return {
init: init,
load: load,
loadUserSettings: loadUserSettings,
set: set,
get: get,
remove: remove,
theme: theme
}
})();
})
();

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1242,7 +1242,7 @@ RED.text.format = (function() {
element.dispatchEvent(event);
return;
}
var range = selection.getRangeAt(0);
var tempRange = range.cloneRange(), startNode, startOffset;
startNode = range.startContainer;
@@ -1304,7 +1304,7 @@ RED.text.format = (function() {
}
return {
/*!
/**
* Returns the HTML representation of a given structured text
* @param text - the structured text
* @param type - could be one of filepath, url, email
@@ -1315,7 +1315,7 @@ RED.text.format = (function() {
getHtml: function (text, type, args, isRtl, locale) {
return getHandler(type).format(text, args, isRtl, true, locale);
},
/*!
/**
* Handle Structured text correct display for a given HTML element.
* @param element - the element : should be of type div contenteditable=true
* @param type - could be one of filepath, url, email

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

@@ -0,0 +1,307 @@
/**
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.clipboard = (function() {
var dialog;
var dialogContainer;
var exportNodesDialog;
var importNodesDialog;
function setupDialogs() {
dialog = $('<div id="clipboard-dialog" class="hide node-red-dialog"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("body")
.dialog({
modal: true,
autoOpen: false,
width: 500,
resizable: false,
buttons: [
{
id: "clipboard-dialog-cancel",
text: RED._("common.label.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-close",
class: "primary",
text: RED._("common.label.close"),
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-copy",
class: "primary",
text: RED._("clipboard.export.copy"),
click: function() {
$("#clipboard-export").select();
document.execCommand("copy");
document.getSelection().removeAllRanges();
RED.notify(RED._("clipboard.nodesExported"));
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-ok",
class: "primary",
text: RED._("common.label.import"),
click: function() {
RED.view.importNodes($("#clipboard-import").val(),$("#import-tab > a.selected").attr('id') === 'import-tab-new');
$( this ).dialog( "close" );
}
}
],
open: function(e) {
$(this).parent().find(".ui-dialog-titlebar-close").hide();
},
close: function(e) {
}
});
dialogContainer = dialog.children(".dialog-form");
exportNodesDialog =
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.export.copy"></label>'+
'<span id="export-range-group" class="button-group">'+
'<a id="export-range-selected" class="editor-button toggle" href="#" data-i18n="clipboard.export.selected"></a>'+
'<a id="export-range-flow" class="editor-button toggle" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="export-range-full" class="editor-button toggle" href="#" data-i18n="clipboard.export.all"></a>'+
'</span>'+
'</div>'+
'<div class="form-row">'+
'<textarea readonly style="resize: none; width: 100%; border-radius: 4px;font-family: monospace; font-size: 12px; background:#f3f3f3; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
'</div>'+
'<div class="form-row" style="text-align: right;">'+
'<span id="export-format-group" class="button-group">'+
'<a id="export-format-mini" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.compact"></a>'+
'<a id="export-format-full" class="editor-button editor-button-small toggle" href="#" data-i18n="clipboard.export.formatted"></a>'+
'</span>'+
'</div>';
importNodesDialog = '<div class="form-row">'+
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+
RED._("clipboard.pasteNodes")+
'"></textarea>'+
'</div>'+
'<div class="form-row">'+
'<label style="width:auto;margin-right: 10px;" data-i18n="clipboard.import.import"></label>'+
'<span id="import-tab" class="button-group">'+
'<a id="import-tab-current" class="editor-button toggle selected" href="#" data-i18n="clipboard.export.current"></a>'+
'<a id="import-tab-new" class="editor-button toggle" href="#" data-i18n="clipboard.import.newFlow"></a>'+
'</span>'+
'</div>';
}
function validateImport() {
var importInput = $("#clipboard-import");
var v = importInput.val();
v = v.substring(v.indexOf('['),v.lastIndexOf(']')+1);
try {
JSON.parse(v);
importInput.removeClass("input-error");
importInput.val(v);
$("#clipboard-dialog-ok").button("enable");
} catch(err) {
if (v !== "") {
importInput.addClass("input-error");
}
$("#clipboard-dialog-ok").button("disable");
}
}
function importNodes() {
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
dialogContainer.i18n();
$("#clipboard-dialog-ok").show();
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-close").hide();
$("#clipboard-dialog-copy").hide();
$("#clipboard-dialog-ok").button("disable");
$("#clipboard-import").keyup(validateImport);
$("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)});
$("#import-tab > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
});
dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open");
}
function exportNodes() {
dialogContainer.empty();
dialogContainer.append($(exportNodesDialog));
dialogContainer.i18n();
$("#export-format-group > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var flow = $("#clipboard-export").val();
if (flow.length > 0) {
var nodes = JSON.parse(flow);
var format = $(this).attr('id');
if (format === 'export-format-full') {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
$("#clipboard-export").val(flow);
}
});
$("#export-range-group > a").click(function(evt) {
evt.preventDefault();
if ($(this).hasClass('disabled') || $(this).hasClass('selected')) {
return;
}
$(this).parent().children().removeClass('selected');
$(this).addClass('selected');
var type = $(this).attr('id');
var flow = "";
var nodes = null;
if (type === 'export-range-selected') {
var selection = RED.view.selection();
nodes = RED.nodes.createExportableNodeSet(selection.nodes);
} else if (type === 'export-range-flow') {
var activeWorkspace = RED.workspaces.active();
nodes = RED.nodes.filterNodes({z:activeWorkspace});
var parentNode = RED.nodes.workspace(activeWorkspace)||RED.nodes.subflow(activeWorkspace);
nodes.unshift(parentNode);
nodes = RED.nodes.createExportableNodeSet(nodes);
} else if (type === 'export-range-full') {
nodes = RED.nodes.createCompleteNodeSet(false);
}
if (nodes !== null) {
if (RED.settings.flowFilePretty) {
flow = JSON.stringify(nodes,null,4);
} else {
flow = JSON.stringify(nodes);
}
}
if (flow.length > 0) {
$("#export-copy").removeClass('disabled');
} else {
$("#export-copy").addClass('disabled');
}
$("#clipboard-export").val(flow);
})
$("#clipboard-dialog-ok").hide();
$("#clipboard-dialog-cancel").hide();
$("#clipboard-dialog-copy").hide();
$("#clipboard-dialog-close").hide();
var selection = RED.view.selection();
if (selection.nodes) {
$("#export-range-selected").click();
} else {
$("#export-range-selected").addClass('disabled').removeClass('selected');
$("#export-range-flow").click();
}
if (RED.settings.flowFilePretty) {
$("#export-format-full").click();
} else {
$("#export-format-mini").click();
}
$("#clipboard-export")
.focus(function() {
var textarea = $(this);
textarea.select();
textarea.mouseup(function() {
textarea.unbind("mouseup");
return false;
})
});
dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" );
setTimeout(function() {
$("#clipboard-export").focus();
if (!document.queryCommandEnabled("copy")) {
$("#clipboard-dialog-cancel").hide();
$("#clipboard-dialog-close").show();
} else {
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-copy").show();
}
},0);
}
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
}
return {
init: function() {
setupDialogs();
RED.events.on("view:selection-changed",function(selection) {
if (!selection.nodes) {
RED.menu.setDisabled("menu-item-export",true);
RED.menu.setDisabled("menu-item-export-clipboard",true);
RED.menu.setDisabled("menu-item-export-library",true);
} else {
RED.menu.setDisabled("menu-item-export",false);
RED.menu.setDisabled("menu-item-export-clipboard",false);
RED.menu.setDisabled("menu-item-export-library",false);
}
});
RED.keyboard.add("workspace", /* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
RED.keyboard.add("workspace", /* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
$('#chart').on("dragenter",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
$("#dropTarget").css({display:'table'});
RED.keyboard.add("*", /* ESCAPE */ 27,hideDropTarget);
}
});
$('#dropTarget').on("dragover",function(event) {
if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
event.preventDefault();
}
})
.on("dragleave",function(event) {
hideDropTarget();
})
.on("drop",function(event) {
var data = event.originalEvent.dataTransfer.getData("text/plain");
hideDropTarget();
data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
RED.view.importNodes(data);
event.preventDefault();
});
},
import: importNodes,
export: exportNodes
}
})();

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,9 +27,6 @@
* - removable : boolean - whether to display delete button on items
* - addItem : function(row,index,itemData) - when an item is added
* - removeItem : function(itemData) - called when an item is removed
* - filter : function(itemData) - called for each item to determine if it should be shown
* - sort : function(itemDataA,itemDataB) - called to sort items
* - scrollOnAdd : boolean - whether to scroll to newly added items
* methods:
* - addItem(itemData)
* - removeItem(itemData)
@@ -37,9 +34,6 @@
* - height(height)
* - items()
* - empty()
* - filter(filter)
* - sort(sort)
* - length()
*/
$.widget( "nodered.editableList", {
_create: function() {
@@ -50,19 +44,9 @@
this.uiContainer = this.element
.wrap( "<div>" )
.parent();
this.topContainer = this.uiContainer.wrap("<div>").parent();
if (this.options.header) {
this.options.header.addClass("red-ui-editableList-header");
this.borderContainer = this.uiContainer.wrap("<div>").parent();
this.borderContainer.prepend(this.options.header);
this.topContainer = this.borderContainer.wrap("<div>").parent();
} else {
this.topContainer = this.uiContainer.wrap("<div>").parent();
}
this.topContainer.addClass('red-ui-editableList');
if (this.options.class) {
this.topContainer.addClass(this.options.class);
}
if (this.options.addButton !== false) {
var addLabel;
@@ -75,7 +59,7 @@
addLabel = 'add';
}
}
$('<a href="#" class="editor-button editor-button-small red-ui-editableList-addButton" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
$('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>')
.appendTo(this.topContainer)
.click(function(evt) {
evt.preventDefault();
@@ -85,7 +69,7 @@
if (this.element.css("position") === "absolute") {
["top","left","bottom","right"].forEach(function(s) {
var v = that.element.css(s);
if (v!=="auto" && v!=="") {
if (s!=="auto" && s!=="") {
that.topContainer.css(s,v);
that.uiContainer.css(s,"0");
that.element.css(s,'auto');
@@ -96,11 +80,6 @@
this.uiContainer.css("position","absolute");
}
if (this.options.header) {
this.borderContainer.addClass("red-ui-editableList-border");
} else {
this.uiContainer.addClass("red-ui-editableList-border");
}
this.uiContainer.addClass("red-ui-editableList-container");
this.uiHeight = this.element.height();
@@ -116,11 +95,6 @@
this.uiContainer.css("minHeight",minHeight);
this.element.css("minHeight",0);
}
var maxHeight = this.element.css("maxHeight");
if (maxHeight !== '0px') {
this.uiContainer.css("maxHeight",maxHeight);
this.element.css("maxHeight",null);
}
if (this.options.height !== 'auto') {
this.uiContainer.css("overflow-y","scroll");
if (!isNaN(this.options.height)) {
@@ -190,7 +164,7 @@
var that = this;
var count = 0;
if (!this.activeFilter) {
return this.element.children().show();
this.element.children().show();
}
var items = this.items();
items.each(function (i,el) {
@@ -293,11 +267,6 @@
},0);
}
},
addItems: function(items) {
for (var i=0; i<items.length;i++) {
this.addItem(items[i]);
}
},
removeItem: function(data) {
var items = this.element.children().filter(function(f) {
return data === $(this).find(".red-ui-editableList-item-content").data('data');
@@ -327,14 +296,6 @@
},
length: function() {
return this.element.children().length;
},
show: function(item) {
var items = this.element.children().filter(function(f) {
return item === $(this).find(".red-ui-editableList-item-content").data('data');
});
if (items.length > 0) {
this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top)
}
}
});
})(jQuery);

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2014, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.menu = (function() {
var menuItems = {};
@@ -28,30 +31,20 @@ RED.menu = (function() {
}
function setInitialState() {
var savedStateActive = RED.settings.get("menu-" + opt.id);
if (opt.setting) {
// May need to migrate pre-0.17 setting
if (savedStateActive !== null) {
RED.settings.set(opt.setting,savedStateActive);
RED.settings.remove("menu-" + opt.id);
} else {
savedStateActive = RED.settings.get(opt.setting);
}
}
var savedStateActive = isSavedStateActive(opt.id);
if (savedStateActive) {
link.addClass("active");
triggerAction(opt.id,true);
opt.onselect.call(opt, true);
} else if (savedStateActive === false) {
link.removeClass("active");
triggerAction(opt.id,false);
opt.onselect.call(opt, false);
} else if (opt.hasOwnProperty("selected")) {
if (opt.selected) {
link.addClass("active");
} else {
link.removeClass("active");
}
triggerAction(opt.id,opt.selected);
opt.onselect.call(opt, opt.selected);
}
}
@@ -92,8 +85,7 @@ RED.menu = (function() {
menuItems[opt.id] = opt;
if (opt.onselect) {
link.click(function(e) {
e.preventDefault();
link.click(function() {
if ($(this).parent().hasClass("disabled")) {
return;
}
@@ -115,12 +107,10 @@ RED.menu = (function() {
setSelected(opt.id, !selected);
}
} else {
triggerAction(opt.id);
opt.onselect.call(opt);
}
});
if (opt.toggle) {
setInitialState();
}
setInitialState();
} else if (opt.href) {
link.attr("target","_blank").attr("href",opt.href);
} else if (!opt.options) {
@@ -150,15 +140,16 @@ RED.menu = (function() {
}
function createMenu(options) {
var topMenu = $("<ul/>",{class:"dropdown-menu pull-right"});
if (options.id) {
topMenu.attr({id:options.id+"-submenu"});
var menuParent = $("#"+options.id);
if (menuParent.length === 1) {
topMenu.insertAfter(menuParent);
}
}
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++) {
@@ -171,27 +162,20 @@ RED.menu = (function() {
}
}
}
return topMenu;
}
function triggerAction(id, args) {
var opt = menuItems[id];
var callback = opt.onselect;
if (typeof opt.onselect === 'string') {
callback = RED.actions.get(opt.onselect);
}
if (callback) {
callback.call(opt,args);
} else {
console.log("No callback for",id,opt.onselect);
}
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;
@@ -203,13 +187,9 @@ RED.menu = (function() {
$("#"+id).removeClass("active");
}
if (opt && opt.onselect) {
triggerAction(opt.id,state);
opt.onselect.call(opt,state);
}
RED.settings.set(opt.setting||("menu-"+opt.id), state);
}
function toggleSelected(id) {
setSelected(id,!isSelected(id));
setSavedState(id, state);
}
function setDisabled(id,state) {
@@ -251,6 +231,16 @@ RED.menu = (function() {
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]);
// }
// });
}
}
@@ -258,7 +248,6 @@ RED.menu = (function() {
init: createMenu,
setSelected: setSelected,
isSelected: isSelected,
toggleSelected: toggleSelected,
setDisabled: setDisabled,
addItem: addItem,
removeItem: removeItem,

View File

@@ -0,0 +1,79 @@
/**
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
RED.popover = (function() {
function createPopover(options) {
var target = options.target;
var content = options.content;
var delay = options.delay;
var timer = null;
var active;
var div;
var openPopup = function() {
if (active) {
div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body");
var targetPos = target.offset();
var targetWidth = target.width();
var targetHeight = target.height();
var divHeight = div.height();
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
}
})();

View File

@@ -1,5 +1,5 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
* Copyright 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,19 +15,6 @@
**/
(function($) {
/**
* options:
* - minimumLength : the minimum length of text before firing a change event
* - delay : delay, in ms, after a keystroke before firing change event
*
* methods:
* - value([val]) - gets the current value, or, if `val` is provided, sets the value
* - count - sets or clears a sub-label on the input. This can be used to provide
* a feedback on the number of matches, or number of available entries to search
* - change - trigger a change event
*
*/
$.widget( "nodered.searchBox", {
_create: function() {
var that = this;
@@ -104,9 +91,6 @@
} else {
this.resultCount.text(val).show();
}
},
change: function() {
this._trigger("change");
}
});
})(jQuery);

351
editor/js/ui/common/tabs.js Normal file
View File

@@ -0,0 +1,351 @@
/**
* 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.tabs = (function() {
function createTabs(options) {
var tabs = {};
var currentTabWidth;
var currentActiveTabWidth = 0;
var ul = $("#"+options.id);
var wrapper = ul.wrap( "<div>" ).parent();
var scrollContainer = ul.wrap( "<div>" ).parent();
wrapper.addClass("red-ui-tabs");
if (options.addButton && typeof options.addButton === 'function') {
wrapper.addClass("red-ui-tabs-add");
var addButton = $('<div class="red-ui-tab-button"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper);
addButton.find('a').click(function(evt) {
evt.preventDefault();
options.addButton();
})
}
var scrollLeft;
var scrollRight;
if (options.scrollable) {
wrapper.addClass("red-ui-tabs-scrollable");
scrollContainer.addClass("red-ui-tabs-scroll-container");
scrollContainer.scroll(updateScroll);
scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a");
scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();});
scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a");
scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();});
}
function scrollEventHandler(evt,dir) {
evt.preventDefault();
if ($(this).hasClass('disabled')) {
return;
}
var currentScrollLeft = scrollContainer.scrollLeft();
scrollContainer.animate( { scrollLeft: dir }, 100);
var interval = setInterval(function() {
var newScrollLeft = scrollContainer.scrollLeft()
if (newScrollLeft === currentScrollLeft) {
clearInterval(interval);
return;
}
currentScrollLeft = newScrollLeft;
scrollContainer.animate( { scrollLeft: dir }, 100);
},100);
$(this).one('mouseup',function() {
clearInterval(interval);
})
}
ul.children().first().addClass("active");
ul.children().addClass("red-ui-tab");
function onTabClick() {
activateTab($(this));
return false;
}
function updateScroll() {
if (ul.children().length !== 0) {
var sl = scrollContainer.scrollLeft();
var scWidth = scrollContainer.width();
var ulWidth = ul.width();
if (sl === 0) {
scrollLeft.hide();
} else {
scrollLeft.show();
}
if (sl === ulWidth-scWidth) {
scrollRight.hide();
} else {
scrollRight.show();
}
}
}
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.scrollable) {
var pos = link.parent().position().left;
if (pos-21 < 0) {
scrollContainer.animate( { scrollLeft: '+='+(pos-50) }, 300);
} else if (pos + 120 > scrollContainer.width()) {
scrollContainer.animate( { scrollLeft: '+='+(pos + 140-scrollContainer.width()) }, 300);
}
}
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 = wrapper.width();
var tabCount = tabs.size();
var tabWidth = (width-12-(tabCount*6))/tabCount;
currentTabWidth = (100*tabWidth/width)+"%";
currentActiveTabWidth = currentTabWidth+"%";
if (options.scrollable) {
tabWidth = Math.max(tabWidth,140);
currentTabWidth = tabWidth+"px";
currentActiveTabWidth = 0;
var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount);
ul.width(listWidth);
updateScroll();
} else 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();
ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"})
} else {
ul.find(".red-ui-tab-close").show();
ul.find(".red-ui-tab-icon").show();
ul.find(".red-ui-tab-label").css({paddingLeft:""})
}
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.active .red-ui-tab-label").css({paddingLeft:""})
}
}
ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick);
setTimeout(function() {
updateTabWidths();
},0);
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);
li.data("tabId",tab.id);
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);
}
var span = $('<span/>',{class:"bidiAware"}).text(tab.label).appendTo(link);
span.attr('dir', RED.text.bidi.resolveBaseTextDir(tab.label));
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);
}
if (options.onreorder) {
var originalTabOrder;
var tabDragIndex;
var tabElements = [];
var startDragIndex;
li.draggable({
axis:"x",
distance: 20,
start: function(event,ui) {
originalTabOrder = [];
tabElements = [];
ul.children().each(function(i) {
tabElements[i] = {
el:$(this),
text: $(this).text(),
left: $(this).position().left,
width: $(this).width()
};
if ($(this).is(li)) {
tabDragIndex = i;
startDragIndex = i;
}
originalTabOrder.push($(this).data("tabId"));
});
ul.children().each(function(i) {
if (i!==tabDragIndex) {
$(this).css({
position: 'absolute',
left: tabElements[i].left+"px",
width: tabElements[i].width+2,
transition: "left 0.3s"
});
}
})
if (!li.hasClass('active')) {
li.css({'zIndex':1});
}
},
drag: function(event,ui) {
ui.position.left += tabElements[tabDragIndex].left+scrollContainer.scrollLeft();
var tabCenter = ui.position.left + tabElements[tabDragIndex].width/2 - scrollContainer.scrollLeft();
for (var i=0;i<tabElements.length;i++) {
if (i === tabDragIndex) {
continue;
}
if (tabCenter > tabElements[i].left && tabCenter < tabElements[i].left+tabElements[i].width) {
if (i < tabDragIndex) {
tabElements[i].left += tabElements[tabDragIndex].width+8;
tabElements[tabDragIndex].el.detach().insertBefore(tabElements[i].el);
} else {
tabElements[i].left -= tabElements[tabDragIndex].width+8;
tabElements[tabDragIndex].el.detach().insertAfter(tabElements[i].el);
}
tabElements[i].el.css({left:tabElements[i].left+"px"});
tabElements.splice(i, 0, tabElements.splice(tabDragIndex, 1)[0]);
tabDragIndex = i;
break;
}
}
},
stop: function(event,ui) {
ul.children().css({position:"relative",left:"",transition:""});
if (!li.hasClass('active')) {
li.css({zIndex:""});
}
updateTabWidths();
if (startDragIndex !== tabDragIndex) {
options.onreorder(originalTabOrder, $.makeArray(ul.children().map(function() { return $(this).data('tabId');})));
}
activateTab(tabElements[tabDragIndex].el.data('tabId'));
}
})
}
},
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).attr('dir', RED.text.bidi.resolveBaseTextDir(label));
updateTabWidths();
},
order: function(order) {
var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');}));
if (existingTabOrder.length !== order.length) {
return
}
var i;
var match = true;
for (i=0;i<order.length;i++) {
if (order[i] !== existingTabOrder[i]) {
match = false;
break;
}
}
if (match) {
return;
}
var existingTabMap = {};
var existingTabs = ul.children().detach().each(function() {
existingTabMap[$(this).data("tabId")] = $(this);
});
for (i=0;i<order.length;i++) {
existingTabMap[order[i]].appendTo(ul);
}
}
}
}
return {
create: createTabs
}
})();

View File

@@ -0,0 +1,472 @@
/**
* Copyright 2015, 2016 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
(function($) {
function validateExpression(str) {
var length = str.length;
var start = 0;
var inString = false;
var inBox = false;
var quoteChar;
var v;
for (var i=0;i<length;i++) {
var c = str[i];
if (!inString) {
if (c === "'" || c === '"') {
if (!inBox) {
return false;
}
inString = true;
quoteChar = c;
start = i+1;
} else if (c === '.') {
if (i===0 || i===length-1) {
return false;
}
// Next char is a-z
if (!/[a-z0-9]/i.test(str[i+1])) {
return false;
}
start = i+1;
} else if (c === '[') {
if (i === 0) {
return false;
}
if (i===length-1) {
return false;
}
// Next char is either a quote or a number
if (!/["'\d]/.test(str[i+1])) {
return false;
}
start = i+1;
inBox = true;
} else if (c === ']') {
if (!inBox) {
return false;
}
if (start != i) {
v = str.substring(start,i);
if (!/^\d+$/.test(v)) {
return false;
}
}
start = i+1;
inBox = false;
} else if (c === ' ') {
return false;
}
} else {
if (c === quoteChar) {
// Next char must be a ]
if (!/\]/.test(str[i+1])) {
return false;
}
start = i+1;
inString = false;
}
}
}
if (inBox || inString) {
return false;
}
return true;
}
var allOptions = {
msg: {value:"msg",label:"msg.",validate:validateExpression},
flow: {value:"flow",label:"flow.",validate:validateExpression},
global: {value:"global",label:"global.",validate:validateExpression},
str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"},
num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/},
bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]},
json: {value:"json",label:"JSON",icon:"red/images/typedInput/json.png", validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}},
re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.png"},
date: {value:"date",label:"timestamp",hasValue:false}
};
var nlsd = false;
$.widget( "nodered.typedInput", {
_create: function() {
if (!nlsd && RED && RED._) {
for (var i in allOptions) {
if (allOptions.hasOwnProperty(i)) {
allOptions[i].label = RED._("typedInput.type."+i,{defaultValue:allOptions[i].label});
}
}
}
nlsd = true;
var that = this;
this.disarmClick = false;
this.element.addClass('red-ui-typedInput');
this.uiWidth = this.element.outerWidth();
this.elementDiv = this.element.wrap("<div>").parent().addClass('red-ui-typedInput-input');
this.uiSelect = this.elementDiv.wrap( "<div>" ).parent();
var attrStyle = this.element.attr('style');
var m;
if ((m = /width\s*:\s*(\d+(%|px))/i.exec(attrStyle)) !== null) {
this.element.css('width','100%');
this.uiSelect.width(m[1]);
this.uiWidth = null;
} else {
this.uiSelect.width(this.uiWidth);
}
["Right","Left"].forEach(function(d) {
var m = that.element.css("margin"+d);
that.uiSelect.css("margin"+d,m);
that.element.css("margin"+d,0);
});
this.uiSelect.addClass("red-ui-typedInput-container");
this.options.types = this.options.types||Object.keys(allOptions);
this.selectTrigger = $('<button tabindex="0"></button>').prependTo(this.uiSelect);
$('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger);
this.selectLabel = $('<span></span>').appendTo(this.selectTrigger);
this.types(this.options.types);
if (this.options.typeField) {
this.typeField = $(this.options.typeField).hide();
var t = this.typeField.val();
if (t && this.typeMap[t]) {
this.options.default = t;
}
} else {
this.typeField = $("<input>",{type:'hidden'}).appendTo(this.uiSelect);
}
this.element.on('focus', function() {
that.uiSelect.addClass('red-ui-typedInput-focus');
});
this.element.on('blur', function() {
that.uiSelect.removeClass('red-ui-typedInput-focus');
});
this.element.on('change', function() {
that.validate();
})
this.selectTrigger.click(function(event) {
event.preventDefault();
that._showTypeMenu();
});
this.selectTrigger.on('keydown',function(evt) {
if (evt.keyCode === 40) {
// Down
that._showTypeMenu();
}
}).on('focus', function() {
that.uiSelect.addClass('red-ui-typedInput-focus');
})
// explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline'
this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="fa fa-sort-desc"></i></span></button>').appendTo(this.uiSelect);
this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger);
this.optionSelectTrigger.click(function(event) {
event.preventDefault();
that._showOptionSelectMenu();
}).on('keydown', function(evt) {
if (evt.keyCode === 40) {
// Down
that._showOptionSelectMenu();
}
}).on('blur', function() {
that.uiSelect.removeClass('red-ui-typedInput-focus');
}).on('focus', function() {
that.uiSelect.addClass('red-ui-typedInput-focus');
});
this.type(this.options.default||this.typeList[0].value);
},
_showTypeMenu: function() {
if (this.typeList.length > 1) {
this._showMenu(this.menu,this.selectTrigger);
this.menu.find("[value='"+this.propertyType+"']").focus();
} else {
this.element.focus();
}
},
_showOptionSelectMenu: function() {
if (this.optionMenu) {
this.optionMenu.css({
minWidth:this.optionSelectLabel.width()
});
this._showMenu(this.optionMenu,this.optionSelectLabel);
var selectedOption = this.optionMenu.find("[value='"+this.value()+"']");
if (selectedOption.length === 0) {
selectedOption = this.optionMenu.children(":first");
}
selectedOption.focus();
}
},
_hideMenu: function(menu) {
$(document).off("mousedown.close-property-select");
menu.hide();
if (this.elementDiv.is(":visible")) {
this.element.focus();
} else if (this.optionSelectTrigger.is(":visible")){
this.optionSelectTrigger.focus();
} else {
this.selectTrigger.focus();
}
},
_createMenu: function(opts,callback) {
var that = this;
var menu = $("<div>").addClass("red-ui-typedInput-options");
opts.forEach(function(opt) {
if (typeof opt === 'string') {
opt = {value:opt,label:opt};
}
var op = $('<a href="#"></a>').attr("value",opt.value).appendTo(menu);
if (opt.label) {
op.text(opt.label);
}
if (opt.icon) {
$('<img>',{src:opt.icon,style:"margin-right: 4px; height: 18px;"}).prependTo(op);
} else {
op.css({paddingLeft: "18px"});
}
op.click(function(event) {
event.preventDefault();
callback(opt.value);
that._hideMenu(menu);
});
});
menu.css({
display: "none",
});
menu.appendTo(document.body);
menu.on('keydown', function(evt) {
if (evt.keyCode === 40) {
// DOWN
$(this).children(":focus").next().focus();
} else if (evt.keyCode === 38) {
// UP
$(this).children(":focus").prev().focus();
} else if (evt.keyCode === 27) {
that._hideMenu(menu);
}
})
return menu;
},
_showMenu: function(menu,relativeTo) {
if (this.disarmClick) {
this.disarmClick = false;
return
}
var that = this;
var pos = relativeTo.offset();
var height = relativeTo.height();
var menuHeight = menu.height();
var top = (height+pos.top-3);
if (top+menuHeight > $(window).height()) {
top -= (top+menuHeight)-$(window).height()+5;
}
menu.css({
top: top+"px",
left: (2+pos.left)+"px",
});
menu.slideDown(100);
this._delay(function() {
that.uiSelect.addClass('red-ui-typedInput-focus');
$(document).on("mousedown.close-property-select", function(event) {
if(!$(event.target).closest(menu).length) {
that._hideMenu(menu);
}
if ($(event.target).closest(relativeTo).length) {
that.disarmClick = true;
event.preventDefault();
}
})
});
},
_getLabelWidth: function(label) {
var labelWidth = label.outerWidth();
if (labelWidth === 0) {
var container = $('<div class="red-ui-typedInput-container"></div>').css({
position:"absolute",
top:0,
left:-1000
}).appendTo(document.body);
var newTrigger = label.clone().appendTo(container);
labelWidth = newTrigger.outerWidth();
container.remove();
}
return labelWidth;
},
_resize: function() {
if (this.uiWidth !== null) {
this.uiSelect.width(this.uiWidth);
}
if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) {
this.selectTrigger.css('width',"100%");
} else {
this.selectTrigger.width('auto');
var labelWidth = this._getLabelWidth(this.selectTrigger);
this.elementDiv.css('left',labelWidth+"px");
if (this.optionSelectTrigger) {
this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'});
}
}
},
_destroy: function() {
this.menu.remove();
},
types: function(types) {
var that = this;
var currentType = this.type();
this.typeMap = {};
this.typeList = types.map(function(opt) {
var result;
if (typeof opt === 'string') {
result = allOptions[opt];
} else {
result = opt;
}
that.typeMap[result.value] = result;
return result;
});
this.selectTrigger.toggleClass("disabled", this.typeList.length === 1);
if (this.menu) {
this.menu.remove();
}
this.menu = this._createMenu(this.typeList, function(v) { that.type(v) });
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
this.type(this.typeList[0].value);
}
},
width: function(desiredWidth) {
this.uiWidth = desiredWidth;
this._resize();
},
value: function(value) {
if (!arguments.length) {
return this.element.val();
} else {
if (this.typeMap[this.propertyType].options) {
if (this.typeMap[this.propertyType].options.indexOf(value) === -1) {
value = "";
}
this.optionSelectLabel.text(value);
}
this.element.val(value);
this.element.trigger('change',this.type(),value);
}
},
type: function(type) {
if (!arguments.length) {
return this.propertyType;
} else {
var that = this;
var opt = this.typeMap[type];
if (opt && this.propertyType !== type) {
this.propertyType = type;
this.typeField.val(type);
this.selectLabel.empty();
var image;
if (opt.icon) {
image = new Image();
image.name = opt.icon;
image.src = opt.icon;
$('<img>',{src:opt.icon,style:"margin-right: 4px;height: 18px;"}).prependTo(this.selectLabel);
} else {
this.selectLabel.text(opt.label);
}
if (opt.options) {
if (this.optionSelectTrigger) {
this.optionSelectTrigger.show();
this.elementDiv.hide();
this.optionMenu = this._createMenu(opt.options,function(v){
that.optionSelectLabel.text(v);
that.value(v);
});
var currentVal = this.element.val();
if (opt.options.indexOf(currentVal) !== -1) {
this.optionSelectLabel.text(currentVal);
} else {
this.value(opt.options[0]);
}
}
} else {
if (this.optionMenu) {
this.optionMenu.remove();
this.optionMenu = null;
}
if (this.optionSelectTrigger) {
this.optionSelectTrigger.hide();
}
if (opt.hasValue === false) {
this.oldValue = this.element.val();
this.element.val("");
this.elementDiv.hide();
} else {
if (this.oldValue !== undefined) {
this.element.val(this.oldValue);
delete this.oldValue;
}
this.elementDiv.show();
}
this.element.trigger('change',this.propertyType,this.value());
}
if (image) {
image.onload = function() { that._resize(); }
image.onerror = function() { that._resize(); }
} else {
this._resize();
}
}
}
},
validate: function() {
var result;
var value = this.value();
var type = this.type();
if (this.typeMap[type] && this.typeMap[type].validate) {
var val = this.typeMap[type].validate;
if (typeof val === 'function') {
result = val(value);
} else {
result = val.test(value);
}
} else {
result = true;
}
if (result) {
this.uiSelect.removeClass('input-error');
} else {
this.uiSelect.addClass('input-error');
}
return result;
},
show: function() {
this.uiSelect.show();
this._resize();
},
hide: function() {
this.uiSelect.hide();
}
});
})(jQuery);

669
editor/js/ui/deploy.js Normal file
View File

@@ -0,0 +1,669 @@
/**
* 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-icon").attr("src",deploymentTypes[type].img);
}
var currentDiff = null;
/**
* 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="#">'+
'<span class="deploy-button-content">'+
'<img id="btn-deploy-icon" src="red/images/deploy-full-o.png"> '+
'<span>'+RED._("deploy.deploy")+'</span>'+
'</span>'+
'<span class="deploy-button-spinner hide">'+
'<img src="red/images/spin.svg"/>'+
'</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="#">'+
'<span class="deploy-button-content">'+
(icon?'<img id="btn-deploy-icon" src="'+icon+'"> ':'')+
'<span>'+label+'</span>'+
'</span>'+
'<span class="deploy-button-spinner hide">'+
'<img src="red/images/spin.svg"/>'+
'</span>'+
'</a>'+
'</span></li>').prependTo(".header-toolbar");
}
$('#btn-deploy').click(function() { save(); });
$( "#node-dialog-confirm-deploy" ).dialog({
title: RED._('deploy.confirm.button.confirm'),
modal: true,
autoOpen: false,
width: 550,
height: "auto",
buttons: [
{
text: RED._("deploy.confirm.button.cancel"),
click: function() {
$( this ).dialog( "close" );
}
},
// {
// id: "node-dialog-confirm-deploy-review",
// text: RED._("deploy.confirm.button.review"),
// class: "primary",
// click: function() {
// showDiff();
// $( this ).dialog( "close" );
// }
// },
{
text: RED._("deploy.confirm.button.confirm"),
class: "primary",
click: function() {
var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked");
if (ignoreChecked) {
ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true;
}
save(true,$( "#node-dialog-confirm-deploy-type" ).val() === "conflict");
$( 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>');
},
open: function() {
if ($( "#node-dialog-confirm-deploy-type" ).val() === "conflict") {
// $("#node-dialog-confirm-deploy-review").show();
$("#node-dialog-confirm-deploy-hide").parent().hide();
} else {
// $("#node-dialog-confirm-deploy-review").hide();
$("#node-dialog-confirm-deploy-hide").parent().show();
}
}
});
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");
}
});
// $("#node-dialog-view-diff").dialog({
// title: RED._('deploy.confirm.button.review'),
// modal: true,
// autoOpen: false,
// buttons: [
// {
// text: RED._("deploy.confirm.button.cancel"),
// click: function() {
// $( this ).dialog( "close" );
// }
// },
// {
// text: RED._("deploy.confirm.button.merge"),
// class: "primary",
// click: function() {
// $( this ).dialog( "close" );
// }
// }
// ],
// open: function() {
// $(this).dialog({width:Math.min($(window).width(),900),height:Math.min($(window).height(),600)});
// }
// });
// $("#node-dialog-view-diff-diff").editableList({
// addButton: false,
// scrollOnAdd: false,
// addItem: function(container,i,object) {
// var tab = object.tab.n;
// var tabDiv = $('<div>',{class:"node-diff-tab collapsed"}).appendTo(container);
//
// var titleRow = $('<div>',{class:"node-diff-tab-title"}).appendTo(tabDiv);
// titleRow.click(function(evt) {
// evt.preventDefault();
// titleRow.parent().toggleClass('collapsed');
// })
// var chevron = $('<i class="fa fa-angle-down node-diff-chevron ">').appendTo(titleRow);
// var title = $('<span>').html(tab.label||tab.id).appendTo(titleRow);
//
// var stats = $('<span>',{class:"node-diff-tab-stats"}).appendTo(titleRow);
//
// var addedCount = 0;
// var deletedCount = 0;
// var changedCount = 0;
// var conflictedCount = 0;
//
// object.tab.nodes.forEach(function(node) {
// var realNode = RED.nodes.node(node.id);
// var hasChanges = false;
// if (currentDiff.added[node.id]) {
// addedCount++;
// hasChanges = true;
// }
// if (currentDiff.deleted[node.id]) {
// deletedCount++;
// hasChanges = true;
// }
// if (currentDiff.changed[node.id]) {
// changedCount++;
// hasChanges = true;
// }
// if (currentDiff.conflicted[node.id]) {
// conflictedCount++;
// hasChanges = true;
// }
//
// if (hasChanges) {
// var def = RED.nodes.getType(node.type)||{};
// var div = $("<div>",{class:"node-diff-node-entry collapsed"}).appendTo(tabDiv);
// var nodeTitleDiv = $("<div>",{class:"node-diff-node-entry-title"}).appendTo(div);
// nodeTitleDiv.click(function(evt) {
// evt.preventDefault();
// $(this).parent().toggleClass('collapsed');
// })
// var newNode = currentDiff.newConfig.all[node.id];
// var nodePropertiesDiv = $("<div>",{class:"node-diff-node-entry-properties"}).appendTo(div);
//
// var nodePropertiesTable = $("<table>").appendTo(nodePropertiesDiv);
//
// if (node.hasOwnProperty('x')) {
// if (newNode.x !== node.x || newNode.y !== node.y) {
// var currentPosition = node.x+", "+node.y
// var newPosition = newNode.x+", "+newNode.y;
// $("<tr><td>position</td><td>"+currentPosition+"</td><td>"+newPosition+"</td></tr>").appendTo(nodePropertiesTable);
// }
// }
// var properties = Object.keys(node).filter(function(p) { return p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))});
// if (def.defaults) {
// properties = properties.concat(Object.keys(def.defaults));
// }
// properties.forEach(function(d) {
// var localValue = JSON.stringify(node[d]);
// var remoteValue = JSON.stringify(newNode[d]);
// var originalValue = realNode._config[d];
//
// if (remoteValue !== originalValue) {
// var formattedProperty = formatNodeProperty(node[d]);
// var newFormattedProperty = formatNodeProperty(newNode[d]);
// if (localValue === originalValue) {
// // no conflict change
// } else {
// // conflicting change
// }
// $("<tr><td>"+d+'</td><td class="">'+formattedProperty+'</td><td class="node-diff-property-changed">'+newFormattedProperty+"</td></tr>").appendTo(nodePropertiesTable);
// }
//
// })
// var nodeChevron = $('<i class="fa fa-angle-down node-diff-chevron">').appendTo(nodeTitleDiv);
//
//
// // var leftColumn = $('<div>',{class:"node-diff-column"}).appendTo(div);
// // var rightColumn = $('<div>',{class:"node-diff-column"}).appendTo(div);
// // rightColumn.html("&nbsp");
//
//
//
// var nodeDiv = $("<div>",{class:"node-diff-node-entry-node"}).appendTo(nodeTitleDiv);
// var colour = def.color;
// var icon_url = "arrow-in.png";
// if (node.type === 'tab') {
// colour = "#C0DEED";
// icon_url = "subflow.png";
// } else if (def.category === 'config') {
// icon_url = "cog.png";
// } else if (node.type === 'unknown') {
// icon_url = "alert.png";
// } else {
// icon_url = def.icon;
// }
// nodeDiv.css('backgroundColor',colour);
//
// var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv);
// $('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer);
//
//
//
// var contentDiv = $('<div>',{class:"node-diff-node-description"}).appendTo(nodeTitleDiv);
//
// $('<span>',{class:"node-diff-node-label"}).html(node.label || node.name || node.id).appendTo(contentDiv);
// //$('<div>',{class:"red-ui-search-result-node-type"}).html(node.type).appendTo(contentDiv);
// //$('<div>',{class:"red-ui-search-result-node-id"}).html(node.id).appendTo(contentDiv);
// }
//
// });
//
// var statsInfo = '<span class="node-diff-count">'+object.tab.nodes.length+" nodes"+
// (addedCount+deletedCount+changedCount+conflictedCount > 0 ? " : ":"")+
// "</span> "+
// ((addedCount > 0)?'<span class="node-diff-added">'+addedCount+' added</span> ':'')+
// ((deletedCount > 0)?'<span class="node-diff-deleted">'+deletedCount+' deleted</span> ':'')+
// ((changedCount > 0)?'<span class="node-diff-changed">'+changedCount+' changed</span> ':'')+
// ((conflictedCount > 0)?'<span class="node-diff-conflicted">'+conflictedCount+' conflicts</span>':'');
// stats.html(statsInfo);
//
//
//
// //
// //
// //
// // var node = object.node;
// // var realNode = RED.nodes.node(node.id);
// // var def = RED.nodes.getType(object.node.type)||{};
// // var l = "";
// // if (def && def.label && realNode) {
// // l = def.label;
// // try {
// // l = (typeof l === "function" ? l.call(realNode) : l);
// // } catch(err) {
// // console.log("Definition error: "+node.type+".label",err);
// // }
// // }
// // l = l||node.label||node.name||node.id||"";
// // console.log(node);
// // var div = $('<div>').appendTo(container);
// // div.html(l);
// }
// });
}
function formatNodeProperty(prop) {
var formattedProperty = prop;
if (formattedProperty === null) {
formattedProperty = 'null';
} else if (formattedProperty === undefined) {
formattedProperty = 'undefined';
} else if (typeof formattedProperty === 'object') {
formattedProperty = JSON.stringify(formattedProperty);
}
if (/\n/.test(formattedProperty)) {
formattedProperty = "<pre>"+formattedProperty+"</pre>"
}
return formattedProperty;
}
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 = RED.utils.getNodeLabel(node,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 resolveConflict(currentNodes) {
$( "#node-dialog-confirm-deploy-config" ).hide();
$( "#node-dialog-confirm-deploy-unknown" ).hide();
$( "#node-dialog-confirm-deploy-unused" ).hide();
$( "#node-dialog-confirm-deploy-conflict" ).show();
$( "#node-dialog-confirm-deploy-type" ).val("conflict");
$( "#node-dialog-confirm-deploy" ).dialog( "open" );
// $("#node-dialog-confirm-deploy-review").append($('<img src="red/images/spin.svg" style="background: rgba(255,255,255,0.8); margin-top: -16px; margin-left: -8px; height:16px; position: absolute; "/>'));
// $("#node-dialog-confirm-deploy-review .ui-button-text").css("opacity",0.4);
// $("#node-dialog-confirm-deploy-review").attr("disabled",true).addClass("disabled");
// $.ajax({
// headers: {
// "Accept":"application/json",
// },
// cache: false,
// url: 'flows',
// success: function(nodes) {
// var newNodes = nodes.flows;
// var newRevision = nodes.rev;
// generateDiff(currentNodes,newNodes);
// $("#node-dialog-confirm-deploy-review").attr("disabled",false).removeClass("disabled");
// $("#node-dialog-confirm-deploy-review img").remove();
// $("#node-dialog-confirm-deploy-review .ui-button-text").css("opacity",1);
// }
// });
}
// function parseNodes(nodeList) {
// var tabOrder = [];
// var tabs = {};
// var subflows = {};
// var globals = [];
// var all = {};
//
// nodeList.forEach(function(node) {
// all[node.id] = node;
// if (node.type === 'tab') {
// tabOrder.push(node.id);
// tabs[node.id] = {n:node,nodes:[]};
// } else if (node.type === 'subflow') {
// subflows[node.id] = {n:node,nodes:[]};
// }
// });
//
// nodeList.forEach(function(node) {
// if (node.type !== 'tab' && node.type !== 'subflow') {
// if (tabs[node.z]) {
// tabs[node.z].nodes.push(node);
// } else if (subflows[node.z]) {
// subflows[node.z].nodes.push(node);
// } else {
// globals.push(node);
// }
// }
// });
//
// return {
// all: all,
// tabOrder: tabOrder,
// tabs: tabs,
// subflows: subflows,
// globals: globals
// }
// }
// function generateDiff(currentNodes,newNodes) {
// var currentConfig = parseNodes(currentNodes);
// var newConfig = parseNodes(newNodes);
// var pending = RED.nodes.pending();
// var added = {};
// var deleted = {};
// var changed = {};
// var conflicted = {};
//
//
// Object.keys(currentConfig.all).forEach(function(id) {
// var node = RED.nodes.workspace(id)||RED.nodes.subflow(id)||RED.nodes.node(id);
// if (!newConfig.all.hasOwnProperty(id)) {
// if (!pending.added.hasOwnProperty(id)) {
// deleted[id] = true;
// conflicted[id] = node.changed;
// }
// } else if (JSON.stringify(currentConfig.all[id]) !== JSON.stringify(newConfig.all[id])) {
// changed[id] = true;
// conflicted[id] = node.changed;
// }
// });
// Object.keys(newConfig.all).forEach(function(id) {
// if (!currentConfig.all.hasOwnProperty(id) && !pending.deleted.hasOwnProperty(id)) {
// added[id] = true;
// }
// });
//
// // console.log("Added",added);
// // console.log("Deleted",deleted);
// // console.log("Changed",changed);
// // console.log("Conflicted",conflicted);
//
// var formatString = function(id) {
// return conflicted[id]?"!":(added[id]?"+":(deleted[id]?"-":(changed[id]?"~":" ")));
// }
// newConfig.tabOrder.forEach(function(tabId) {
// var tab = newConfig.tabs[tabId];
// console.log(formatString(tabId),"Flow:",tab.n.label, "("+tab.n.id+")");
// tab.nodes.forEach(function(node) {
// console.log(" ",formatString(node.id),node.type,node.name || node.id);
// })
// if (currentConfig.tabs[tabId]) {
// currentConfig.tabs[tabId].nodes.forEach(function(node) {
// if (deleted[node.id]) {
// console.log(" ",formatString(node.id),node.type,node.name || node.id);
// }
// })
// }
// });
// currentConfig.tabOrder.forEach(function(tabId) {
// if (deleted[tabId]) {
// console.log(formatString(tabId),"Flow:",tab.n.label, "("+tab.n.id+")");
// }
// });
//
// currentDiff = {
// currentConfig: currentConfig,
// newConfig: newConfig,
// added: added,
// deleted: deleted,
// changed: changed,
// conflicted: conflicted
// }
// }
// function showDiff() {
// if (currentDiff) {
// var list = $("#node-dialog-view-diff-diff");
// list.editableList('empty');
// var currentConfig = currentDiff.currentConfig;
// currentConfig.tabOrder.forEach(function(tabId) {
// var tab = currentConfig.tabs[tabId];
// list.editableList('addItem',{tab:tab})
// });
// }
// $("#node-dialog-view-diff").dialog("open");
// }
function save(skipValidation,force) {
if (!$("#btn-deploy").hasClass("disabled")) {
if (!skipValidation) {
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 && (node._def.hasUsers !== false)) {
unusedConfigNodes.push(getNodeInfo(node));
hasUnusedConfig = true;
}
});
$( "#node-dialog-confirm-deploy-config" ).hide();
$( "#node-dialog-confirm-deploy-unknown" ).hide();
$( "#node-dialog-confirm-deploy-unused" ).hide();
$( "#node-dialog-confirm-deploy-conflict" ).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();
var startTime = Date.now();
$(".deploy-button-content").css('opacity',0);
$(".deploy-button-spinner").show();
$("#btn-deploy").addClass("disabled");
var data = {flows:nns};
if (!force) {
data.rev = RED.nodes.version();
}
$.ajax({
url:"flows",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
headers: {
"Node-RED-Deployment-Type":deploymentType
}
}).done(function(data,textStatus,xhr) {
RED.nodes.dirty(false);
RED.nodes.version(data.rev);
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) {
confNode.changed = false;
if (confNode.credentials) {
delete confNode.credentials;
}
});
RED.nodes.eachWorkspace(function(ws) {
ws.changed = false;
})
// 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);
$("#btn-deploy").removeClass("disabled");
if (xhr.status === 401) {
RED.notify(RED._("deploy.deployFailed",{message:RED._("user.notAuthorized")}),"error");
} else if (xhr.status === 409) {
resolveConflict(nns);
} else if (xhr.responseText) {
RED.notify(RED._("deploy.deployFailed",{message:xhr.responseText}),"error");
} else {
RED.notify(RED._("deploy.deployFailed",{message:RED._("deploy.errors.noResponse")}),"error");
}
}).always(function() {
var delta = Math.max(0,300-(Date.now()-startTime));
setTimeout(function() {
$(".deploy-button-content").css('opacity',1);
$(".deploy-button-spinner").hide();
},delta);
});
}
}
return {
init: init
}
})();

1435
editor/js/ui/editor.js Normal file

File diff suppressed because it is too large Load Diff

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