Compare commits
	
		
			4 Commits
		
	
	
		
			4396-fix-g
			...
			bidi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 841438793d | ||
|  | e0ac565bab | ||
|  | e5511ea86d | ||
|  | b67df2c0ab | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1 +0,0 @@ | |||||||
| /packages/node_modules/** linguist-generated=false |  | ||||||
							
								
								
									
										61
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,61 +0,0 @@ | |||||||
| name: 🐞 Report a bug |  | ||||||
| description: File a bug/issue on the core of Node-RED |  | ||||||
| labels: [needs-triage] |  | ||||||
| body: |  | ||||||
| - type: markdown |  | ||||||
|   attributes: |  | ||||||
|     value: | |  | ||||||
|         This issue tracker is for problems with the Node-RED runtime, the editor or the core nodes. |  | ||||||
|  |  | ||||||
|         If your issue is: |  | ||||||
|           - a general 'how-to' type question, |  | ||||||
|           - a feature request or suggestion for a change, |  | ||||||
|           - or problems with 3rd party (`node-red-contrib-`) nodes |  | ||||||
|  |  | ||||||
|         please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack). |  | ||||||
|  |  | ||||||
|         You could also consider asking a question on [Stack Overflow](https://stackoverflow.com/questions/tagged/node-red) and tag it `node-red`. |  | ||||||
|  |  | ||||||
|         That way the whole Node-RED user community can help, rather than rely on the core development team. |  | ||||||
|  |  | ||||||
|         To help us understand the issue, please fill-in as much of the following information as you can: |  | ||||||
| - type: textarea |  | ||||||
|   attributes: |  | ||||||
|     label: Current Behavior |  | ||||||
|     description: A clear & concise description of what you're experiencing. |  | ||||||
|   validations: |  | ||||||
|     required: false |  | ||||||
| - type: textarea |  | ||||||
|   attributes: |  | ||||||
|     label: Expected Behavior |  | ||||||
|     description: A clear & concise description of what you expected to happen. |  | ||||||
|   validations: |  | ||||||
|     required: false |  | ||||||
| - type: textarea |  | ||||||
|   attributes: |  | ||||||
|     label: Steps To Reproduce |  | ||||||
|     description: Steps to reproduce the behavior. |  | ||||||
|   validations: |  | ||||||
|     required: false |  | ||||||
| - type: textarea |  | ||||||
|   attributes: |  | ||||||
|     label: Example flow |  | ||||||
|     description: If you have a minimal example flow that demonstrates the issue, share it here. |  | ||||||
|     value: | |  | ||||||
|       ``` |  | ||||||
|       paste your flow here |  | ||||||
|       ``` |  | ||||||
|   validations: |  | ||||||
|     required: false |  | ||||||
| - type: textarea |  | ||||||
|   attributes: |  | ||||||
|     label: Environment |  | ||||||
|     description: Please tell us about your environment. Include any relevant information on how you are running Node-RED. |  | ||||||
|     value: | |  | ||||||
|         - Node-RED version: |  | ||||||
|         - Node.js version: |  | ||||||
|         - npm version: |  | ||||||
|         - Platform/OS: |  | ||||||
|         - Browser: |  | ||||||
|   validations: |  | ||||||
|     required: false |  | ||||||
							
								
								
									
										14
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,14 +0,0 @@ | |||||||
| blank_issues_enabled: true |  | ||||||
| contact_links: |  | ||||||
|   - name: ❓ Questions |  | ||||||
|     url: https://discourse.nodered.org |  | ||||||
|     about: Ask your question on the Node-RED forum |  | ||||||
|   - name: ⭐️ Feature Request |  | ||||||
|     url: https://discourse.nodered.org/c/development/feature-requests |  | ||||||
|     about: Discuss your request with the community |  | ||||||
|   - name: 🗂 Documentation |  | ||||||
|     url: https://nodered.org/docs |  | ||||||
|     about: Go straight to the documentation |  | ||||||
|   - name: 💬 Slack |  | ||||||
|     url: https://nodered.org/slack |  | ||||||
|     about: Chat about the project on our slack team |  | ||||||
							
								
								
									
										34
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,34 +0,0 @@ | |||||||
| <!-- |  | ||||||
| ## Before you hit that Submit button.... |  | ||||||
|  |  | ||||||
| Please read our [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) |  | ||||||
| before submitting a pull-request. |  | ||||||
|  |  | ||||||
| ## Types of changes |  | ||||||
|  |  | ||||||
| What types of changes does your code introduce? |  | ||||||
| Put an `x` in the boxes that apply |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| - [ ] Bugfix (non-breaking change which fixes an issue) |  | ||||||
| - [ ] New feature (non-breaking change which adds functionality) |  | ||||||
|  |  | ||||||
| <!-- |  | ||||||
| If you want to raise a pull-request with a new feature, or a refactoring |  | ||||||
| of existing code, it **may well get rejected** if it hasn't been discussed on |  | ||||||
| the [forum](https://discourse.nodered.org) or |  | ||||||
| [slack team](https://nodered.org/slack) first. |  | ||||||
|  |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| ## Proposed changes |  | ||||||
|  |  | ||||||
| <!-- Describe the nature of this change. What problem does it address? --> |  | ||||||
|  |  | ||||||
| ## Checklist |  | ||||||
| <!-- Put an `x` in the boxes that apply --> |  | ||||||
|  |  | ||||||
| - [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) |  | ||||||
| - [ ] For non-bugfix PRs, I have discussed this change on the forum/slack team. |  | ||||||
| - [ ] I have run `npm run test` to verify the unit tests pass |  | ||||||
| - [ ] I have added suitable unit tests to cover the new/changed functionality |  | ||||||
							
								
								
									
										15
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,15 +0,0 @@ | |||||||
| # To get started with Dependabot version updates, you'll need to specify which |  | ||||||
| # package ecosystems to update and where the package manifests are located. |  | ||||||
| # Please see the documentation for all configuration options: |  | ||||||
| # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates |  | ||||||
|  |  | ||||||
| version: 2 |  | ||||||
| updates: |  | ||||||
|   - package-ecosystem: "github-actions" # See documentation for possible values |  | ||||||
|     directory: "/" # Location of package manifests |  | ||||||
|     schedule: |  | ||||||
|       interval: "monthly" |  | ||||||
|     groups: |  | ||||||
|       github-actions: |  | ||||||
|         patterns: |  | ||||||
|           - "*" |  | ||||||
							
								
								
									
										29
									
								
								.github/scripts/update-node-red-docker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,29 +0,0 @@ | |||||||
| const fs = require("fs"); |  | ||||||
|  |  | ||||||
| const newVersion = require("../../package.json").version; |  | ||||||
|  |  | ||||||
| if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) { |  | ||||||
|     console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`); |  | ||||||
|     process.exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if (!/^\d+\.\d+\.\d+$/.test(newVersion)) { |  | ||||||
|     console.log(`Not updating for a non-stable release - ${newVersion}`); |  | ||||||
|     process.exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const currentVersion = require("../../../node-red-docker/package.json").version; |  | ||||||
|  |  | ||||||
| console.log(`Update from ${currentVersion} to ${newVersion}`) |  | ||||||
|  |  | ||||||
| updateFile(__dirname+"/../../../node-red-docker/package.json", currentVersion, newVersion); |  | ||||||
| updateFile(__dirname+"/../../../node-red-docker/docker-custom/package.json", currentVersion, newVersion); |  | ||||||
| updateFile(__dirname+"/../../../node-red-docker/README.md", currentVersion, newVersion); |  | ||||||
|  |  | ||||||
| console.log(`::set-env name=newVersion::${newVersion}`); |  | ||||||
|  |  | ||||||
| function updateFile(path,from,to) { |  | ||||||
|     let contents = fs.readFileSync(path,"utf8"); |  | ||||||
|     contents = contents.replace(new RegExp(from.replace(/\./g,"\\."),"g"), to); |  | ||||||
|     fs.writeFileSync(path, contents); |  | ||||||
| } |  | ||||||
							
								
								
									
										18
									
								
								.github/scripts/update-node-red-website.js
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,18 +0,0 @@ | |||||||
| const fs = require("fs"); |  | ||||||
|  |  | ||||||
| const newVersion = require("../../package.json").version; |  | ||||||
|  |  | ||||||
| if (process.env.GITHUB_REF !== "refs/tags/"+newVersion) { |  | ||||||
|     console.log(`GITHUB_REF doesn't match the package.json version: ${process.env.GITHUB_REF} !== ${newVersion}`); |  | ||||||
|     process.exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| if (!/^\d+\.\d+\.\d+$/.test(newVersion)) { |  | ||||||
|     console.log(`Not updating for a non-stable release - ${newVersion}`); |  | ||||||
|     process.exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const path = __dirname+"/../../../node-red.github.io/index.html"; |  | ||||||
| let contents = fs.readFileSync(path, "utf8"); |  | ||||||
| contents = contents.replace(/<span class="node-red-latest-version">v\d+\.\d+\.\d+<\/span>/, `<span class="node-red-latest-version">v${newVersion}<\/span>` ); |  | ||||||
| fs.writeFileSync(path, contents); |  | ||||||
							
								
								
									
										62
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,62 +0,0 @@ | |||||||
| name: Publish Release |  | ||||||
| env: |  | ||||||
|   ACTIONS_ALLOW_UNSECURE_COMMANDS: true |  | ||||||
| on: |  | ||||||
|   release: |  | ||||||
|     types: [published] |  | ||||||
|  |  | ||||||
| permissions: |  | ||||||
|   contents: read |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   generate: |  | ||||||
|     name: 'Update node-red-docker image' |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Check out node-red repository |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|         with: |  | ||||||
|             path: 'node-red' |  | ||||||
|       - name: Check out node-red-docker repository |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|         with: |  | ||||||
|             repository: 'node-red/node-red-docker' |  | ||||||
|             path: 'node-red-docker' |  | ||||||
|       - name: Check out node-red.github.io repository |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|         with: |  | ||||||
|             repository: 'node-red/node-red.github.io' |  | ||||||
|             path: 'node-red.github.io' |  | ||||||
|       - uses: actions/setup-node@v3 |  | ||||||
|         with: |  | ||||||
|             node-version: '16' |  | ||||||
|       - run: node ./node-red/.github/scripts/update-node-red-docker.js |  | ||||||
|       - name: Create Docker Pull Request |  | ||||||
|         uses: peter-evans/create-pull-request@v2 |  | ||||||
|         with: |  | ||||||
|           token: ${{ secrets.NR_REPO_TOKEN }} |  | ||||||
|           committer: GitHub <noreply@github.com> |  | ||||||
|           author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> |  | ||||||
|           path: 'node-red-docker' |  | ||||||
|           commit-message: 'Bump to ${{ env.newVersion }}' |  | ||||||
|           title: '🚀 Update to Node-RED ${{ env.newVersion }} release' |  | ||||||
|           body: | |  | ||||||
|             Updates the Node-RED Docker repo for the ${{ env.newVersion }} release. |  | ||||||
|  |  | ||||||
|             Once this is merged, you will need to create a new release with the tag `v${{ env.newVersion }}`. |  | ||||||
|  |  | ||||||
|             This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary |  | ||||||
|       - run: node ./node-red/.github/scripts/update-node-red-website.js |  | ||||||
|       - name: Create Website Pull Request |  | ||||||
|         uses: peter-evans/create-pull-request@v2 |  | ||||||
|         with: |  | ||||||
|           token: ${{ secrets.NR_REPO_TOKEN }} |  | ||||||
|           committer: GitHub <noreply@github.com> |  | ||||||
|           author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> |  | ||||||
|           path: 'node-red.github.io' |  | ||||||
|           commit-message: 'Bump to ${{ env.newVersion }}' |  | ||||||
|           title: '🚀 Update to Node-RED ${{ env.newVersion }} release' |  | ||||||
|           body: | |  | ||||||
|             Updates the Node-RED Website repo for the ${{ env.newVersion }} release. |  | ||||||
|  |  | ||||||
|             This PR was auto-generated by a GitHub Action. Any questions, speak to @knolleary |  | ||||||
							
								
								
									
										36
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,36 +0,0 @@ | |||||||
| name: Run tests |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: [ master, dev ] |  | ||||||
|   pull_request: |  | ||||||
|     branches: [ master, dev ] |  | ||||||
|  |  | ||||||
| permissions: |  | ||||||
|   contents: read |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   build: |  | ||||||
|     permissions: |  | ||||||
|       checks: write  # for coverallsapp/github-action to create new checks |  | ||||||
|       contents: read  # for actions/checkout to fetch code |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     strategy: |  | ||||||
|       matrix: |  | ||||||
|         node-version: [16, 18, 20] |  | ||||||
|     steps: |  | ||||||
|     - uses: actions/checkout@v4 |  | ||||||
|     - name: Use Node.js ${{ matrix.node-version }} |  | ||||||
|       uses: actions/setup-node@v3 |  | ||||||
|       with: |  | ||||||
|         node-version: ${{ matrix.node-version }} |  | ||||||
|     - name: Install Dependencies |  | ||||||
|       run: npm install |  | ||||||
|     - name: Run tests |  | ||||||
|       run: | |  | ||||||
|         npm run test |  | ||||||
|     # - name: Publish to coveralls.io |  | ||||||
|     #   if: ${{ matrix.node-version == 16 }} |  | ||||||
|     #   uses: coverallsapp/github-action@v1.1.2 |  | ||||||
|     #   with: |  | ||||||
|     #     github-token: ${{ github.token }} |  | ||||||
							
								
								
									
										11
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -7,9 +7,7 @@ | |||||||
| .sessions.json | .sessions.json | ||||||
| .settings | .settings | ||||||
| .tern-project | .tern-project | ||||||
| .i18n-editor-metadata |  | ||||||
| *.backup | *.backup | ||||||
| *.bak |  | ||||||
| *_cred* | *_cred* | ||||||
| coverage | coverage | ||||||
| credentials.json | credentials.json | ||||||
| @@ -19,12 +17,3 @@ node_modules | |||||||
| public | public | ||||||
| locales/zz-ZZ | locales/zz-ZZ | ||||||
| nodes/core/locales/zz-ZZ | nodes/core/locales/zz-ZZ | ||||||
| !packages/node_modules |  | ||||||
| packages/node_modules/@node-red/editor-client/public |  | ||||||
| !test/**/node_modules |  | ||||||
| docs |  | ||||||
| !packages/node_modules/**/docs |  | ||||||
| .vscode |  | ||||||
| .nyc_output |  | ||||||
| sync.ffs_db |  | ||||||
| package-lock.json |  | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ | |||||||
|     "asi": true,        // allow missing semicolons |     "asi": true,        // allow missing semicolons | ||||||
|     "curly": true,      // require braces |     "curly": true,      // require braces | ||||||
|     "eqnull": true,     // ignore ==null |     "eqnull": true,     // ignore ==null | ||||||
|     //"eqeqeq": true,   // enforce === |  | ||||||
|     "freeze": true,     // don't allow override |  | ||||||
|     "indent": 4,        // default indent of 4 |  | ||||||
|     "forin": true,      // require property filtering in "for in" loops |     "forin": true,      // require property filtering in "for in" loops | ||||||
|     "immed": true,      // require immediate functions to be wrapped in ( ) |     "immed": true,      // require immediate functions to be wrapped in ( ) | ||||||
|     "nonbsp": true,     // warn on unexpected whitespace breaking chars |     "nonbsp": true,     // warn on unexpected whitespace breaking chars | ||||||
| @@ -12,8 +9,6 @@ | |||||||
|     //"unused": true,   // Check for unused functions and variables |     //"unused": true,   // Check for unused functions and variables | ||||||
|     "loopfunc": true,   // allow functions to be defined in loops |     "loopfunc": true,   // allow functions to be defined in loops | ||||||
|     //"expr": true,     // allow ternery operator syntax... |     //"expr": true,     // allow ternery operator syntax... | ||||||
|     "shadow": true,     // allow variable shadowing (re-use of names...) |  | ||||||
|     "sub": true,        // don't warn that foo['bar'] should be written as foo.bar |     "sub": true,        // don't warn that foo['bar'] should be written as foo.bar | ||||||
|     "proto": true,      // allow setting of __proto__ in node < v0.12, |     "proto": true       // allow setting of __proto__ in node < v0.12 | ||||||
|     "esversion": 11      // allow es11(ES2020) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| /Gruntfile.js | /Gruntfile.js | ||||||
| /.git/* | /.git/* | ||||||
|  | /lib/* | ||||||
| *.backup | *.backup | ||||||
| /public/* | /public/* | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | |||||||
|  | .settings | ||||||
|  | .jshintignore | ||||||
|  | .jshintrc | ||||||
|  | .project | ||||||
|  | .tern-project | ||||||
|  | .travis.yml | ||||||
|  | .git | ||||||
							
								
								
									
										24
									
								
								.travis.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | |||||||
|  | sudo: false | ||||||
|  | language: node_js | ||||||
|  | env: | ||||||
|  |   - CXX="g++-4.8" | ||||||
|  | addons: | ||||||
|  |   apt: | ||||||
|  |     sources: | ||||||
|  |     - ubuntu-toolchain-r-test | ||||||
|  |     packages: | ||||||
|  |     - g++-4.8 | ||||||
|  |     - gcc-4.8 | ||||||
|  | matrix: | ||||||
|  |   allow_failures: | ||||||
|  |   - node_js: "7" | ||||||
|  | node_js: | ||||||
|  |   - "7" | ||||||
|  |   - "6" | ||||||
|  |   - "4" | ||||||
|  |   - "0.10" | ||||||
|  | script: | ||||||
|  |   - istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage | ||||||
|  | before_script: | ||||||
|  |   - npm install -g istanbul | ||||||
|  |   - npm install coveralls | ||||||
							
								
								
									
										19
									
								
								API.md
									
									
									
									
									
								
							
							
						
						| @@ -1,19 +0,0 @@ | |||||||
| Node-RED consists of 6 node modules under the `@node-red` scope, which are pulled together |  | ||||||
| by the top-level `node-red` module. The typical scenario is where you are embedding Node-RED into your |  | ||||||
| own application, in which case you would use the `node-red` module rather than any of the |  | ||||||
| internal modules directly. |  | ||||||
|  |  | ||||||
| ```javascript |  | ||||||
| let RED = require("node-red"); |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Module | Description |  | ||||||
| -------|------- |  | ||||||
| [node-red](node-red.html) | the main module that pulls together all of the internal modules and provides the executable version of Node-RED |  | ||||||
| [@node-red/editor-api](@node-red_editor-api.html) | an Express application that serves the Node-RED editor and provides the Admin HTTP API |  | ||||||
| [@node-red/runtime](@node-red_runtime.html) | the core runtime of Node-RED |  | ||||||
| [@node-red/util](@node-red_util.html) | common utilities for the Node-RED runtime and editor modules |  | ||||||
| [@node-red/registry](@node-red_registry.html) | the internal node registry |  | ||||||
| @node-red/nodes | the default set of core nodes. This module only contains the Node-RED nodes - it does not expose any APIs. |  | ||||||
| @node-red/editor-client | the client-side resources of the Node-RED editor application |  | ||||||
							
								
								
									
										971
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						| @@ -1,501 +1,498 @@ | |||||||
| #### 3.1.0: Milestone Release | #### 0.15.1: Maintenance Release | ||||||
|  |  | ||||||
| Editor |  - Update default palette catalogue to use https | ||||||
|  |  - Disable palette editor if npm not found - and fix for Windows | ||||||
|  |  - Searching package catalogue should be case-insensitive Fixes #1010 | ||||||
|  |  - contenteditable fields not handled in config nodes Fixes #1011 | ||||||
|  |  - Change html link refs from `_new` to `_blank` to be standards compliant | ||||||
|  |  | ||||||
|  - Default filter to All Catalogues and show nodes for small lists (#4318) @knolleary | #### 0.15.0: Milestone Release | ||||||
|  - Better distinguish between ctrl and meta keys on mac (#4310) @knolleary |  | ||||||
|  - Ensure junction appears when filtering quick-add list (#4297) @knolleary |  | ||||||
|  - Update message catalogs for JSONata Expression editor (#4287) @kazuhitoyokoi |  | ||||||
|  - Add tooltip to relevance sort button in user settings UI (#4288) @kazuhitoyokoi |  | ||||||
|  - Capture workspace dirty state when quick-adding junction (#4283) @knolleary |  | ||||||
|  - Add docs for $clone function (#4284) @knolleary |  | ||||||
|  |  | ||||||
| Runtime | Runtime | ||||||
|  |  | ||||||
|  - Dependency updates (#4317) @knolleary |  - Increase default apiMaxLength to 5mb and add to default settings Closes #1001 | ||||||
|  - Ensure storage/util.writeFile handles concurrent write attempts (#4316) @knolleary |  - Add v2 /flows api and deploy-overwrite protection | ||||||
|  - Migrate http -> https for nodered.org (#4313) @Rotzbua |  - Encrypt credentials by default | ||||||
|  - Add Node 20 to GH Action test matrix (#4305) @Rotzbua |  - Ensure errors thrown by RED.events handlers don't percolate up | ||||||
|  - Handle group-scoped nodes inside subflow (#4301) @knolleary |  | ||||||
|  - Handle non-url-safe chars in context api (#4298) @knolleary |  | ||||||
|  - Fix git pull operation in project feature (#4290) @kazuhitoyokoi |  | ||||||
|  - Change linefeed codes in Korean message catalogs (#4286) @kazuhitoyokoi |  | ||||||
|  - Fix file permissions of message catalogs (#4285) @kazuhitoyokoi |  | ||||||
|  - Update tour (#4278) @knolleary |  | ||||||
|   |  | ||||||
| Nodes |  | ||||||
|  |  | ||||||
|  - File: Fix handling in file nodes when number is specified as file name (#4267) @kazuhitoyokoi |  | ||||||
|  - Function: Adding function timeout to settings file (#4265) (#4309) @knolleary |  | ||||||
|  - Function: Fix function setup tab layout (#4299) @knolleary |  | ||||||
|  - HTTP Request: Handle 204 in httprequest JSON (#4262) @sammachin |  | ||||||
|  - JSON: Fix test cases of JSON node (#4275) @kazuhitoyokoi |  | ||||||
|  - MQTT: Remove unnecessary check for clientid if autoUnsub set (#4302) @knolleary |  | ||||||
|  |  | ||||||
| ##### 3.1.0-beta.4: Beta Release |  | ||||||
|  |  | ||||||
| Editor | Editor | ||||||
|  |  | ||||||
|  - Add Japanese translation for 3.1.0 (#4252) @kazuhitoyokoi |  - Mark nodes as changed when they are moved | ||||||
|  - Improve Catalogue visibility (#4248) @Steve-Mcl |  - Added parent containment option for draggable. (#1006) | ||||||
|  - Add support for wiring and moving junctions on touch device (#4244) @Steve-Mcl |  - Ignore bidi event handling on non-existent and non-Input elements Closes #999 | ||||||
|  - Show errors and statuses of config nodes in the sidebar when no catch node is available (#4231) @bvmensvoort |  - Remove list of flows from menu | ||||||
|  - Improve wiring for horizontally aligned nodes (#4232) @knolleary |  - Allow nodes to be imported with their credentials | ||||||
|  - French translation of Welcome Tours (#4200) @GogoVega |  - Add workspace search option | ||||||
|  - French translation of v3.1.0-beta.3 changes (#4199) @GogoVega |  - Add scrollOnAdd option to editableList | ||||||
|  - add Japanese message for 3.1.0 beta 3 (#4209) @HiroyasuNishiyama |  - Add swift markup to editor for open whisk node | ||||||
|  - Dont clone the group nodes `node` array when saving edits (#4208) @Steve-Mcl |  - Scrollable tabs 👍 | ||||||
|  |  - Allow linking to individual flow via url hash | ||||||
|  |  - Avoid duplicating existing subflows on import | ||||||
|  |  - Add import-to-new-tab option | ||||||
|  |  - Add new options to export-nodes dialog | ||||||
|  |  - Stop nodes being added beyond the outer bounds of the workspace | ||||||
|  |  - Default config nodes to global scope unless in a subflow Closes #972 | ||||||
|  |  - Bidi support for Text Direction and Structured Text (#961) | ||||||
|  |  - Fix jQuery selector, selecting more than one help pane/popover and displaying incorrectly. (#970) | ||||||
|  |  - Fixes removeItem not passing row data to callback. (#965) | ||||||
|  |  - Move common components and add searchBox | ||||||
|  |  - Add initial palette sidebar | ||||||
|  |  | ||||||
|  | Nodes | ||||||
|  |  | ||||||
|  |  - Inject node label - show topic for timestamp mode if short | ||||||
|  |  - Let change node set type if total match | ||||||
|  |  - Clean up status on close for several core nodes. | ||||||
|  |  - Change node: re-parse JSON set value each time to avoid pass-by-ref | ||||||
|  |  - Better handle HTTP Request header capitalisation | ||||||
|  |  - Enable ES6 parsing in Function editor by default Fixes #985 | ||||||
|  |  - Update debug sidebar to use RED.view.reveal to show debug nodes | ||||||
|  |  - Add full path tip to file node, And tidy up Pi node tips | ||||||
|  |  - Remove WebSocket node maxlistener warning | ||||||
|  |  - Update mqtt-broker node to use fully name-space qualified status messages | ||||||
|  |  - Let UDP node better share same port instance if required | ||||||
|  |  - Add number of units to the delay node (rate) (#994) | ||||||
|  |  - Allow http middleware to skip rawBodyParser | ||||||
|  |  - Let change node move property to sub-property. | ||||||
|  |  - Add info to exec warning about buffered output if using python | ||||||
|  |  - TCP node: pass on latest input msg properties | ||||||
|  |  - Make sure MQTT broker is really set | ||||||
|  |  - Fix escape character catch in TCPGet + support 0x?? sequences | ||||||
|  |  - Fix split character in TCP Request node | ||||||
|  |  - Add CSS highlighting to the template node (#950) | ||||||
|  |  - Only update switch previous value after all rules are run | ||||||
|  |  | ||||||
|  | Other | ||||||
|  |  | ||||||
|  |  - Add npm build/test scripts Closes #946 #660 | ||||||
|  |  - Move travis to node 6 and 7 - drop 5 and 0.12 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #### 0.14.6: Maintenance Release | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Tell ace about Function node globals. Closes #927 | ||||||
|  |  - Tidy up mqtt nodes - linting and done handling. Closes #935 | ||||||
|  |  - Fix invalid html in TCP and HTML node edit templates | ||||||
|  |  - Add proper help text to link nodes | ||||||
|  |  - Handle importing old mqtt-broker configs that lack properties | ||||||
|  |  - Update ace to 1.2.4 | ||||||
|  |  - Allow config nodes to provide a sort function for their select list | ||||||
|  |  - Add log warning if node module required version cannot be satisfied | ||||||
|  |  - Handle empty credentials file. Closes #937 | ||||||
|  |  - Add RPi.GPIO lib test for ArchLinux | ||||||
|  |  | ||||||
|  | #### 0.14.5: Maintenance Release | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Cannot clear cookies with http nodes | ||||||
|  |  - let HTML parse node allow msg.select set select | ||||||
|  |  - Validate nodes on import after any references have been remapped | ||||||
|  |  - Debug node handles objects without constructor property Fixes #933 | ||||||
|  |  - Ensure 'false' property values are displayed in info panel Fixes #940 | ||||||
|  |  - Fix node enable/disable over restart - load configs after settings init | ||||||
|  |  | ||||||
|  | #### 0.14.4: Maintenance Release | ||||||
|  |  | ||||||
|  | Nodes | ||||||
|  |  | ||||||
|  |  - Update trigger node ui to use typedInputs | ||||||
|  |  - Better handling of quotes in CSV node | ||||||
|  |  - Clarify the MQTT node sends msg.payload - closes #929 | ||||||
|  |  - Inject node should reuse the message it is triggered with Closes #914 | ||||||
|  |  - Stop trigger node re-using old message | ||||||
|  |  - Allow node.status text to be 'falsey' values | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Handle DOMException when embedded in an iframe of different origin Fixes #932 | ||||||
|  |  - Fix double firing of menu actions | ||||||
|  |  - Fix select box handling in Safari - fixes #928 | ||||||
|  |  - Clear context in node test helper Fixes #858 | ||||||
|  |  - Allow node properties to be same as existing object functions Fixes #880 | ||||||
|  |  - Handle comms link closing whilst completing the initial connect | ||||||
|  |  - Protect against node type names that clash with Object property names Fixes #917 | ||||||
|  |  - Clone default node properties to avoid reference leakage | ||||||
|  |  - Strip tab node definition when exporting | ||||||
|  |  - Check for null config properties in editor before over-writing them | ||||||
|  |  - Add hasUsers flag to config nodes | ||||||
|  |  | ||||||
|  | Editor | ||||||
|  |  | ||||||
|  |  - Add sql mode to ace editor | ||||||
|  |  - Keyboard shortcuts dialog update (#923) | ||||||
|  |  - Ensure importing link nodes to a subflow doesn't add outbound links Fixes #921 | ||||||
|  |  - Add updateConfigNodeUsers function to editor | ||||||
|  |  - Scroll to bottom when item added to editableList | ||||||
|  |  - Form input widths behave more consistently when resizing Fixes #919 #920 | ||||||
|  |  | ||||||
|  | #### 0.14.3: Maintenance Release | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Create default setting.js in user-specified directory. Fixes #908 | ||||||
|  |  - MQTT In subscription qos not defaulting properly | ||||||
|  |  - Let exec node handle 0 as well as "0" | ||||||
|  |  | ||||||
|  | #### 0.14.2: Maintenance Release | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Cannot add new twitter credentials. Fixes #913 | ||||||
|  |  - Support array references in Debug property field | ||||||
|  |  | ||||||
|  | #### 0.14.1: Maintenance Release | ||||||
|  |  | ||||||
|  | Fixes | ||||||
|  |  | ||||||
|  |  - Handle undefined property that led to missing wires in the editor | ||||||
|  |  - Remove duplicate 'Delete' entry in keyboard shortcut window. Closes #911 | ||||||
|  |  - Add 'exec' to node-red-pi launch script. Closes #910 | ||||||
|  |  | ||||||
|  | #### 0.14.0: Milestone Release | ||||||
|  |  | ||||||
|  | Editor | ||||||
|  |  | ||||||
|  |  - Replace edit dialog with edit tray | ||||||
|  |  - Enable shift-drag detach of just the selected link | ||||||
|  |  - Allow workspace tabs to be re-ordered | ||||||
|  |  - Scope keyboard shortcuts to dom elements | ||||||
|  |  - Ensure parent nodes marked as changed due to child config node changes | ||||||
|  |  - Validate all edit dialog inputs when one changes | ||||||
|  |  - Add editableList widget and update Switch/Change nodes to use it | ||||||
|  |  - Add option to filter Debug sidebar by flow and highlight subflow-emitting nodes | ||||||
|  |  - Back off comms reconnect attempts after prolonged failures | ||||||
|  |  - Prompt for login if comms reconnect fails authentication | ||||||
|  |  - Change style of nodes in subflow template view | ||||||
|  |  - Add CHANGELOG.md and make it accessible from menu | ||||||
|  |  | ||||||
| Runtime | Runtime | ||||||
|  |  | ||||||
|  - Add NR_SUBFLOW_NAME/ID/PATH env vars (#4250) @knolleary |  - Always log node warnings on start without requiring -v | ||||||
|  - Evaluate all env vars as part of async flow start (#4230) @knolleary |  - Add support for loading scoped node modules. Closes #885 | ||||||
|  - Add support for httpStatic middleware (#4229) @knolleary |  - Add process.env.PORT to settings.js | ||||||
|  |  - Clear node context on deploy. Closes #870 | ||||||
|  |  - Enable finer grained permissions in adminAuth | ||||||
|  |  | ||||||
| Nodes | Nodes | ||||||
|  |  | ||||||
|  - Fix JSONata in file nodes (#4246) @kazuhitoyokoi |  - Enable config nodes to reference other config nodes | ||||||
|  - Fix timeout icon in function and link call nodes (#4253) @kazuhitoyokoi |  - Add Split/Join nodes | ||||||
|  - Fix connection keep-alive in http request node (#4228) @knolleary |  - Add Link nodes | ||||||
|  - adding timeout attribute to function node (#4177) @k1ln |  - Add support to HTTP In node for PATCH requests. Closes #904 | ||||||
|  - Fix manual mode join when multiple sequences being handled (#4143) @BitCaesar |  - Add cookie handling to HTTP In and HTTP Response nodes | ||||||
|  - Fix delay node flush issue (#4203) @dceejay |  - Add repeat indicator to inject node label. Closes #887 | ||||||
|  - Update status and catch node labels in group mode (#4207) @Steve-Mcl |  - Add javascript highlighter to template node | ||||||
|  |  - Add optional timeout to exec node | ||||||
| ##### 3.1.0-beta.3: Beta Release |  - Add TLS node and update MQTT/HTTP nodes to use it | ||||||
|  |  - Let trigger node also send last payload to arrive | ||||||
| Editor |  - Add timestamp as a default typedInput and update Inject and change nodes to match, | ||||||
|  |  - Add QoS option to MQTT In node | ||||||
|  - Select the item that is specified in a deep link URL (#4113) @Steve-Mcl |  - Add status to exec spawn mode | ||||||
|  - Update to Monaco 0.38.0 (#4189) @Steve-Mcl |  - Add Move capability to Change node | ||||||
|  - Place subflow outputs/inputs relative to current view (#4183) @knolleary |  - Update Serial node to support custom baud rates | ||||||
|  - Enable RED.view.select to select group by id (#4184) @knolleary |  - Add support for array-syntax in typedInput msg properties | ||||||
|  - Combine existing env vars when merging groups (#4182) @knolleary |  - Add RED.util to Function node sandbox | ||||||
|  - Avoid creating empty global-config node if not needed (#4153) @knolleary |  - Capture error stack on node.error. Closes #879 | ||||||
|  - Fix group selection when using lasso (#4108) @knolleary |  | ||||||
|  - Use editor path in generating localStorage keys (#4151) @mw75 |  | ||||||
|  - Ensure no node credentials are included when exporting to clipboard (#4112) @knolleary | Fixes | ||||||
|  - Fix jsonata expression test ui (#4097) @knolleary |  | ||||||
|  - Fix search button in palette popover (#4096) @knolleary |  - Add error handling to all node definition api calls | ||||||
|  |  - Handle null return from Function node in array of messages | ||||||
| Runtime |  - Defer loading of token sessions until they are accessed. Fixes #895 | ||||||
|  |  - set pi gpio pin status correctly if set on start | ||||||
|  - Allow options object on each httpStatic configuration (#4109) @kevinGodell |  - Prevent parent window scrolling when view is focused. Fixes #635 | ||||||
|  - Ensure non-zero exit codes for errors (#4181) @knolleary |  - Handle missing tab nodes in a loaded flow config | ||||||
|  - Ensure external modules are installed synchronously (#4180) @knolleary |  - Ensure typedInput dropdown doesn't fall off the page | ||||||
|  - Update dependecies include got (#4155) @knolleary |  - Protect against node types with reserved names such as toString. Fixes #880 | ||||||
|  - Add Japanese translations for v3.1 beta.2 (#4158) @kazuhitoyokoi |  - Do not rely on the HTML file to identify where nodes are registered from | ||||||
|  - Ensure express server options are applied consistently (#4178) @knolleary |  - Preserve node properties on import | ||||||
|  - Remove version info from theme endpoint (#4179) @knolleary |  - Fix regression in delay node. topic based queue was emptying all the time instead of spreading out messages. | ||||||
|  - Add Japanese translations for welcome tour of 3.1.0 beta.2 (#4145) @kazuhitoyokoi |  - Throw an error if a Function node adds an input event listener | ||||||
|  - Added SHA-256 and SHA-512-256 digest authentication (#4100) @sroebert |  - Fix hang on partial deploy with disconnected mqtt node | ||||||
|  - Add "timers" types to known types (#4103) @Steve-Mcl |  - TypedInput: preload type icons to ensure width calc correct | ||||||
|  |  - Ensure tcp node creates a buffer of size 1 at least | ||||||
| Nodes |  - Return editorTheme default if value is undefined | ||||||
|  |  - Fix RED.util.compareObjects for Function created objects and Buffers | ||||||
|  - Allow Catch/Status nodes to be scoped to their group (#4185) @NetHans |  - Ensure default settings copied to command-line specified userDir | ||||||
|  - MQTT: Option to disable MQTT topic unsubscribe on disconnect (#4078) @flying7eleven |  | ||||||
|  |  | ||||||
|  | #### 0.13.4: Maintenance Release | ||||||
| ##### 3.1.0-beta.2: Beta Release |  | ||||||
|  |  - Add timed release mode to delay node | ||||||
| Editor |  - Enable link splicing for when import_dragging nodes. Closes #811 | ||||||
|  |  - Fix uncaught exception on deploy whilst node sending messages | ||||||
|  - NEW: Add change icon to tabs (#4068) @knolleary |  - Deprecate old mqtt client and connection pool modules | ||||||
|  - NEW: Complete overhaul of Group UX (#4079) @knolleary |  - Change node: add bool/num types to change mode Closes #835 | ||||||
|  - NEW: Add link to node help in node edit dialog footer (#4065) @knolleary |  - Validate fields that are `$(env-vars)` Closes #825 | ||||||
|  - NEW: Added editor feature for connecting multiple nodes to single node (#4051) @sonntam |  - Handle missing config nodes when validating node properties | ||||||
|  - NEW: Increase workspace size to 8000x8000 (#4094) @knolleary |  - Pi node - don't try to send data if closing | ||||||
|  - Ensure node buttons are redrawn when flow lock state is changed (#4091) @knolleary |  - Load node message catalog when added dynamically | ||||||
|  - Prevent loops being created with junction nodes (#4087) @knolleary |  - Split palette labels on spaces and hyphens when laying out | ||||||
|  - Prevent opening locked node's edit dialog (#4069) @knolleary |  - Warn if editor routes are accessed but runtime not started Closes #816 | ||||||
|  - Reverse direction of tab scroll to expected direction (#4064) @knolleary |  - Better handling of zero-length flow files Closes #819 | ||||||
|  - Add cancel operation to editableList (#4077) @HiroyasuNishiyama |  - Allow runtime calls to RED._ to specify other namespace | ||||||
|  - Apply Mermaid diagram for project settings UI (#4054) @kazuhitoyokoi |  - Better right alignment of numerics in delay and trigger nodes | ||||||
|  - Add tooltip for show/hide button on info sidebar (#4050) @kazuhitoyokoi |  - Allow node modules to include example flows | ||||||
|  - Fix align nodes on locked tab (#4072) @HiroyasuNishiyama |  - Create node_modules in userDir | ||||||
|  - Fix importing connected link nodes into a subflow (#4082) @knolleary |  - Ensure errors in node def functions don't break view rendering Fixes #815 | ||||||
|  - Fix to add empty marker to empty group (#4060) @HiroyasuNishiyama |  - Updated Inject node info with instructions for flow and global options | ||||||
|  - Fix image URLs for v3.0 tour (#4053) @kazuhitoyokoi |  | ||||||
|  - Show scrollbar in notification dialog only when needed (#4048) @kazuhitoyokoi |  | ||||||
|  - Update-monaco-and-typings (#4089) @Steve-Mcl |  | ||||||
|  - Update jquery UI (#4088) @knolleary | #### 0.13.3: Maintenance Release | ||||||
|  - Support i18n of lock/unlock buttons in flow property UI (#4049) @kazuhitoyokoi |  | ||||||
|  - Translation kr (#3895) @hae-iotplatform |  - Fix crash on repeated inject of invalid json payload | ||||||
|  - Translation zhcn (!!请懂中文的帮忙review) (#3952) @cliyr |  - Add binary mode to tail node | ||||||
|  - Add French translation of nodes (#3964) @GogoVega |  - Revert Cheerio to somewhat smaller version | ||||||
|  - Add French translation (#3962) @GogoVega |  - Add os/platform info to default debug | ||||||
|  - Portuguese Brazilian (pt-BR) translation (#3804) @FabsMuller |  | ||||||
|   |  | ||||||
|  |  | ||||||
| Runtime | #### 0.13.2: Maintenance Release | ||||||
|  |  | ||||||
|  - NEW: Generate stable ids for subflow instance internal nodes (#4093) @knolleary |  - Don't force reconnect mqtt client if message arrives (fixes the MQTT connect/disconnect endless cycle) | ||||||
|  - NEW: Change default file name to flows.json in project feature (#4073) @kazuhitoyokoi |  - Add -p/--port option to override listening port | ||||||
|  - NEW: Deprecate synchronous access to jsonata (#4090) @knolleary |  - Invert config node filter toggle button colours so state is more obvious | ||||||
|  - Add Node 18 to test matrix (#4084) @knolleary |  - Add timeout to httprequest node | ||||||
|  - Bump minimum nodejs version supported to match documented value (#4086) @knolleary |  - Tidy up of all node info content - make style consistent | ||||||
|  - Update monaco docs link in settings.js (#4075) @Steve-Mcl |  - Make jquery spinner element css consistent with other inputs | ||||||
|  - Remove duplicated messages in the message catalog (#4066) @kazuhitoyokoi |  - tcp node add reply (to all) capability | ||||||
|  - Ensure errors in preDeliver callback are handled (#3911) @knolleary |  - Allow the template node to be treated as plain text | ||||||
|  - Fix "EADDRINUSE" error (#4046) @bggbr |  - Validate MQTT In topics Fixes #792 | ||||||
|  |  - httpNodeAuth should not block http options requests Fixes #793 | ||||||
| Nodes |  - Disable perMessageDeflate on WS servers - fixes 'zlib binding closed' error | ||||||
|  |  - Clear trigger status icon on re-deploy | ||||||
|  - Link Call: Clear link-call timeouts when node is closed (#4085) @knolleary |  - Don't default inject payload to blank string | ||||||
|  - Join: ensure inflight status is cleared when in auto mode (#4083) @knolleary |  - Trigger node, add configurable reset | ||||||
|  - File Out: Fix extra newline append for multipart file write (#3915) @dceejay |  - Allow function properties in settings Fixes #790 - fixes use of httpNodeMiddleware | ||||||
|  - Add validators for complete and link call nodes (#4056) @kazuhitoyokoi |  - Fix order of config dialog calls to save/creds/validate | ||||||
|  |  - Add debounce to Pi GPIO node | ||||||
| ##### 3.1.0-beta.1: Beta Release |  | ||||||
|  |  | ||||||
| Editor |  | ||||||
|  | #### 0.13.1: Maintenance Release | ||||||
|  - NEW: Locking Flows (#3938) @knolleary |  | ||||||
|  - NEW: Improve UX around hiding flows via context menu (#3930) @knolleary |  - Revert wrapping of http request object | ||||||
|  - NEW: Add support for inline image in markdown editor by drag and drop of an image file (#4006) @HiroyasuNishiyama |  | ||||||
|  - NEW: Add support for mermaid diagram to markdown editor (#4007) @HiroyasuNishiyama |  | ||||||
|  - NEW: Support uri fragments for nodes and groups including edit support (#3870) @knolleary |  | ||||||
|  - NEW: Add global environment variable feature (#3941) @HiroyasuNishiyama | #### 0.13.0: Milestone Release | ||||||
|  |  | ||||||
|  - Remember compact/pretty flow export user choice (#3974) @Steve-Mcl |  - Add 'previous value' option to Switch node | ||||||
|  - fix .red-ui-notification class (#4035) @xiaobinqt |  - Allow existing nodes to splice into links on drag | ||||||
|  - Fix border radius on Modules list header (#4038) @bonanitech |  - CORS not properly configured on multiple http routes Fixes #783 | ||||||
|  - fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama |  - Restore shift-drag to snap/unsnap to grid | ||||||
|  - Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama |  - Moving nodes with keyboard should flag workspace dirty | ||||||
|  - Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama |  - Notifications flagged as fixed should not be click-closable | ||||||
|  - fix hide subflow tooltip (#4033) @HiroyasuNishiyama |  - Rework config sidebar and deploy warning | ||||||
|  - Fix disabled menu items in project feature (#4027) @kazuhitoyokoi |  - Wrap http request object to match http response object | ||||||
|  - Let themes change radialMenu text colors (#3995) @bonanitech |  - Add 'view' menu and reorganise a few things | ||||||
|  - Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi |  - Allow shift-click to detach existing wires | ||||||
|  - Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi |  - Splice nodes dragged from palette into links | ||||||
|  - Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi |  - try to trim imported/dragged flows to [ ] | ||||||
|  - Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi |  - Move version number as title of NR logo | ||||||
|  - Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary |  - Moving nodes mark workspace as dirty | ||||||
|  - Handle replacing unknown node inside group or subflow (#3921) @knolleary |  - Ok/Cancel edit dialogs with Ctrl-Enter/Escape | ||||||
|  - Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo |  - Handle OSX Meta key when selecting nodes | ||||||
|  - i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama |  - Add grid-alignment options | ||||||
|  - add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama |  - Add oneditresize function definition | ||||||
|  - Fix autocomplete entry for responseUrl (#3884) @knolleary |  - Rename propertySelect to typedInput and add boolean opt | ||||||
|  - Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama |  - Add propertySelect to switch node | ||||||
|  - Fix search type with spaces (#3841) @Steve-Mcl |  - Add propertySelect support to Change node | ||||||
|  - Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama |  - Add context/flow/global support to Function node | ||||||
|  - Add button type to the adding SSH key button (#3866) @kazuhitoyokoi |  - Add node context/flow/global | ||||||
|  - Check radio button as default in project dialog (#3879) @kazuhitoyokoi |  - Add propertySelect jquery widget | ||||||
|  - Add $clone as supported function (#3874) @HiroyasuNishiyama |  - Add add/update/delete flow apis | ||||||
|  - Env var jsonata (#3807) @HiroyasuNishiyama |  - Allow core nodes dir to be provided to runtime via settings | ||||||
|  - Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi |  - Tidy up API passed to node modules | ||||||
|  |  - Move locale files under api/runtime components | ||||||
| Runtime |  - Add flow reload admin api | ||||||
|  |  | ||||||
|  - Force IPv4 name resolution to have priority (#4019) @dceejay |  | ||||||
|  - Fix async loading of modules containing both nodes and plugins (#3999) @knolleary |  | ||||||
|  - Use main branch as default in project feature (#4036) @kazuhitoyokoi | #### 0.12.5: Maintenance Release | ||||||
|  - Rename package var to avoid strict mode error (#4020) @knolleary |  | ||||||
|  - Fix typos in settings.js (#4013) @ypid |  - Add attribute capability to HTML parser node | ||||||
|  - Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary |  - Add Pi Keyboard code node | ||||||
|  - Ignore commit error in project feature (#3987) @kazuhitoyokoi |  - Fix for MQTT client connection cycling on partial deploy | ||||||
|  - Update dependencies (#3969) @knolleary |  - Fix for tcp node properly closing connections | ||||||
|  - Add check that node sends object rather than primitive type (#3909) @knolleary |  - Update sentiment node dependencies | ||||||
|  - Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary |  - Fix for file node handling of UTF8 extended characters | ||||||
|  - Fix nodesDir scan when node package has js/html in sub dir to package.json (#3867) @Steve-Mcl |  | ||||||
|  - Fix file permissions (#3917) @kazuhitoyokoi |  | ||||||
|  - ci: add minimum GitHub token permissions for workflows (#3907) @boahc077 |  | ||||||
|  | #### 0.12.4: Maintenance Release | ||||||
| Nodes |  | ||||||
|  |  - Add readOnly setting to prevent file writes in localfilesystem storage | ||||||
|  - Catch: fix typo in catch.html (#3965) @we11adam |  - Support bcrypt for httpNodeAuth | ||||||
|  - Change: Fix change node overwriting msg with itself (#3899) @dceejay |  - Pi no longer needs root workaround to access gpio | ||||||
|  - Comment node: Clarify where the text will appear (#4004) @dirkjanfaber |  - Fix: Input File node will not retain the file name | ||||||
|  - CSV: change replace to replaceAll (#3990) @dceejay |  | ||||||
|  - CSV node: check header properties for ' and " (#3920) @dceejay |  | ||||||
|  - CSV: Fix for CSV undefined property (#3906) @dceejay |  | ||||||
|  - Delay: let delay node handle both flush then reset (#3898) @dceejay | #### 0.12.3: Maintenance Release | ||||||
|  - Function: Limit number of ports in function node (#3886) @kazuhitoyokoi |  | ||||||
|  - Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi |  - Fixes for TCP Get node reconnect handling | ||||||
|  - Function: add function node monaco types util and promisify (#3868) @Steve-Mcl |  - Clear delay node status on re-deploy | ||||||
|  - HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary |  - Update Font-Awesome to v4.5 | ||||||
|  - HTTP Request: Support form-data arrays (#3991) @hardillb |  - Fix trigger to block properly until reset | ||||||
|  - HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary |  - Update example auth properties in settings.js | ||||||
|  - HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb |  - Ensure httpNodeAuth doesn't get applied to admin routes | ||||||
|  - HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi |  - TCP Get node not passing on existing msg properties | ||||||
|  - HTTP Response: Ensure statusCode is a number (#3894) @hardillb |  | ||||||
|  - Inject: Allow Inject node to work with async context stores (#4021) @knolleary |  | ||||||
|  - Join/Batch: Add count to join and batch node labels (#4028) @dceejay |  | ||||||
|  - MQTT: Fix birth topic handling in MQTT node (#3905) @Steve-Mcl | #### 0.12.2: Maintenance Release | ||||||
|  - MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi |  | ||||||
|  - MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl |  - Enable touch-menu for links so they can be deleted | ||||||
|  - MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl |  - Allow nodes to be installed by path name | ||||||
|  - MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary |  - Fix basic authentication on httpNode/Admin/Static | ||||||
|  - MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl |  - Handle errors thrown in Function node setTimeout/Interval | ||||||
|  - Range: Add drop mode to range node (#3935) @dceejay |  - Fix mqtt node lifecycle with partial deployments | ||||||
|  - Remove done from describe (#3873) @HiroyasuNishiyama |  - Update tcp node status on reconnect after timeout | ||||||
|  - Split node: avoid duplicate done call for buffer split (#4000) @knolleary |  - Debug node not handling null messages | ||||||
|  - Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi |  - Kill processes run with exec node when flows redeployed | ||||||
|  - TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay |  - Inject time spinner incrementing value incorrectly | ||||||
|  - Template: Add information about environment variable to template node (#3882) @kazuhitoyokoi |  | ||||||
|  - Trigger: Hide trigger node repeat send option if sending nothing (#4023) @dceejay |  | ||||||
|  - Watch: fix watch node test on MacOS/ARM (#3942) @HiroyasuNishiyama |  | ||||||
|  | #### 0.12.1: Maintenance Release | ||||||
| #### 3.0.2: Maintenance Release |  | ||||||
|  |  - Enable touch-menu for links so they can be deleted | ||||||
| Editor |  - Allow nodes to be installed by path name | ||||||
|  |  - Fix basic authentication on httpNode/Admin/Static | ||||||
|  - Fix workspace chart bottom property (#3812) @bonanitech |  | ||||||
|  - Update german translation (#3802) @Dennis14e |  | ||||||
|  - Support color reset to the default in subflow and group (#3801) @kazuhitoyokoi |  | ||||||
|  - Allow generateNodeNames to handle names containing regex control chars (#3817) @knolleary | #### 0.12.0: Milestone Release | ||||||
|  - Hide scrollbars until they're needed (#3808) @bonanitech |  | ||||||
|  - Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary |  - Change/Switch rules now resize with dialog width | ||||||
|  - remove console.log (#3820) @Steve-Mcl |  - Support for node 4.x | ||||||
|  |  - Move to Express 4.x | ||||||
| Runtime |  - Copy default settings file to user dir on start up | ||||||
|  |  - Config nodes can be scoped to a particular subflow/tab | ||||||
|  - Register subflow module instance node with parent flow (#3818) @knolleary |  - Comms link tolerates <5 second breaks in connection before notifying user | ||||||
|  |  - MQTT node overhaul - add will/tls/birth message support | ||||||
| Nodes |  - Status node - to report status events from other nodes | ||||||
|  |  - Error node can be targeted to specific other nodes | ||||||
|  - HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb |  - JSON node can encode Array types | ||||||
|  |  - Switch node regular expression rule can now be set to be case-insensitive | ||||||
| #### 3.0.1: Maintenance Release |  - HTTP In node can accept non-UTF8 payloads - will return a Buffer when appropriate | ||||||
|  |  - Exec node configuration consistent regardless of the spawn option | ||||||
| Editor |  - Function node can now display status icon/text | ||||||
|  |  - CSV node can now handle arrays | ||||||
|  - Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl |  - setInterval/clearInterval add to Function node | ||||||
|  - Sys info (diagnostics report) amendments (#3793) @Steve-Mcl |  - Function node automatically clears all timers (setInterval/setTimeout) when the node is stopped | ||||||
|  - Allow `mode` and `title` to be omitted in `options` argument for `createEditor` (#3791) @Steve-Mcl |  | ||||||
|  - Fix focus issues (#3789) @Steve-Mcl |  | ||||||
|  - Ensure all typedInput buttons have button type set (#3788) @knolleary |  | ||||||
|  - Do not flag hasUsers=false nodes as unused in search (#3787) @knolleary | #### 0.11.2: Maintenance Release | ||||||
|  - Properly position quick-add dialog in all cases (#3786) @knolleary |  | ||||||
|  - Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary |  - Allow XML parser options be set on the message | ||||||
|  - Remove use of Object.hasOwn (#3784) @knolleary |  - Add 'mobile' category to the palette (no core nodes included) | ||||||
|  |  - Allow a message catalog provide a partial translation | ||||||
| #### 3.0.0: Milestone Release |  - Fix HTTP Node nls message id | ||||||
|  |  - Remove delay spinner upper limit | ||||||
| Editor |  - Update debug node output to include length of payload | ||||||
|  |  | ||||||
|  - Use theme page and header values if settings.js values are not present (#3767) @Steve-Mcl |  | ||||||
|  - Focus editor for undo after some actions in menu (#3759) @kazuhitoyokoi |  | ||||||
|  - Ensure node icon shade has properly rounded corners (#3763) @knolleary |  | ||||||
|  - Fix storing subflow credential type when input has multiple types (#3762) @knolleary | #### 0.11.1: Maintenance Release | ||||||
|  - Ensure global-config and flow-config have info in the hierarchy popover (#3752) @Steve-Mcl |  | ||||||
|  - Include dirty state in history event (#3748) @Steve-Mcl |  - Fix exclusive config node check when type not registered (prevented HTTP In node from being editable unless the swagger node was also installed) | ||||||
|  - Fix display direction of context sub-menu (#3746) @knolleary |  | ||||||
|  - Fix clear pinned paths of debug sidebar menu (#3745) @HiroyasuNishiyama |  | ||||||
|  - prevent exception generating tooltip for deleted nodes (#3742) @Steve-Mcl |  | ||||||
|  - Fix context menu issues ready for v3 beta.5 (#3741) @Steve-Mcl | #### 0.11.0: Milestone Release | ||||||
|  - Do not generate new node-ids when pasting a cut flow (#3729) @knolleary |  | ||||||
|  - Fix to prevent node from moving out of workspace (#3731) @HiroyasuNishiyama |  - Add Node 0.12 support | ||||||
|  - Don't let themes change disabled config node background color (#3736) @bonanitech |  - Internationalization support | ||||||
|  - Move colors left behind in #3692 to CSS variables (#3737) @bonanitech |  - Editor UI refresh | ||||||
|  - Fix handling of global debug message (#3733) @HiroyasuNishiyama |  - Add RBE node | ||||||
|  - Fix label overflow @ config-node palette (#3730) @ralphwetzel |  - File node optionally creates path to file | ||||||
|  - Fix defaulting to monaco if settings does not contain codeEditor (#3732) @knolleary |  - Function node can access `clearTimeout` | ||||||
|  - Disable keyboard shortcut mapping when showing Edit[..]Dialog (#3700) @ralphwetzel |  - Fix: Unable to login with 'read' permission | ||||||
|  - Update add-junction menu to work in more cases (#3727) @knolleary |  | ||||||
|  - Ensure importMap is not null when using import UI (#3723) @Steve-Mcl |  | ||||||
|  - Add Japanese translations for v3.0-beta.4 (#3724) @kazuhitoyokoi |  | ||||||
|  - Fix "split with" on virtual links (#3766) @Steve-Mcl | #### 0.10.10: Maintenance Release | ||||||
|  |  | ||||||
| Runtime |  - Fix permissions issue with packaged nrgpio script | ||||||
|  | - Add better help message if deprecated node missing | ||||||
|  - Do not remove unknown credentials of Subflow Modules (#3728) @knolleary |  | ||||||
|  - Add missing entries from beta.4 changelog (#3721) @knolleary |  | ||||||
|  |  | ||||||
| Nodes | #### 0.10.9: Maintenance Release | ||||||
|  |  | ||||||
|  - Change: Fix change node, not handling from field properly when using context (#3754) @Fadoli | Fix packaging of bin scripts | ||||||
|  - Link Call: Fix linkcall registry bugs (#3751) @Steve-Mcl |  | ||||||
|  - WebSocket: Fix close timeout of websocket node (#3734) @HiroyasuNishiyama |  | ||||||
|  |  | ||||||
| #### 3.0.0-beta.4: Beta Release | #### 0.10.8: Maintenance Release | ||||||
|  |  | ||||||
| Editor | - Nodes moved out of core | ||||||
|  |   - still included as a dependency: twitter, serial, email, feedparser | ||||||
|  - Move all colours to CSS variables (#3692) @bonanitech |  - no longer included: mongo, arduino, irc, redis | ||||||
|  - Fix clicking on node in workspace to hide context menu (#3696) @knolleary | - node icon defn can be a function | ||||||
|  - Fix credential type input item of subflow template (#3703) @HiroyasuNishiyama | - http_proxy support | ||||||
|  - Add option flag `reimport` to `importNodes` (#3718) @Steve-Mcl | - httpNodeMiddleware setting | ||||||
|  - Update german translation (#3691) @Dennis14e | - Trigger node ui refresh | ||||||
|  - List welcome tours in help sidebar (#3717) @knolleary | - editorTheme setting | ||||||
|  - Ensure 'hidden flow' count doesn't include subflows (#3715) @knolleary | - Warn on deploy of unused config nodes | ||||||
|  - Fix Chinese translate (#3706) @hotlong | - catch node prevents error loops | ||||||
|  - Fix use default button for node icon (#3714) @kazuhitoyokoi |  | ||||||
|  - Fix select boxes vertical alignment (#3698) @bonanitech |  | ||||||
|  - Ensure workspace clean after undoing dropped node (#3708) @Steve-Mcl |  | ||||||
|  - Use solid colour as config node icon background to hide text overflow (#3710) @Steve-Mcl | #### 0.10.6: Maintenance Release | ||||||
|  - Increase quick-add height to reveal 2 most recent entries (#3711) @Steve-Mcl |  | ||||||
|  - Set default editor to monaco in absence of user preference (#3702) @knolleary | Changes: | ||||||
|  - Add Japanese translations for v3.0-beta.3 (#3688) @kazuhitoyokoi |  - Performance improvements in editor | ||||||
|  - Fix handling of spacebar inside JSON visual editor (#3687) @knolleary |  - Palette appearance update | ||||||
|  - Fix menu padding to handle both icons and submenus (#3686) @knolleary |  - Warn on navigation with undeployed changes | ||||||
|  - Include scroll offset when positioning quick-add dialog (#3685) @knolleary |  - Disable undeployed node action buttons | ||||||
|  |  - Disable subflow node action buttons | ||||||
| Runtime |  - Add Catch node | ||||||
|  |  - Add logging functions to Function node | ||||||
|  - Allow flows to be stopped and started manually (#3719) @knolleary |  - Add send function to Function node | ||||||
|  - Import default export if node is a transpiled es module (#3669) @dschmidt |  - Update Change node to support multiple rules | ||||||
|  - Leave Monaco theme commented out by default (#3704) @bonanitech |  | ||||||
|  |  | ||||||
| Nodes |  | ||||||
|  | #### 0.10.4: Maintenance Release | ||||||
|  - CSV: Fix CSV node to handle when outputting text fields (#3716) @dceejay |  | ||||||
|  - Delay: Fix delay rate limit last timing when empty (#3709) @dceejay | Changes: | ||||||
|  - Link: Ensure link-call cache is updated when link-in is modified (#3695) @Steve-Mcl |  | ||||||
|  - Join: Join node in reduce mode doesn't keep existing msg properties (#3670) @dceejay | - http request node passes on request url as msg.url | ||||||
|  - Template: Add support for evalulating {{env.<var>}} within a template node (#3690) @cow0w | - handle config nodes appearing out of order in flow file - don't assume they are always at the start | ||||||
|  | - move subflow palette category to the top, to make it more obvious | ||||||
| #### 3.0.0-beta.3: Beta Release | - fix labelling of Raspberry Pi pins | ||||||
|  | - allow email node to mark mail as read | ||||||
| Editor | - fix saving library content | ||||||
|  | - add node-red and node-red-pi start scripts | ||||||
|  - Add Right-Click content menu (#3678) @knolleary | - use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir) | ||||||
|  - Fix disable junction (#3671) @HiroyasuNishiyama |  | ||||||
|  - Add Japanese translations for v2.2.3 (#3672) @kazuhitoyokoi |  | ||||||
|  - Reset mouse state when switching tabs (#3643) @knolleary |  | ||||||
|  - Fix uncorrect fix of junction to subflow conversion (#3666) @HiroyasuNishiyama | #### 0.10.3: Maintenance Release | ||||||
|  - Fix undoing junction to subflow (#3653) @HiroyasuNishiyama |  | ||||||
|  - Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama | Fixes: | ||||||
|  - Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama |  | ||||||
|  - Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi |  - httpAdminAuth was too aggressively deprecated (ie removed); restoring with a console warning when used | ||||||
|  - Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama |  - adds reporting of node.js version on start-up | ||||||
|  - Fix to sanitize tab name (#3646) @HiroyasuNishiyama |  - mongo node skip/limit options can be strings or numbers | ||||||
|  - Fix selector placement (#3644) @bonanitech |  - CSV parser passes through provided message object | ||||||
|  - Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi |  | ||||||
|  - Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama |  | ||||||
|  - Fix layer of palette node (#3638) @HiroyasuNishiyama |  | ||||||
|  - Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama | #### 0.10.2: Maintenance Release | ||||||
|  - Fix typo in CSS (#3628) @bonanitech |  | ||||||
|  - Use the correct variable for the gutter text color (#3615) @bonanitech | Fixes: | ||||||
|  |  - subflow info sidebar more useful | ||||||
|  |  - adds missing font-awesome file | ||||||
| Runtime |  - inject node day selection defaulted to invalid selection | ||||||
|  |  - loading a flow with no tabs failed to add nodes to default tab | ||||||
|  - Support loading node modules from `nodesdir` (#3676) @Steve-Mcl |  | ||||||
|  - fix buffer parse error message of evaluateNodeProperty (#3624) @HiroyasuNishiyama |  | ||||||
|  |  | ||||||
| Nodes |  | ||||||
|  |  | ||||||
|  - File: Further simplify file node filename entry UX (v3) (#3677) @Steve-Mcl |  | ||||||
|  - Function: Fix initial cursor position of init/finalize tab of function node (#3674) @HiroyasuNishiyama |  | ||||||
|  - Function: Fix ESM module loading in Function node (#3645) @knolleary |  | ||||||
|  - Inject: Fix JSONata evaluation of inject button (#3632) @HiroyasuNishiyama |  | ||||||
|  - TCP: Dont delete TCP socket twice (#3630) @Steve-Mcl |  | ||||||
|  - MQTT Node: define noproxy variable (#3626) @Steve-Mcl |  | ||||||
|  - Debug: i18n debug sidebar node label (#3623) @HiroyasuNishiyama |  | ||||||
|  |  | ||||||
| #### 3.0.0-beta.2: Beta Release |  | ||||||
|  |  | ||||||
| **Migration from 2.x** |  | ||||||
|  |  | ||||||
|  - The 'slice wires' action has changed from Ctrl-RightMouseButton to Alt-LeftMouseButton |  | ||||||
|  |  | ||||||
| Editor |  | ||||||
|  |  | ||||||
|  - Rework Junctions to be more node like in their event handling (#3607) @knolleary |  | ||||||
|  - Change slicing / slice-junction operations over to mouse button 0 (Left Mouse Button) (#3609) @Steve-Mcl |  | ||||||
|  - Do not slice-junction link node wires (#3608) @knolleary |  | ||||||
|  - Handle many-to-one slicing of wires (#3604) @knolleary |  | ||||||
|  - Ensure ACE worker options are set (#3611) @Steve-Mcl |  | ||||||
|  - Remove duplicate history add of ungroup event (#3605) @knolleary |  | ||||||
|  - use text width instead of number of characters for deciding select fi… (#3603) @HiroyasuNishiyama |  | ||||||
|  - Update Japanese info of link call node reflecting update of English info (#3600) @HiroyasuNishiyama |  | ||||||
|  - Fix typedInput label not visible on themes (#3580) @bonanitech |  | ||||||
|  - Fix project switching when junctions are present (#3595) @Steve-Mcl |  | ||||||
|  - Fix junction: when wiring from a regular nodes INPUT, backwards to a junction (#3591) @Steve-Mcl |  | ||||||
|  - Fix error initialising flow tab editor (#3585) @Steve-Mcl |  | ||||||
|  - Add Japanese translations for v3.0-beta.1 (#3576) @kazuhitoyokoi |  | ||||||
|  - Fix image paths where `red/image/typedInput/XXXX.png` should be `red/image/typedInput/XXXX.svg` (#3592) @kazuhitoyokoi |  | ||||||
|  - Fix browser console error Uncaught TypeError when searching certain terms (#3584) @Steve-Mcl |  | ||||||
|  |  | ||||||
| Runtime |  | ||||||
|  |  | ||||||
|  - fix error on system-info action (#3589) @HiroyasuNishiyama |  | ||||||
|  |  | ||||||
| Nodes |  | ||||||
|  |  | ||||||
|  - I18n switch rule selector (#3602) @HiroyasuNishiyama |  | ||||||
|  - Handle removal of event handlers to allow mqtt client.end() to work (#3594) @PhilDay-CT |  | ||||||
|  - update link-call node info according to current behavior (#3597) @HiroyasuNishiyama |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #### 3.0.0-beta.1: Beta Release |  | ||||||
|  |  | ||||||
| **Migration from 2.x** |  | ||||||
|  |  | ||||||
|  - Node-RED now requires Node.js 14.x or later. |  | ||||||
|  - New installs of Node-RED will default to the monaco editor. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Editor |  | ||||||
|  |  | ||||||
|  - Add Junctions (#3462) @knolleary |  | ||||||
|  - Allow node name to be auto-generated when added (#3478, #3538) @knolleary |  | ||||||
|  - Set monaco as default code editor as of v3.x (#3543) @Steve-Mcl |  | ||||||
|  - Update Monaco to V0.33.0 (#3522) @Steve-Mcl |  | ||||||
|  - Auto-complete Improvements (#3521) @Steve-Mcl |  | ||||||
|  - Add a tooltip to debug sidebar messages to reveal full path to node (#3503) @knolleary |  | ||||||
|  - Fix down arrow triggering menu in search box (#3507) @Steve-Mcl |  | ||||||
|  - Add Japanese translations for v3.0 (#3512) @kazuhitoyokoi |  | ||||||
|  - Add feature: Continuous search tools (search previous, search next) (#3405) @Steve-Mcl |  | ||||||
|  - Add feature: split-wire-to-links (#3399, #3476) @Steve-Mcl |  | ||||||
|  - Add copy button to node properties tables (#3390) @knolleary |  | ||||||
|  - Add info-tab search options dropdown to the regular search (#3395) @Steve-Mcl |  | ||||||
|  - New Feature: Add ability to find modified nodes/flows. (#3392) @Steve-Mcl |  | ||||||
|  - Code editor ux improvements around remembering state of each code editor in a flow (#3553) @Steve-Mcl |  | ||||||
|  - Make it easier to apply themes on SVG icons (#3515) @bonanitech |  | ||||||
|  - Add support of property validation message (#3438) @HiroyasuNishiyama |  | ||||||
|  - Ensure node validation tooltip is closed when field becomes valid (#3570) @knolleary |  | ||||||
|  - Add "search for" buttons to notifications (#3567) @Steve-Mcl |  | ||||||
|  - Don't let themes change node config colors (#3564) @bonanitech |  | ||||||
|  - Fix gap between typedInput containers borders (#3560) @bonanitech |  | ||||||
|  - Fix recording removed links in edit history (#3547) @knolleary |  | ||||||
|  - Remove unused SASS vars (#3536) @bonanitech |  | ||||||
|  - Add custom style for jQuery widgets borders (#3537) @bonanitech |  | ||||||
|  - fix out of scope reference of hasUnusedConfig variable (#3535) @HiroyasuNishiyama |  | ||||||
|  - correct "non string" check parenthesis (#3524) @Steve-Mcl |  | ||||||
|  - Ensure i18n of scoped package name (#3516) @Steve-Mcl |  | ||||||
|  - Prevent shortcut deploy when deploy button shaded (#3517) @Steve-Mcl |  | ||||||
|  - Fix: Sidebar "Configuration" filter button tooltip (#3500) @ralphwetzel |  | ||||||
|  - Add the ability to customize diff colors even more (#3499) @bonanitech |  | ||||||
|  - Do JSON comparison of old value/new value in editor (#3481) @Steve-Mcl |  | ||||||
|  - Fix nodes losing their wires when in an iframe (#3484) @zettca |  | ||||||
|  - Improve scroll into view (#3468) @Steve-Mcl |  | ||||||
|  - Do not show 1st tab if hidden when loading (#3464) @Steve-Mcl |  | ||||||
|  |  | ||||||
| Runtime |  | ||||||
|  |  | ||||||
|  - Fix importing external module from node-red module (#3541) @knolleary |  | ||||||
|  - Add support for multiple static paths with optional static root (#3542) @Steve-Mcl |  | ||||||
|  - Store external token when authenticating if provided (#3460) @ArFe |  | ||||||
|  - Support OAuth/OpenID logout (#3388) @mw75 |  | ||||||
|  - Allow adminAuth to auto-login users when using passport strategy (#3519) @knolleary |  | ||||||
|  - Add runtime diagnostics admin endpoint (#3511) @Steve-Mcl |  | ||||||
|  - Don't start if user has no home directory (#3540) @hardillb |  | ||||||
|  - Error on invalid encrypted credentials (#3498) @sammachin |  | ||||||
|  |  | ||||||
| Nodes |  | ||||||
|  |  | ||||||
|  - Debug: Add message count option to Debug status (#3544 #3551) @rafaelmuynarsk @knolleary |  | ||||||
|  - File: Change basic Filename field to a typedInput (#3533) @Steve-Mcl |  | ||||||
|  - HTTP Request: Add UI for Http Request node headers (#3488) @Steve-Mcl |  | ||||||
|  - Inject: let inject optionally fire at start in only at time mode. (#3385) @dceejay |  | ||||||
|  - Link Call: Dynamic link call (#3463) @Steve-Mcl |  | ||||||
|  - Link Call: Display link targets of nodes in a regular flow, for Link Call nodes inside a subflow (#3528) @Steve-Mcl |  | ||||||
|  - MQTT: MQTT payload auto parsing improvements (#3530) @Steve-Mcl |  | ||||||
|  - MQTT: Add client and Runtime MQTT topic validation (#3563) @Steve-Mcl [dev] |  | ||||||
|  - MQTT: save and restore v5 config user props (#3562) @Steve-Mcl |  | ||||||
|  - MQTT: Fix incorrect MQTT status (#3552) @Steve-Mcl |  | ||||||
|  - MQTT: fix reference error of msg.status in debug node (#3526) @HiroyasuNishiyama |  | ||||||
|  - MQTT: Add unit tests for MQTT nodes (#3497) @Steve-Mcl |  | ||||||
|  - MQTT: fix typo of will properties (#3502) @Steve-Mcl |  | ||||||
|  - MQTT: ensure mqtt v5 props can be set false (#3472) @Steve-Mcl |  | ||||||
|  - Switch: add check for NaN in is of type number to be false (#3409) @dceejay |  | ||||||
|  - TCP: TCP node better split (#3465) @dceejay |  | ||||||
|  - Watch: Update Watch node to use node-watch module (#3559 #3569) @knolleary |  | ||||||
|  - WebSocket: call done after ws disconnects (#3531) @Steve-Mcl |  | ||||||
|  |  | ||||||
| #### Older Releases |  | ||||||
|  |  | ||||||
| Change logs for older releases are available on GitHub: https://github.com/node-red/node-red/releases |  | ||||||
|   | |||||||
| @@ -9,16 +9,13 @@ We welcome contributions, but request you follow these guidelines. | |||||||
|  |  | ||||||
| This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | ||||||
| By participating, you are expected to uphold this code. Please report unacceptable | By participating, you are expected to uphold this code. Please report unacceptable | ||||||
| behavior to the project's core team at team@nodered.org. | behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core). | ||||||
|  |  | ||||||
| ## Raising issues | ## Raising issues | ||||||
|  |  | ||||||
| Please raise any bug reports on the relevant project's issue tracker. Be sure to | Please raise any bug reports on the relevant project's issue tracker. Be sure to | ||||||
| search the list to see if your issue has already been raised. | search the list to see if your issue has already been raised. | ||||||
|  |  | ||||||
| If your issue is more of a question on how to do something with Node-RED, please |  | ||||||
| consider using the [community forum](https://discourse.nodered.org/). |  | ||||||
|  |  | ||||||
| A good bug report is one that make it easy for us to understand what you were | A good bug report is one that make it easy for us to understand what you were | ||||||
| trying to do and what went wrong. | trying to do and what went wrong. | ||||||
|  |  | ||||||
| @@ -29,34 +26,33 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog. | |||||||
| At a minimum, please include: | At a minimum, please include: | ||||||
|  |  | ||||||
|  - Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly. |  - Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly. | ||||||
|  - Version of Node.js - what does `node -v` say? |  - Version of node.js - what does `node -v` say? | ||||||
|  |  | ||||||
| ## Feature requests | ## Feature requests | ||||||
|  |  | ||||||
| For feature requests, please raise them on the [forum](https://discourse.nodered.org). | For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||||
|  |  | ||||||
| ## Pull-Requests | ## Pull-Requests | ||||||
|  |  | ||||||
| If you want to raise a pull-request with a new feature, or a refactoring | If you want to raise a pull-request with a new feature, or a refactoring | ||||||
| of existing code, please come and discuss it with us first. We prefer to | of existing code, it may well get rejected if you haven't discussed it on | ||||||
| do it that way to make sure your time and effort is well spent on something | the [mailing list](https://groups.google.com/forum/#!forum/node-red) first. | ||||||
| that fits with our goals. |  | ||||||
|  |  | ||||||
| If you've got a bug-fix or similar for us, then you are most welcome to | ### Contributor License Agreement | ||||||
| get it raised - just make sure you link back to the issue it's fixing and |  | ||||||
| try to include some tests! |  | ||||||
|  |  | ||||||
| All contributors need to sign the OpenJS Foundation's Contributor License Agreement. | In order for us to accept pull-requests, the contributor must first complete | ||||||
| It is an online process and quick to do. If you raise a pull-request without | a Contributor License Agreement (CLA). This clarifies the intellectual | ||||||
| having signed the CLA, you will be prompted to do so automatically. | property license granted with any contribution. It is for your protection as a | ||||||
|  | Contributor as well as the protection of IBM and its customers; it does not | ||||||
|  | change your rights to use your own Contributions for any other purpose. | ||||||
|  |  | ||||||
| ### Code Branches | You can download the CLAs here: | ||||||
|  |  | ||||||
| When raising a PR for a fix or a new feature, it is important to target the right branch. |  - [individual](http://nodered.org/cla/node-red-cla-individual.pdf) | ||||||
|  |  - [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf) | ||||||
|  |  | ||||||
|  - `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch. | If you are an IBMer, please contact us directly as the contribution process is | ||||||
|  - `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream. | slightly different. | ||||||
|  - `dev` - this is the branch for new feature development targeting the next milestone release. |  | ||||||
|  |  | ||||||
| ### Coding standards | ### Coding standards | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										570
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,5 @@ | |||||||
| /** | /** | ||||||
|  * Copyright JS Foundation and other contributors, http://js.foundation |  * Copyright 2013, 2016 IBM Corp. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @@ -15,35 +15,17 @@ | |||||||
|  **/ |  **/ | ||||||
|  |  | ||||||
| var path = require("path"); | var path = require("path"); | ||||||
| var fs = require("fs-extra"); |  | ||||||
| var sass = require("sass"); |  | ||||||
|  |  | ||||||
| module.exports = function(grunt) { | module.exports = function(grunt) { | ||||||
|  |  | ||||||
|     var nodemonArgs = ["-V"]; |     var nodemonArgs = ["-v"]; | ||||||
|     var flowFile = grunt.option('flowFile'); |     var flowFile = grunt.option('flowFile'); | ||||||
|     if (flowFile) { |     if (flowFile) { | ||||||
|         nodemonArgs.push(flowFile); |         nodemonArgs.push(flowFile); | ||||||
|         process.env.NODE_RED_ENABLE_PROJECTS=false; |  | ||||||
|     } |  | ||||||
|     var userDir = grunt.option('userDir'); |  | ||||||
|     if (userDir) { |  | ||||||
|         nodemonArgs.push("-u"); |  | ||||||
|         nodemonArgs.push(userDir); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     var browserstack = grunt.option('browserstack'); |  | ||||||
|     if (browserstack) { |  | ||||||
|         process.env.BROWSERSTACK = true; |  | ||||||
|     } |  | ||||||
|     var nonHeadless = grunt.option('non-headless'); |  | ||||||
|     if (nonHeadless) { |  | ||||||
|         process.env.NODE_RED_NON_HEADLESS = true; |  | ||||||
|     } |  | ||||||
|     const pkg = grunt.file.readJSON('package.json'); |  | ||||||
|     process.env.NODE_RED_PACKAGE_VERSION = pkg.version; |  | ||||||
|     grunt.initConfig({ |     grunt.initConfig({ | ||||||
|         pkg: pkg, |         pkg: grunt.file.readJSON('package.json'), | ||||||
|         paths: { |         paths: { | ||||||
|             dist: ".dist" |             dist: ".dist" | ||||||
|         }, |         }, | ||||||
| @@ -55,29 +37,10 @@ module.exports = function(grunt) { | |||||||
|                 ui: 'bdd', |                 ui: 'bdd', | ||||||
|                 reporter: 'spec' |                 reporter: 'spec' | ||||||
|             }, |             }, | ||||||
|             all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] }, |             all: { src: ['test/**/*_spec.js'] }, | ||||||
|             core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]}, |             core: { src: ["test/_spec.js","test/red/**/*_spec.js"]}, | ||||||
|             nodes: { src: ["test/nodes/**/*_spec.js"]} |             nodes: { src: ["test/nodes/**/*_spec.js"]} | ||||||
|         }, |         }, | ||||||
|         webdriver: { |  | ||||||
|             all: { |  | ||||||
|                 configFile: 'test/editor/wdio.conf.js' |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         nyc: { |  | ||||||
|             options: { |  | ||||||
|                 cwd: '.', |  | ||||||
|                 include: ['packages/node_modules/**'], |  | ||||||
|                 excludeNodeModules: false, |  | ||||||
|                 exclude: ['packages/node_modules/@node-red/editor-client/**'], |  | ||||||
|                 reporter: ['lcov', 'html','text-summary'], |  | ||||||
|                 reportDir: 'coverage', |  | ||||||
|                 all: true |  | ||||||
|             }, |  | ||||||
|             all:   { cmd: false, args: ['grunt', 'simplemocha:all'] }, |  | ||||||
|             core:  { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] }, |  | ||||||
|             nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] } |  | ||||||
|         }, |  | ||||||
|         jshint: { |         jshint: { | ||||||
|             options: { |             options: { | ||||||
|                 jshintrc:true |                 jshintrc:true | ||||||
| @@ -92,20 +55,22 @@ module.exports = function(grunt) { | |||||||
|                 //"loopfunc": true, // allow functions to be defined in loops |                 //"loopfunc": true, // allow functions to be defined in loops | ||||||
|                 //"sub": true       // don't warn that foo['bar'] should be written as foo.bar |                 //"sub": true       // don't warn that foo['bar'] should be written as foo.bar | ||||||
|             }, |             }, | ||||||
|             // all: [ |             all: [ | ||||||
|             //     'Gruntfile.js', |                 'Gruntfile.js', | ||||||
|             //     'red.js', |                 'red.js', | ||||||
|             //     'packages/**/*.js' |                 'red/**/*.js', | ||||||
|             // ], |                 'nodes/core/*/*.js', | ||||||
|             // core: { |                 'editor/js/**/*.js' | ||||||
|             //     files: { |             ], | ||||||
|             //         src: [ |             core: { | ||||||
|             //             'Gruntfile.js', |                 files: { | ||||||
|             //             'red.js', |                     src: [ | ||||||
|             //             'packages/**/*.js', |                         'Gruntfile.js', | ||||||
|             //         ] |                         'red.js', | ||||||
|             //     } |                         'red/**/*.js' | ||||||
|             // }, |                     ] | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|             nodes: { |             nodes: { | ||||||
|                 files: { |                 files: { | ||||||
|                     src: [ 'nodes/core/*/*.js' ] |                     src: [ 'nodes/core/*/*.js' ] | ||||||
| @@ -113,7 +78,7 @@ module.exports = function(grunt) { | |||||||
|             }, |             }, | ||||||
|             editor: { |             editor: { | ||||||
|                 files: { |                 files: { | ||||||
|                     src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ] |                     src: [ 'editor/js/**/*.js' ] | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             tests: { |             tests: { | ||||||
| @@ -133,110 +98,60 @@ module.exports = function(grunt) { | |||||||
|                 src: [ |                 src: [ | ||||||
|                     // Ensure editor source files are concatenated in |                     // Ensure editor source files are concatenated in | ||||||
|                     // the right order |                     // the right order | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/polyfills.js", |                     "editor/js/main.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js", |                     "editor/js/events.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/red.js", |                     "editor/js/i18n.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/events.js", |                     "editor/js/settings.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/hooks.js", |                     "editor/js/user.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/i18n.js", |                     "editor/js/comms.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/settings.js", |                     "editor/js/bidi/bidi.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/user.js", |                     "editor/js/bidi/base-text-dir.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/comms.js", |                     "editor/js/bidi/format.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/runtime.js", |                     "editor/js/bidi/numeric-shaping.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/text/bidi.js", |                     "editor/js/ui/state.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/text/format.js", |                     "editor/js/nodes.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/state.js", |                     "editor/js/history.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/plugins.js", |                     "editor/js/validators.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/nodes.js", |                     "editor/js/ui/common/editableList.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/font-awesome.js", |                     "editor/js/ui/common/menu.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/history.js", |                     "editor/js/ui/common/popover.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/validators.js", |                     "editor/js/ui/common/searchBox.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/utils.js", |                     "editor/js/ui/common/tabs.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js", |                     "editor/js/ui/common/typedInput.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", |                     "editor/js/ui/deploy.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js", |                     "editor/js/ui/keyboard.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js", |                     "editor/js/ui/workspaces.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js", |                     "editor/js/ui/view.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js", |                     "editor/js/ui/sidebar.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js", |                     "editor/js/ui/palette.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js", |                     "editor/js/ui/tab-info.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", |                     "editor/js/ui/tab-config.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", |                     "editor/js/ui/palette-editor.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", |                     "editor/js/ui/editor.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", |                     "editor/js/ui/tray.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", |                     "editor/js/ui/clipboard.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", |                     "editor/js/ui/library.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", |                     "editor/js/ui/notifications.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", |                     "editor/js/ui/search.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", |                     "editor/js/ui/subflow.js", | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js", |                     "editor/js/ui/touch/radialMenu.js" | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editor.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tray.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/library.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/search.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/group.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js" |  | ||||||
|                 ], |                 ], | ||||||
|                 dest: "packages/node_modules/@node-red/editor-client/public/red/red.js" |                 dest: "public/red/red.js" | ||||||
|             }, |             }, | ||||||
|             vendor: { |             vendor: { | ||||||
|                 files: { |                 files: { | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [ |                     "public/vendor/vendor.js": [ | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js", |                         "editor/vendor/jquery/js/jquery-1.11.3.min.js", | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js", |                         "editor/vendor/bootstrap/js/bootstrap.min.js", | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js", |                         "editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js", | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js", |                         "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js", | ||||||
|                         "node_modules/marked/marked.min.js", |                         "editor/vendor/marked/marked.min.js", | ||||||
|                         "node_modules/dompurify/dist/purify.min.js", |                         "editor/vendor/d3/d3.v3.min.js", | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js", |                         "editor/vendor/i18next/i18next.min.js" | ||||||
|                         "node_modules/i18next/i18next.min.js", |  | ||||||
|                         "node_modules/i18next-http-backend/i18nextHttpBackend.min.js", |  | ||||||
|                         "node_modules/jquery-i18next/jquery-i18next.min.js", |  | ||||||
|                         "node_modules/jsonata/jsonata-es5.min.js", |  | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js", |  | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js", |  | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js" |  | ||||||
|                     ], |                     ], | ||||||
|                     // "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [ |                     "public/vendor/vendor.css": [ | ||||||
|                     //     // TODO: resolve relative resource paths in |                         // TODO: resolve relative resource paths in | ||||||
|                     //     //       bootstrap/FA/jquery |                         //       bootstrap/FA/jquery | ||||||
|                     // ], |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor/ace/worker-jsonata.js": [ |  | ||||||
|                         "node_modules/jsonata/jsonata-es5.min.js", |  | ||||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/worker-jsonata.js" |  | ||||||
|                     ], |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor/mermaid/mermaid.min.js": [ |  | ||||||
|                         "node_modules/mermaid/dist/mermaid.min.js" |  | ||||||
|                     ] |                     ] | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -244,62 +159,54 @@ module.exports = function(grunt) { | |||||||
|         uglify: { |         uglify: { | ||||||
|             build: { |             build: { | ||||||
|                 files: { |                 files: { | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js', |                     'public/red/red.min.js': 'public/red/red.js' | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js', |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js', |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js' |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         sass: { |         sass: { | ||||||
|             build: { |             build: { | ||||||
|                 options: { |                 options: { | ||||||
|                     implementation: sass, |  | ||||||
|                     outputStyle: 'compressed' |                     outputStyle: 'compressed' | ||||||
|                 }, |                 }, | ||||||
|                 files: [{ |                 files: [{ | ||||||
|                     dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css', |                     dest: 'public/red/style.min.css', | ||||||
|                     src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss' |                     src: 'editor/sass/style.scss' | ||||||
|  |                 }, | ||||||
|  |                 { | ||||||
|  |                     dest: 'public/vendor/bootstrap/css/bootstrap.min.css', | ||||||
|  |                     src: 'editor/vendor/bootstrap/css/bootstrap.css' | ||||||
|                 }] |                 }] | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         jsonlint: { |         jsonlint: { | ||||||
|             messages: { |             messages: { | ||||||
|                 src: [ |                 src: [ | ||||||
|                     'packages/node_modules/@node-red/nodes/locales/**/*.json', |                     'nodes/core/locales/en-US/messages.json', | ||||||
|                     'packages/node_modules/@node-red/editor-client/locales/**/*.json', |                     'red/api/locales/en-US/editor.json', | ||||||
|                     'packages/node_modules/@node-red/runtime/locales/**/*.json' |                     'red/runtime/locales/en-US/runtime.json' | ||||||
|                 ] |  | ||||||
|             }, |  | ||||||
|             keymaps: { |  | ||||||
|                 src: [ |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/js/keymap.json' |  | ||||||
|                 ] |                 ] | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         attachCopyright: { |         attachCopyright: { | ||||||
|             js: { |             js: { | ||||||
|                 src: [ |                 src: [ | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/red/red.min.js', |                     'public/red/red.min.js' | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/red/main.min.js' |  | ||||||
|                 ] |                 ] | ||||||
|             }, |             }, | ||||||
|             css: { |             css: { | ||||||
|                 src: [ |                 src: [ | ||||||
|                     'packages/node_modules/@node-red/editor-client/public/red/style.min.css' |                     'public/red/style.min.css' | ||||||
|                 ] |                 ] | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         clean: { |         clean: { | ||||||
|             build: { |             build: { | ||||||
|                 src: [ |                 src: [ | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/red", |                     "public/red", | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/index.html", |                     "public/index.html", | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/favicon.ico", |                     "public/favicon.ico", | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/icons", |                     "public/icons", | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor", |                     "public/vendor" | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node", |  | ||||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node-red", |  | ||||||
|                 ] |                 ] | ||||||
|             }, |             }, | ||||||
|             release: { |             release: { | ||||||
| @@ -311,36 +218,24 @@ module.exports = function(grunt) { | |||||||
|         watch: { |         watch: { | ||||||
|             js: { |             js: { | ||||||
|                 files: [ |                 files: [ | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/js/**/*.js' |                     'editor/js/**/*.js' | ||||||
|                 ], |                 ], | ||||||
|                 tasks: ['copy:build','concat',/*'uglify',*/ 'attachCopyright:js'] |                 tasks: ['concat','uglify','attachCopyright:js'] | ||||||
|             }, |             }, | ||||||
|             sass: { |             sass: { | ||||||
|                 files: [ |                 files: [ | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss' |                     'editor/sass/**/*.scss' | ||||||
|                 ], |                 ], | ||||||
|                 tasks: ['sass','attachCopyright:css'] |                 tasks: ['sass','attachCopyright:css'] | ||||||
|             }, |             }, | ||||||
|             json: { |             json: { | ||||||
|                 files: [ |                 files: [ | ||||||
|                     'packages/node_modules/@node-red/nodes/locales/**/*.json', |                     'nodes/core/locales/en-US/messages.json', | ||||||
|                     'packages/node_modules/@node-red/editor-client/locales/**/*.json', |                     'red/api/locales/en-US/editor.json', | ||||||
|                     'packages/node_modules/@node-red/runtime/locales/**/*.json' |                     'red/runtime/locales/en-US/runtime.json' | ||||||
|                 ], |                 ], | ||||||
|                 tasks: ['jsonlint:messages'] |                 tasks: ['jsonlint:messages'] | ||||||
|             }, |             }, | ||||||
|             keymaps: { |  | ||||||
|                 files: [ |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/js/keymap.json' |  | ||||||
|                 ], |  | ||||||
|                 tasks: ['jsonlint:keymaps','copy:build'] |  | ||||||
|             }, |  | ||||||
|             tours: { |  | ||||||
|                 files: [ |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/tours/**/*.js' |  | ||||||
|                 ], |  | ||||||
|                 tasks: ['copy:build'] |  | ||||||
|             }, |  | ||||||
|             misc: { |             misc: { | ||||||
|                 files: [ |                 files: [ | ||||||
|                     'CHANGELOG.md' |                     'CHANGELOG.md' | ||||||
| @@ -352,13 +247,12 @@ module.exports = function(grunt) { | |||||||
|         nodemon: { |         nodemon: { | ||||||
|             /* uses .nodemonignore */ |             /* uses .nodemonignore */ | ||||||
|             dev: { |             dev: { | ||||||
|                 script: 'packages/node_modules/node-red/red.js', |                 script: 'red.js', | ||||||
|                 options: { |                 options: { | ||||||
|                     args: nodemonArgs, |                     args: nodemonArgs, | ||||||
|                     ext: 'js,html,json', |                     ext: 'js,html,json', | ||||||
|                     watch: [ |                     watch: [ | ||||||
|                         'packages/node_modules', |                         'red','nodes' | ||||||
|                         '!packages/node_modules/@node-red/editor-client' |  | ||||||
|                     ] |                     ] | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -375,77 +269,62 @@ module.exports = function(grunt) { | |||||||
|  |  | ||||||
|         copy: { |         copy: { | ||||||
|             build: { |             build: { | ||||||
|                 files:[ |                 files:[{ | ||||||
|                     { |                     cwd: 'editor/images', | ||||||
|                         src: 'packages/node_modules/@node-red/editor-client/src/js/main.js', |  | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js' |  | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json', |  | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json' |  | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/images', |  | ||||||
|                     src: '**', |                     src: '**', | ||||||
|                     expand: true, |                     expand: true, | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/images/' |                     dest: 'public/red/images/' | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/vendor', |                     cwd: 'editor/vendor', | ||||||
|                     src: [ |                     src: [ | ||||||
|                         'ace/**', |                         'ace/**', | ||||||
|                             'jquery/css/base/**', |                         //'bootstrap/css/**', | ||||||
|                             'font-awesome/**', |                         'bootstrap/img/**', | ||||||
|                             'monaco/dist/**', |                         'jquery/css/**', | ||||||
|                             'monaco/types/extraLibs.js', |                         'font-awesome/**' | ||||||
|                             'monaco/style.css', |  | ||||||
|                             'monaco/monaco-bootstrap.js' |  | ||||||
|                     ], |                     ], | ||||||
|                     expand: true, |                     expand: true, | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' |                     dest: 'public/vendor/' | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src', |                     cwd: 'editor/icons', | ||||||
|                         src: [ |  | ||||||
|                             'types/node/**/*.ts', |  | ||||||
|                             'types/node-red/*.ts', |  | ||||||
|                         ], |  | ||||||
|                         expand: true, |  | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/' |  | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/icons', |  | ||||||
|                     src: '**', |                     src: '**', | ||||||
|                     expand: true, |                     expand: true, | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/icons/' |                     dest: 'public/icons/' | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     expand: true, |                     expand: true, | ||||||
|                         src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'], |                     src: ['editor/index.html','editor/favicon.ico'], | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/', |                     dest: 'public/', | ||||||
|                     flatten: true |                     flatten: true | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     src: 'CHANGELOG.md', |                     src: 'CHANGELOG.md', | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/about' |                     dest: 'public/red/about' | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         src: 'CHANGELOG.md', |  | ||||||
|                         dest: 'packages/node_modules/node-red/' |  | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/', |  | ||||||
|                         src: '**', |  | ||||||
|                         expand: true, |  | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/' |  | ||||||
|                     }, |  | ||||||
|                     { |  | ||||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/tours', |  | ||||||
|                         src: '**', |  | ||||||
|                         expand: true, |  | ||||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/' |  | ||||||
|                 } |                 } | ||||||
|                 ] |                 ] | ||||||
|  |             }, | ||||||
|  |             release: { | ||||||
|  |                 files: [{ | ||||||
|  |                     mode: true, | ||||||
|  |                     expand: true, | ||||||
|  |                     src: [ | ||||||
|  |                         '*.md', | ||||||
|  |                         'LICENSE', | ||||||
|  |                         'package.json', | ||||||
|  |                         'settings.js', | ||||||
|  |                         'red.js', | ||||||
|  |                         'lib/.gitignore', | ||||||
|  |                         'nodes/*.demo', | ||||||
|  |                         'nodes/core/**', | ||||||
|  |                         'red/**', | ||||||
|  |                         'public/**', | ||||||
|  |                         'editor/templates/**', | ||||||
|  |                         'bin/**' | ||||||
|  |                     ], | ||||||
|  |                     dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>') | ||||||
|  |                 }] | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         chmod: { |         chmod: { | ||||||
| @@ -453,96 +332,20 @@ module.exports = function(grunt) { | |||||||
|                 mode: '755' |                 mode: '755' | ||||||
|             }, |             }, | ||||||
|             release: { |             release: { | ||||||
|  |                 // Target-specific file/dir lists and/or options go here. | ||||||
|                 src: [ |                 src: [ | ||||||
|                     "packages/node_modules/@node-red/nodes/core/hardware/nrgpio", |                     path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*') | ||||||
|                     "packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/node-red-*sh" |  | ||||||
|                 ] |                 ] | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         'npm-command': { |  | ||||||
|             options: { |  | ||||||
|                 cmd: "pack", |  | ||||||
|                 cwd: "<%= paths.dist %>/modules" |  | ||||||
|             }, |  | ||||||
|             'node-red': { options: { args: [__dirname+'/packages/node_modules/node-red'] } }, |  | ||||||
|             '@node-red/editor-api': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-api'] } }, |  | ||||||
|             '@node-red/editor-client': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-client'] } }, |  | ||||||
|             '@node-red/nodes': { options: { args: [__dirname+'/packages/node_modules/@node-red/nodes'] } }, |  | ||||||
|             '@node-red/registry': { options: { args: [__dirname+'/packages/node_modules/@node-red/registry'] } }, |  | ||||||
|             '@node-red/runtime': { options: { args: [__dirname+'/packages/node_modules/@node-red/runtime'] } }, |  | ||||||
|             '@node-red/util': { options: { args: [__dirname+'/packages/node_modules/@node-red/util'] } } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         }, |  | ||||||
|         mkdir: { |  | ||||||
|             release: { |  | ||||||
|                 options: { |  | ||||||
|                     create: ['<%= paths.dist %>/modules'] |  | ||||||
|                 }, |  | ||||||
|             }, |  | ||||||
|         }, |  | ||||||
|         compress: { |         compress: { | ||||||
|             release: { |             release: { | ||||||
|                 options: { |                 options: { | ||||||
|                     archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip' |                     archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip' | ||||||
|                 }, |                 }, | ||||||
|                 expand: true, |                 expand: true, | ||||||
|                 cwd: 'packages/node_modules/', |                 cwd: '<%= paths.dist %>/', | ||||||
|                 src: [ |                 src: ['node-red-<%= pkg.version %>/**'] | ||||||
|                     '**', |  | ||||||
|                     '!@node-red/editor-client/src/**' |  | ||||||
|                 ] |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         jsdoc : { |  | ||||||
|             modules: { |  | ||||||
|                 src: [ |  | ||||||
|                     'API.md', |  | ||||||
|                     'packages/node_modules/node-red/lib/red.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/index.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/api/*.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/events.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/hooks.js', |  | ||||||
|                     'packages/node_modules/@node-red/util/**/*.js', |  | ||||||
|                     'packages/node_modules/@node-red/editor-api/lib/index.js', |  | ||||||
|                     'packages/node_modules/@node-red/editor-api/lib/auth/index.js', |  | ||||||
|                     'packages/node_modules/@node-red/registry/lib/index.js' |  | ||||||
|                 ], |  | ||||||
|                 options: { |  | ||||||
|                     destination: 'docs', |  | ||||||
|                     configure: './jsdoc.json', |  | ||||||
|                     fred: "hi there" |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             _editor: { |  | ||||||
|                 src: [ |  | ||||||
|                     'packages/node_modules/@node-red/editor-client/src/js' |  | ||||||
|                     ], |  | ||||||
|                 options: { |  | ||||||
|                     destination: 'packages/node_modules/@node-red/editor-client/docs', |  | ||||||
|                     configure: './jsdoc.json' |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         }, |  | ||||||
|         jsdoc2md: { |  | ||||||
|             runtimeAPI: { |  | ||||||
|                 options: { |  | ||||||
|                     separators: true |  | ||||||
|                 }, |  | ||||||
|                 src: [ |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/index.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/api/*.js', |  | ||||||
|                     'packages/node_modules/@node-red/runtime/lib/events.js' |  | ||||||
|                 ], |  | ||||||
|                 dest: 'packages/node_modules/@node-red/runtime/docs/api.md' |  | ||||||
|             }, |  | ||||||
|             nodeREDUtil: { |  | ||||||
|                 options: { |  | ||||||
|                     separators: true |  | ||||||
|                 }, |  | ||||||
|                 src: 'packages/node_modules/@node-red/util/**/*.js', |  | ||||||
|                 dest: 'packages/node_modules/@node-red/util/docs/api.md' |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| @@ -555,42 +358,16 @@ module.exports = function(grunt) { | |||||||
|     grunt.loadNpmTasks('grunt-contrib-watch'); |     grunt.loadNpmTasks('grunt-contrib-watch'); | ||||||
|     grunt.loadNpmTasks('grunt-concurrent'); |     grunt.loadNpmTasks('grunt-concurrent'); | ||||||
|     grunt.loadNpmTasks('grunt-sass'); |     grunt.loadNpmTasks('grunt-sass'); | ||||||
|  |     grunt.loadNpmTasks('grunt-nodemon'); | ||||||
|     grunt.loadNpmTasks('grunt-contrib-compress'); |     grunt.loadNpmTasks('grunt-contrib-compress'); | ||||||
|     grunt.loadNpmTasks('grunt-contrib-copy'); |     grunt.loadNpmTasks('grunt-contrib-copy'); | ||||||
|     grunt.loadNpmTasks('grunt-chmod'); |     grunt.loadNpmTasks('grunt-chmod'); | ||||||
|     grunt.loadNpmTasks('grunt-jsonlint'); |     grunt.loadNpmTasks('grunt-jsonlint'); | ||||||
|     if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { |  | ||||||
|         grunt.loadNpmTasks('grunt-webdriver'); |  | ||||||
|     } |  | ||||||
|     grunt.loadNpmTasks('grunt-jsdoc'); |  | ||||||
|     grunt.loadNpmTasks('grunt-jsdoc-to-markdown'); |  | ||||||
|     grunt.loadNpmTasks('grunt-npm-command'); |  | ||||||
|     grunt.loadNpmTasks('grunt-mkdir'); |  | ||||||
|     grunt.loadNpmTasks('grunt-simple-nyc'); |  | ||||||
|  |  | ||||||
|     grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () { |  | ||||||
|         const nodemon = require('nodemon'); |  | ||||||
|         this.async(); |  | ||||||
|         const options = this.options(); |  | ||||||
|         options.script = this.data.script; |  | ||||||
|         let callback; |  | ||||||
|         if (options.callback) { |  | ||||||
|             callback = options.callback; |  | ||||||
|             delete options.callback; |  | ||||||
|         } else { |  | ||||||
|             callback = function(nodemonApp) { |  | ||||||
|                 nodemonApp.on('log', function (event) { |  | ||||||
|                     console.log(event.colour); |  | ||||||
|                 }); |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|         callback(nodemon(options)); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     grunt.registerMultiTask('attachCopyright', function() { |     grunt.registerMultiTask('attachCopyright', function() { | ||||||
|         var files = this.data.src; |         var files = this.data.src; | ||||||
|         var copyright = "/**\n"+ |         var copyright = "/**\n"+ | ||||||
|             " * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+ |             " * Copyright 2013, 2015 IBM Corp.\n"+ | ||||||
|             " *\n"+ |             " *\n"+ | ||||||
|             " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+ |             " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+ | ||||||
|             " * you may not use this file except in compliance with the License.\n"+ |             " * you may not use this file except in compliance with the License.\n"+ | ||||||
| @@ -627,38 +404,6 @@ module.exports = function(grunt) { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     grunt.registerTask('verifyPackageDependencies', function() { |  | ||||||
|         var done = this.async(); |  | ||||||
|         var verifyDependencies = require("./scripts/verify-package-dependencies.js"); |  | ||||||
|         verifyDependencies().then(function(failures) { |  | ||||||
|             if (failures.length > 0) { |  | ||||||
|                 failures.forEach(f => grunt.log.error(f)); |  | ||||||
|                 grunt.fail.fatal("Failed to verify package dependencies"); |  | ||||||
|             } |  | ||||||
|             done(); |  | ||||||
|         }); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     grunt.registerTask('verifyUiTestDependencies', function() { |  | ||||||
|         if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { |  | ||||||
|             grunt.fail.fatal('You need to install the UI test dependencies first.\nUse the script in "scripts/install-ui-test-dependencies.sh"'); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|     grunt.registerTask('generatePublishScript', |  | ||||||
|         'Generates a script to publish build output to npm', |  | ||||||
|             function () { |  | ||||||
|                 const done = this.async(); |  | ||||||
|                 const generatePublishScript = require("./scripts/generate-publish-script.js"); |  | ||||||
|                 generatePublishScript().then(function(output) { |  | ||||||
|                     grunt.log.writeln(output); |  | ||||||
|  |  | ||||||
|                     const filePath = path.join(grunt.config.get('paths.dist'),"modules","publish.sh"); |  | ||||||
|                     grunt.file.write(filePath,output); |  | ||||||
|  |  | ||||||
|                     done(); |  | ||||||
|                 }); |  | ||||||
|             }); |  | ||||||
|     grunt.registerTask('setDevEnv', |     grunt.registerTask('setDevEnv', | ||||||
|         'Sets NODE_ENV=development so non-minified assets are used', |         'Sets NODE_ENV=development so non-minified assets are used', | ||||||
|             function () { |             function () { | ||||||
| @@ -667,42 +412,23 @@ module.exports = function(grunt) { | |||||||
|  |  | ||||||
|     grunt.registerTask('default', |     grunt.registerTask('default', | ||||||
|         'Builds editor content then runs code style checks and unit tests on all components', |         'Builds editor content then runs code style checks and unit tests on all components', | ||||||
|         ['build','verifyPackageDependencies','jshint:editor','nyc:all']); |         ['build','test-core','test-editor','test-nodes']); | ||||||
|  |  | ||||||
|     grunt.registerTask('no-coverage', |  | ||||||
|         'Builds editor content then runs code style checks and unit tests on all components without code coverage', |  | ||||||
|         ['build','verifyPackageDependencies','jshint:editor','simplemocha:all']); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     grunt.registerTask('test-core', |     grunt.registerTask('test-core', | ||||||
|         'Runs code style check and unit tests on core runtime code', |         'Runs code style check and unit tests on core runtime code', | ||||||
|         ['build','nyc:core']); |         ['jshint:core','simplemocha:core']); | ||||||
|  |  | ||||||
|     grunt.registerTask('test-editor', |     grunt.registerTask('test-editor', | ||||||
|         'Runs code style check on editor code', |         'Runs code style check on editor code', | ||||||
|         ['jshint:editor']); |         ['jshint:editor']); | ||||||
|  |  | ||||||
|     if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { |  | ||||||
|         grunt.registerTask('test-ui', |  | ||||||
|             'Builds editor content then runs unit tests on editor ui', |  | ||||||
|             ['verifyUiTestDependencies']); |  | ||||||
|     } else { |  | ||||||
|         grunt.registerTask('test-ui', |  | ||||||
|             'Builds editor content then runs unit tests on editor ui', |  | ||||||
|             ['verifyUiTestDependencies','build','jshint:editor','webdriver:all']); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     grunt.registerTask('test-nodes', |     grunt.registerTask('test-nodes', | ||||||
|         'Runs unit tests on core nodes', |         'Runs unit tests on core nodes', | ||||||
|         ['build','nyc:nodes']); |         ['simplemocha:nodes']); | ||||||
|  |  | ||||||
|     grunt.registerTask('build', |     grunt.registerTask('build', | ||||||
|         'Builds editor content', |         'Builds editor content', | ||||||
|         ['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']); |         ['clean:build','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy:build','attachCopyright']); | ||||||
|  |  | ||||||
|     grunt.registerTask('build-dev', |  | ||||||
|         'Developer mode: build dev version', |  | ||||||
|         ['clean:build','concat:build','concat:vendor','copy:build','sass:build','setDevEnv']); |  | ||||||
|  |  | ||||||
|     grunt.registerTask('dev', |     grunt.registerTask('dev', | ||||||
|         'Developer mode: run node-red, watch for source changes and build/restart', |         'Developer mode: run node-red, watch for source changes and build/restart', | ||||||
| @@ -710,18 +436,6 @@ module.exports = function(grunt) { | |||||||
|  |  | ||||||
|     grunt.registerTask('release', |     grunt.registerTask('release', | ||||||
|         'Create distribution zip file', |         'Create distribution zip file', | ||||||
|         ['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules','generatePublishScript']); |         ['build','clean:release','copy:release','chmod:release','compress:release']); | ||||||
|  |  | ||||||
|     grunt.registerTask('pack-modules', |  | ||||||
|         'Create module pack files for release', |  | ||||||
|         ['mkdir:release','npm-command']); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     grunt.registerTask('coverage', |  | ||||||
|         'Run Istanbul code test coverage task', |  | ||||||
|         ['build','nyc:all']); |  | ||||||
|  |  | ||||||
|     grunt.registerTask('docs', |  | ||||||
|         'Generates API documentation', |  | ||||||
|         ['jsdoc']); |  | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						| @@ -1,4 +1,3 @@ | |||||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org/ |  | ||||||
|  |  | ||||||
|                                  Apache License |                                  Apache License | ||||||
|                            Version 2.0, January 2004 |                            Version 2.0, January 2004 | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,27 +1,29 @@ | |||||||
| # Node-RED | # Node-RED | ||||||
|  |  | ||||||
| https://nodered.org | http://nodered.org | ||||||
|  |  | ||||||
| [](https://github.com/node-red/node-red/actions?query=branch%3Amaster) | [](https://travis-ci.org/node-red/node-red) | ||||||
|  | [](https://coveralls.io/r/node-red/node-red?branch=master) | ||||||
|  |  | ||||||
| Low-code programming for event-driven applications. | A visual tool for wiring the Internet of Things. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Quick Start | ## Quick Start | ||||||
|  |  | ||||||
| Check out https://nodered.org/docs/getting-started/ for full instructions on getting | Check out http://nodered.org/docs/getting-started/ for full instructions on getting | ||||||
| started. | started. | ||||||
|  |  | ||||||
| 1. `sudo npm install -g --unsafe-perm node-red` | 1. `sudo npm install -g node-red` | ||||||
| 2. `node-red` | 2. `node-red` | ||||||
| 3. Open <http://localhost:1880> | 3. Open <http://localhost:1880> | ||||||
|  |  | ||||||
| ## Getting Help | ## Getting Help | ||||||
|  |  | ||||||
| More documentation can be found [here](https://nodered.org/docs). | More documentation can be found [here](http://nodered.org/docs). | ||||||
|  |  | ||||||
| For further help, or general discussion, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack). | For further help, or general discussion, please use the | ||||||
|  | [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||||
|  |  | ||||||
| ## Developers | ## Developers | ||||||
|  |  | ||||||
| @@ -43,6 +45,9 @@ If you want to run the latest code from git, here's how to get started: | |||||||
| 4. Run | 4. Run | ||||||
|  |  | ||||||
|         npm start |         npm start | ||||||
|  |    or | ||||||
|  |  | ||||||
|  |         node red.js | ||||||
|  |  | ||||||
| ## Contributing | ## Contributing | ||||||
|  |  | ||||||
| @@ -51,19 +56,17 @@ Before raising a pull-request, please read our | |||||||
|  |  | ||||||
| This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | ||||||
|  By participating, you are expected to uphold this code. Please report unacceptable |  By participating, you are expected to uphold this code. Please report unacceptable | ||||||
|  behavior to any of the project's core team at team@nodered.org. |  behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core). | ||||||
|  |  | ||||||
| ## Authors | ## Authors | ||||||
|  |  | ||||||
| Node-RED is a project of the [OpenJS Foundation](http://openjsf.org). | Node-RED is a creation of [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/). | ||||||
|  |  | ||||||
| It is maintained by: |  | ||||||
|  |  | ||||||
| * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | ||||||
| * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | ||||||
|  * And many others... |  | ||||||
|  |  | ||||||
|  | For more open-source projects from IBM, head over [here](http://ibm.github.io). | ||||||
|  |  | ||||||
| ## Copyright and license | ## Copyright and license | ||||||
|  |  | ||||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE). | Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE). | ||||||
|   | |||||||
| @@ -1,5 +0,0 @@ | |||||||
| # Security Policy |  | ||||||
|  |  | ||||||
| ## Reporting a Vulnerability |  | ||||||
|  |  | ||||||
| Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly. |  | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| #!/bin/sh | #!/bin/bash | ||||||
| # | # | ||||||
| # Copyright JS Foundation and other contributors, http://js.foundation | # Copyright 2015 IBM Corp. | ||||||
| # | # | ||||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | # Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| # you may not use this file except in compliance with the License. | # you may not use this file except in compliance with the License. | ||||||
| @@ -29,16 +29,15 @@ do | |||||||
| done | done | ||||||
| 
 | 
 | ||||||
| # Find the real location of this script | # Find the real location of this script | ||||||
| CURRENT_PATH=$(pwd) | CURRENT_PATH=`pwd` | ||||||
| SCRIPT_PATH=$(readlink -f "$0") | SCRIPT_PATH="${BASH_SOURCE[0]}"; | ||||||
| while [ -h "${SCRIPT_PATH}" ]; do | while([ -h "${SCRIPT_PATH}" ]); do | ||||||
|     cd "$(dirname "${SCRIPT_PATH}")" || exit 1 |     cd "`dirname "${SCRIPT_PATH}"`" | ||||||
|     P=$(basename "${SCRIPT_PATH}") |     SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")"; | ||||||
|     SCRIPT_PATH=$(readlink "${P}") |  | ||||||
| done | done | ||||||
| cd "$(dirname "${SCRIPT_PATH}")" > /dev/null || exit 1 | cd "`dirname "${SCRIPT_PATH}"`" > /dev/null | ||||||
| SCRIPT_PATH=$(pwd) | SCRIPT_PATH="`pwd`"; | ||||||
| cd "$CURRENT_PATH" || exit 1 | cd $CURRENT_PATH | ||||||
| 
 | 
 | ||||||
| # Run Node-RED | # Run Node-RED | ||||||
| exec /usr/bin/env node ${OPTIONS} ${SCRIPT_PATH}/../red.js ${ARGS} | exec /usr/bin/env node $OPTIONS $SCRIPT_PATH/../red.js $ARGS | ||||||
| Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/alert.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 308 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/arduino.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 603 B | 
| Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/bluetooth.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/bridge-dash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 508 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/bridge.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 575 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/cog.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 493 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/comment.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 601 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/db.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 459 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/debug.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 218 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/envelope.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 324 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/feed.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 378 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/file.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 255 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/function.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 457 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/hash.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 502 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/inject.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 449 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/join.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 253 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/leveldb.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/light.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 639 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/link-out.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 402 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/mongodb.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 414 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/mouse.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 671 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/node-changed.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 386 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/node-error.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 386 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/parser-csv.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 413 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/parser-html.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 393 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/parser-json.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 467 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/parser-xml.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 393 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/range.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 360 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/redis.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 736 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/rpi.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 482 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/serial.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 273 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/split.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 256 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/subflow.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 439 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/swap.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 592 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/switch.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 509 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/template.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 488 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/timer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 628 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/trigger.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 258 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/twitter.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 404 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/watch.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 591 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/icons/white-globe.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 707 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-flows-o.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 291 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-flows.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 386 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-full-o.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 289 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-full.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 368 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-nodes-o.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 290 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/deploy-nodes.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 392 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/grip.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 192 B | 
| Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB | 
| Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB | 
| Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/node-red.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1019 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/pw_maze_white.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 600 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/subflow_tab.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 410 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/09.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 638 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/az.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 546 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/bool.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 646 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/json.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 588 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/re.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 502 B | 
							
								
								
									
										67
									
								
								editor/js/bidi/base-text-dir.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,67 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | RED.bidi.baseTextDir = (function() { | ||||||
|  |     var LRE = "\u202A", | ||||||
|  |         RLE = "\u202B", | ||||||
|  |         PDF = "\u202C"; | ||||||
|  |  | ||||||
|  |     function _isRTLValue(stringValue) { | ||||||
|  |         var length = stringValue.length; | ||||||
|  |         for (var i = 0 ; i < length; i++) { | ||||||
|  |             if (_isBidiChar(stringValue.charCodeAt(i))) { | ||||||
|  |                 return true; | ||||||
|  |             } else if (_isLatinChar(stringValue.charCodeAt(i))) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |            return RED.bidi.isMirroringEnabled(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function _isBidiChar(c) { | ||||||
|  |         return (c >= 0x05d0 && c <= 0x05ff)|| | ||||||
|  |                (c >= 0x0600 && c <= 0x065f)|| | ||||||
|  |                (c >= 0x066a && c <= 0x06ef)|| | ||||||
|  |                (c >= 0x06fa && c <= 0x07ff)|| | ||||||
|  |                (c >= 0xfb1d && c <= 0xfdff)|| | ||||||
|  |                (c >= 0xfe70 && c <= 0xfefc); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function _isLatinChar(c) { | ||||||
|  |         return (c > 64 && c < 91) || (c > 96 && c < 123); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Enforces the text direction of a given string by adding | ||||||
|  |      * UCC (Unicode Control Characters) | ||||||
|  |      * @param value - the string | ||||||
|  |      */ | ||||||
|  |     function _enforceTextDirectionWithUCC(value) { | ||||||
|  |         if (value) { | ||||||
|  |             var dir = RED.bidi.resolveBaseTextDir(value); | ||||||
|  |             if (dir == "ltr") { | ||||||
|  |                return LRE + value + PDF; | ||||||
|  |             } else if (dir == "rtl") { | ||||||
|  |                return RLE + value + PDF; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         enforceTextDirectionWithUCC: _enforceTextDirectionWithUCC, | ||||||
|  |         isRTLValue : _isRTLValue | ||||||
|  |     } | ||||||
|  | })(); | ||||||
							
								
								
									
										189
									
								
								editor/js/bidi/bidi.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,189 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | RED.bidi = (function() { | ||||||
|  |     var textDir = ""; | ||||||
|  |     var shaperType = ""; | ||||||
|  |     var calendarType = ""; | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Indicates the type of bidi-support | ||||||
|  |      * BTD : Base text direction | ||||||
|  |      * NS : Numeric Shaping | ||||||
|  |      * CALENDAR : National Calendar | ||||||
|  |      * STT_ATTACH : Structure Text Support, it is using to call attach function located at format.js | ||||||
|  |      * STT_GETHTML : STT_ATTACH : Structure Text Support, it is using to call getHtml function located at format.js | ||||||
|  |      */ | ||||||
|  |     var _flags = { | ||||||
|  |         BTD: 1, | ||||||
|  |         NS: 2, | ||||||
|  |         CALENDAR: 4, | ||||||
|  |         STT_ATTACH: 8, | ||||||
|  |         STT_GETHTML: 16 | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |    /** | ||||||
|  |     * Reverse component position when mirroring is enabled | ||||||
|  |     */ | ||||||
|  |     var _componentPos = {}; | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Check if browser language is RTL language | ||||||
|  |      */ | ||||||
|  |     function _isMirroringEnabled() { | ||||||
|  |         var isRTLLang = new RegExp("^(ar|he)").test(navigator.language); | ||||||
|  |         if (isRTLLang) { | ||||||
|  |         	_componentPos.left = "right"; | ||||||
|  |         	_componentPos.right = "left"; | ||||||
|  |             return true; | ||||||
|  |         } else { | ||||||
|  |         	_componentPos.left = "left"; | ||||||
|  |         	_componentPos.right = "right"; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     | ||||||
|  |     /** | ||||||
|  |      * @param val - the numeric shaping type: None , National or contextual | ||||||
|  |      */ | ||||||
|  |     function _setNumericShapingType(val) { | ||||||
|  |         shaperType = val; | ||||||
|  |         _refreshView(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Sets the national calendar preference | ||||||
|  |      * @param val - the calendar type hijri, hebrew or gregorian | ||||||
|  |      */ | ||||||
|  |     function _setCalendarType(val) { | ||||||
|  |         calendarType = val; | ||||||
|  |     } | ||||||
|  |        | ||||||
|  |     /** | ||||||
|  |      * Formats the date according to the current selected calendar  | ||||||
|  |      * @param date - the date object to be formatted | ||||||
|  |      */ | ||||||
|  |     function _getGlobalizedDate(date) { | ||||||
|  |         var options = {}; | ||||||
|  |         var lang = navigator.language; | ||||||
|  |         if (calendarType === "hijri") { | ||||||
|  |             options = lang + "-u-ca-islamic"; | ||||||
|  |         } else if (calendarType === "hebrew") { | ||||||
|  |             options = lang + "-u-ca-hebrew"; | ||||||
|  |         } | ||||||
|  |         return date.toLocaleString(options); | ||||||
|  |     }   | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Sets the text direction preference | ||||||
|  |      * @param dir - the text direction preference | ||||||
|  |      */ | ||||||
|  |     function _setTextDirection(dir) { | ||||||
|  |         textDir = dir; | ||||||
|  |         _refreshView(); | ||||||
|  |         _enforceTextDirectionOnPage(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Enforces the text direction for all the spans with style bidiAware under | ||||||
|  |      * workspace or sidebar div | ||||||
|  |      */ | ||||||
|  |     function _enforceTextDirectionOnPage() { | ||||||
|  |         $("#workspace").find('span.bidiAware').each(function() { | ||||||
|  |             $(this).attr("dir", _resolveBaseTextDir($(this).html())); | ||||||
|  |         }); | ||||||
|  |         $("#sidebar").find('span.bidiAware').each(function() { | ||||||
|  |             $(this).attr("dir", _resolveBaseTextDir($(this).text())); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Determines the text direction of a given string. | ||||||
|  |      * @param value - the string | ||||||
|  |      */ | ||||||
|  |     function _resolveBaseTextDir(value) { | ||||||
|  |         if (textDir == "auto") { | ||||||
|  |             if (RED.bidi.baseTextDir.isRTLValue(value)) { | ||||||
|  |                 return "rtl"; | ||||||
|  |             } else { | ||||||
|  |                 return "ltr"; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             return textDir; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Adds event listeners to the Input to ensure its text-direction attribute | ||||||
|  |      * is properly set based on its content. | ||||||
|  |      * @param input - the input field | ||||||
|  |      */ | ||||||
|  |     function _prepareInput(input) { | ||||||
|  |         input.on("keyup",_onInputChange).on("paste",_onInputChange).on("cut",_onInputChange); | ||||||
|  |         // Set the initial text direction | ||||||
|  |         _onInputChange.call(input); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     function _onInputChange() { | ||||||
|  |         $(this).attr("dir", _resolveBaseTextDir($(this).val())); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Refreshes the view whenever changing the user preferences | ||||||
|  |      */ | ||||||
|  |     function _refreshView() { | ||||||
|  |         RED.nodes.eachNode(function(n) { n.dirty = true;}); | ||||||
|  |         RED.view.redraw(); | ||||||
|  |         RED.palette.refresh(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Applying bidi support for these features: base-text-dir ,Numeric-shaping or both, STT ,Calendar which is controlled by the flag value. | ||||||
|  |      * @param value- the string to apply the bidi-support on it. | ||||||
|  |      * @param flag - indicates the type of bidi-support (Base-text-dir ,Numeric-shaping or both, STT , Calendar) | ||||||
|  |      * @param type - could be one of filepath, url, email | ||||||
|  |      * @param args - pass additional arguments to the handler. generally null. | ||||||
|  |      */ | ||||||
|  |     function _applyBidiSupport(value, flag, type, args) { | ||||||
|  |         switch (flag) { | ||||||
|  |         case 0: | ||||||
|  |             value = RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value); | ||||||
|  |             return RED.bidi.numericShaping.shape(value, shaperType, textDir); | ||||||
|  |         case 1: | ||||||
|  |             return RED.bidi.baseTextDir.enforceTextDirectionWithUCC(value); | ||||||
|  |         case 2: | ||||||
|  |             return RED.bidi.numericShaping.shape(value, shaperType, textDir); | ||||||
|  |         case 4: | ||||||
|  |             return _getGlobalizedDate(value); | ||||||
|  |         case 8: | ||||||
|  |             return RED.bidi.format.attach(value, type, args, _isMirroringEnabled(), navigator.language); | ||||||
|  |         case 16: | ||||||
|  |             return RED.bidi.format.getHtml(value, type, args, _isMirroringEnabled(), navigator.language); | ||||||
|  |         default: | ||||||
|  |             return value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         isMirroringEnabled: _isMirroringEnabled, | ||||||
|  |         setNumericShapingType : _setNumericShapingType, | ||||||
|  |         setCalendarType: _setCalendarType, | ||||||
|  |         setTextDirection : _setTextDirection, | ||||||
|  |         applyBidiSupport : _applyBidiSupport, | ||||||
|  |         resolveBaseTextDir : _resolveBaseTextDir, | ||||||
|  |         prepareInput: _prepareInput, | ||||||
|  |         flags: _flags, | ||||||
|  |         componentPos: _componentPos | ||||||
|  |     } | ||||||
|  |  })(); | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /** | /** | ||||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 |  * Copyright 2016 IBM Corp. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @@ -13,7 +13,8 @@ | |||||||
|  * See the License for the specific language governing permissions and |  * See the License for the specific language governing permissions and | ||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  **/ |  **/ | ||||||
| RED.text.format = (function() { | 
 | ||||||
|  | RED.bidi.format = (function() { | ||||||
| 
 | 
 | ||||||
|     var TextSegment = (function() { |     var TextSegment = (function() { | ||||||
|         var TextSegment = function (obj) { |         var TextSegment = function (obj) { | ||||||
| @@ -758,7 +759,7 @@ RED.text.format = (function() { | |||||||
|                 { |                 { | ||||||
|                         guiDir: isRtl ? "rtl" : "ltr", |                         guiDir: isRtl ? "rtl" : "ltr", | ||||||
|                         dir: "ltr", |                         dir: "ltr", | ||||||
|                         points: "/\\:." |                         points: "/\\:.&<>" | ||||||
|                 }; |                 }; | ||||||
|                 if (!parseOnly) { |                 if (!parseOnly) { | ||||||
|                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); |                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); | ||||||
| @@ -876,7 +877,7 @@ RED.text.format = (function() { | |||||||
|                 { |                 { | ||||||
|                         guiDir: isRtl ? "rtl" : "ltr", |                         guiDir: isRtl ? "rtl" : "ltr", | ||||||
|                         dir: "ltr", |                         dir: "ltr", | ||||||
|                         points: ":?#/@.[]=" |                         points: ":?#/@.[]=&<>" | ||||||
|                 }; |                 }; | ||||||
|                 if (!parseOnly) { |                 if (!parseOnly) { | ||||||
|                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); |                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); | ||||||
| @@ -1304,7 +1305,19 @@ RED.text.format = (function() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return { |     return { | ||||||
|         /*! | 	    /** | ||||||
|  |         * Returns the string representation of a given structured text | ||||||
|  |         * @param text - the structured text | ||||||
|  |         * @param type - could be one of filepath, url, email | ||||||
|  |         * @param args - pass additional arguments to the handler. generally null. | ||||||
|  |         * @param isRtl - indicates if the GUI is mirrored | ||||||
|  |         * @param locale - the browser locale | ||||||
|  |         */ | ||||||
|  |         getString:  function (text, type, args, isRtl, locale) { | ||||||
|  |             return getHandler(type).format(text, args, isRtl, false, locale); | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         /** | ||||||
|         * Returns the HTML representation of a given structured text |         * Returns the HTML representation of a given structured text | ||||||
|         * @param text - the structured text |         * @param text - the structured text | ||||||
|         * @param type - could be one of filepath, url, email |         * @param type - could be one of filepath, url, email | ||||||
| @@ -1315,7 +1328,7 @@ RED.text.format = (function() { | |||||||
|         getHtml: function (text, type, args, isRtl, locale) { |         getHtml: function (text, type, args, isRtl, locale) { | ||||||
|             return getHandler(type).format(text, args, isRtl, true, locale); |             return getHandler(type).format(text, args, isRtl, true, locale); | ||||||
|         }, |         }, | ||||||
|         /*! |         /** | ||||||
|         * Handle Structured text correct display for a given HTML element. |         * Handle Structured text correct display for a given HTML element. | ||||||
|         * @param element - the element  : should be of type div contenteditable=true |         * @param element - the element  : should be of type div contenteditable=true | ||||||
|         * @param type - could be one of filepath, url, email |         * @param type - could be one of filepath, url, email | ||||||
							
								
								
									
										86
									
								
								editor/js/bidi/numeric-shaping.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,86 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | RED.bidi.numericShaping = (function(){ | ||||||
|  |     var regex = /([0-9])|([\u0660-\u0669])|([\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE]+)|([^0-9\u0660-\u0669\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FF\u0750-\u077F\u08A0-\u08E3\u200F\u202B\u202E\u2067\uFB50-\uFD3D\uFD40-\uFDCF\uFDF0-\uFDFC\uFDFE-\uFDFF\uFE70-\uFEFE\u0600-\u0607\u0609-\u060A\u060C\u060E-\u061A\u064B-\u066C\u0670\u06D6-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u08E4-\u08FF\uFD3E-\uFD3F\uFDD0-\uFDEF\uFDFD\uFEFF\u0000-\u0040\u005B-\u0060\u007B-\u007F\u0080-\u00A9\u00AB-\u00B4\u00B6-\u00B9\u00BB-\u00BF\u00D7\u00F7\u02B9-\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u02FF\u2070\u2074-\u207E\u2080-\u208E\u2100-\u2101\u2103-\u2106\u2108-\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A-\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189\uA720-\uA721\uA788\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE]+)/g; | ||||||
|  | 	 | ||||||
|  |     /** | ||||||
|  |      * Converts the digits in the text to European or Arabic digits According to | ||||||
|  |      * the shaperType & the textDir. | ||||||
|  |      */ | ||||||
|  |     function _shape(text, shaperType, textDir) { | ||||||
|  |         text = text.toString(); | ||||||
|  |         if(textDir === "auto"){ | ||||||
|  |         	textDir = RED.bidi.resolveBaseTextDir(text); | ||||||
|  |         } | ||||||
|  |         if (!text) {  | ||||||
|  |             return text; | ||||||
|  |         } | ||||||
|  |         switch (shaperType) { | ||||||
|  |             case "defaultNumeral": | ||||||
|  |                 return _shapeEuropean(text); | ||||||
|  |             case "national": | ||||||
|  |                 return _shapeArabic(text); | ||||||
|  |             case "contextual": | ||||||
|  |                 return _shapeContextual(text, textDir === "rtl" ? 2 : 1); | ||||||
|  |             default: | ||||||
|  |                 return text; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Converts the digits in the text to European digits. | ||||||
|  |      */ | ||||||
|  |     function _shapeEuropean(text) { | ||||||
|  |         return text.replace(/[\u0660-\u0669]/g, function(c) { | ||||||
|  |             return c.charCodeAt(0) - 1632; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     /** | ||||||
|  |      * Converts the digits in the text to Arabic digits. | ||||||
|  |      */ | ||||||
|  |     function _shapeArabic(text) { | ||||||
|  |         return text.replace(/[0-9]/g, function(c) { | ||||||
|  |             return String.fromCharCode(parseInt(c) + 1632); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 	 | ||||||
|  |     /** | ||||||
|  |      * Converts the digits in the text to European or Arabic digits | ||||||
|  |      * According to the type of the preceding strong character. | ||||||
|  |      * @param context:The current effective context. | ||||||
|  |      * Allowed values: | ||||||
|  |      * '1': European context | ||||||
|  |      * '2': Arabic context | ||||||
|  |      */ | ||||||
|  |     function _shapeContextual(text, context) { | ||||||
|  |         return text.replace(regex, function(match, latinDigit, arabicDigit, strongArabic, strongLatin){ | ||||||
|  |             if (latinDigit) { | ||||||
|  |                 return (context === 2) ? String.fromCharCode(parseInt(latinDigit) + 1632) : latinDigit; | ||||||
|  |             } else if (arabicDigit) { | ||||||
|  |                 return (context === 1) ? arabicDigit.charCodeAt(0) - 1632 : arabicDigit; | ||||||
|  |             } else if (strongArabic) { | ||||||
|  |                 context = 2; | ||||||
|  |             } else if (strongLatin) { | ||||||
|  |                 context = 1; | ||||||
|  |             } | ||||||
|  |             return match; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     return { | ||||||
|  |         shape: _shape | ||||||
|  |     } | ||||||
|  | })(); | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /** | /** | ||||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 |  * Copyright 2014, 2016 IBM Corp. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @@ -28,15 +28,6 @@ RED.comms = (function() { | |||||||
| 
 | 
 | ||||||
|     function connectWS() { |     function connectWS() { | ||||||
|         active = true; |         active = true; | ||||||
|         var wspath; |  | ||||||
| 
 |  | ||||||
|         if (RED.settings.apiRootUrl) { |  | ||||||
|             var m = /^(https?):\/\/(.*)$/.exec(RED.settings.apiRootUrl); |  | ||||||
|             if (m) { |  | ||||||
|                 console.log(m); |  | ||||||
|                 wspath = "ws"+(m[1]==="https"?"s":"")+"://"+m[2]+"comms"; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|         var path = location.hostname; |         var path = location.hostname; | ||||||
|         var port = location.port; |         var port = location.port; | ||||||
|         if (port.length !== 0) { |         if (port.length !== 0) { | ||||||
| @@ -44,8 +35,7 @@ RED.comms = (function() { | |||||||
|         } |         } | ||||||
|         path = path+document.location.pathname; |         path = path+document.location.pathname; | ||||||
|         path = path+(path.slice(-1) == "/"?"":"/")+"comms"; |         path = path+(path.slice(-1) == "/"?"":"/")+"comms"; | ||||||
|             wspath = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path; |         path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path; | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         var auth_tokens = RED.settings.get("auth-tokens"); |         var auth_tokens = RED.settings.get("auth-tokens"); | ||||||
|         pendingAuth = (auth_tokens!=null); |         pendingAuth = (auth_tokens!=null); | ||||||
| @@ -58,7 +48,7 @@ RED.comms = (function() { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         ws = new WebSocket(wspath); |         ws = new WebSocket(path); | ||||||
|         ws.onopen = function() { |         ws.onopen = function() { | ||||||
|             reconnectAttempts = 0; |             reconnectAttempts = 0; | ||||||
|             if (errornotification) { |             if (errornotification) { | ||||||
| @@ -74,31 +64,19 @@ RED.comms = (function() { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         ws.onmessage = function(event) { |         ws.onmessage = function(event) { | ||||||
|             var message = JSON.parse(event.data); |             var msg = JSON.parse(event.data); | ||||||
|             if (message.auth) { |             if (pendingAuth && msg.auth) { | ||||||
|                 if (pendingAuth) { |                 if (msg.auth === "ok") { | ||||||
|                     if (message.auth === "ok") { |  | ||||||
|                     pendingAuth = false; |                     pendingAuth = false; | ||||||
|                     completeConnection(); |                     completeConnection(); | ||||||
|                     } else if (message.auth === "fail") { |                 } else if (msg.auth === "fail") { | ||||||
|                     // anything else is an error...
 |                     // anything else is an error...
 | ||||||
|                     active = false; |                     active = false; | ||||||
|                     RED.user.login({updateMenu:true},function() { |                     RED.user.login({updateMenu:true},function() { | ||||||
|                         connectWS(); |                         connectWS(); | ||||||
|                     }) |                     }) | ||||||
|                 } |                 } | ||||||
|                 } else if (message.auth === "fail") { |             } else if (msg.topic) { | ||||||
|                     // Our current session has expired
 |  | ||||||
|                     active = false; |  | ||||||
|                     RED.user.login({updateMenu:true},function() { |  | ||||||
|                         connectWS(); |  | ||||||
|                     }) |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 // Otherwise, 'message' is an array of actual comms messages
 |  | ||||||
|                 for (var m = 0; m < message.length; m++) { |  | ||||||
|                     var msg = message[m]; |  | ||||||
|                     if (msg.topic) { |  | ||||||
|                 for (var t in subscriptions) { |                 for (var t in subscriptions) { | ||||||
|                     if (subscriptions.hasOwnProperty(t)) { |                     if (subscriptions.hasOwnProperty(t)) { | ||||||
|                         var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); |                         var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); | ||||||
| @@ -113,8 +91,6 @@ RED.comms = (function() { | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         }; |         }; | ||||||
|         ws.onclose = function() { |         ws.onclose = function() { | ||||||
|             if (!active) { |             if (!active) { | ||||||
| @@ -142,10 +118,10 @@ RED.comms = (function() { | |||||||
|                         connectWS(); |                         connectWS(); | ||||||
|                     } else { |                     } else { | ||||||
|                         var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>'; |                         var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>'; | ||||||
|                         errornotification.update(msg,{silent:true}); |                         errornotification.update(msg); | ||||||
|                         $(errornotification).find("a").on("click", function(e) { |                         $(errornotification).find("a").click(function(e) { | ||||||
|                             e.preventDefault(); |                             e.preventDefault(); | ||||||
|                             errornotification.update(RED._("notification.errors.lostConnection"),{silent:true}); |                             errornotification.update(RED._("notification.errors.lostConnection")); | ||||||
|                             clearInterval(connectCountdownTimer); |                             clearInterval(connectCountdownTimer); | ||||||
|                             connectWS(); |                             connectWS(); | ||||||
|                         }) |                         }) | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /** | /** | ||||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 |  * Copyright 2015, 2016 IBM Corp. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @@ -32,19 +32,14 @@ | |||||||
|              } |              } | ||||||
|          } |          } | ||||||
|      } |      } | ||||||
|      function emit() { |      function emit(evt,arg) { | ||||||
|          var evt = arguments[0] |  | ||||||
|          var args = Array.prototype.slice.call(arguments,1); |  | ||||||
|          if (RED.events.DEBUG) { |  | ||||||
|              console.warn(evt,args); |  | ||||||
|          } |  | ||||||
|          if (handlers[evt]) { |          if (handlers[evt]) { | ||||||
|              for (var i=0;i<handlers[evt].length;i++) { |              for (var i=0;i<handlers[evt].length;i++) { | ||||||
|                  try { |                  try { | ||||||
|                      handlers[evt][i].apply(null, args); |                      handlers[evt][i](arg); | ||||||
|                  } catch(err) { |                  } catch(err) { | ||||||
|                      console.warn("RED.events.emit error: ["+evt+"] "+(err.toString())); |                      console.log("RED.events.emit error: ["+evt+"] "+(err.toString())); | ||||||
|                      console.warn(err); |                      console.log(err); | ||||||
|                  } |                  } | ||||||
|              } |              } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										297
									
								
								editor/js/history.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,297 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2013, 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | RED.history = (function() { | ||||||
|  |     var undo_history = []; | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         //TODO: this function is a placeholder until there is a 'save' event that can be listened to | ||||||
|  |         markAllDirty: function() { | ||||||
|  |             for (var i=0;i<undo_history.length;i++) { | ||||||
|  |                 undo_history[i].dirty = true; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         list: function() { | ||||||
|  |             return undo_history | ||||||
|  |         }, | ||||||
|  |         depth: function() { | ||||||
|  |             return undo_history.length; | ||||||
|  |         }, | ||||||
|  |         push: function(ev) { | ||||||
|  |             undo_history.push(ev); | ||||||
|  |         }, | ||||||
|  |         pop: function() { | ||||||
|  |             var ev = undo_history.pop(); | ||||||
|  |             var i; | ||||||
|  |             var node; | ||||||
|  |             var subflow; | ||||||
|  |             var modifiedTabs = {}; | ||||||
|  |             if (ev) { | ||||||
|  |                 if (ev.t == 'add') { | ||||||
|  |                     if (ev.nodes) { | ||||||
|  |                         for (i=0;i<ev.nodes.length;i++) { | ||||||
|  |                             node = RED.nodes.node(ev.nodes[i]); | ||||||
|  |                             if (node.z) { | ||||||
|  |                                 modifiedTabs[node.z] = true; | ||||||
|  |                             } | ||||||
|  |                             RED.nodes.remove(ev.nodes[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.links) { | ||||||
|  |                         for (i=0;i<ev.links.length;i++) { | ||||||
|  |                             RED.nodes.removeLink(ev.links[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.workspaces) { | ||||||
|  |                         for (i=0;i<ev.workspaces.length;i++) { | ||||||
|  |                             RED.nodes.removeWorkspace(ev.workspaces[i].id); | ||||||
|  |                             RED.workspaces.remove(ev.workspaces[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflows) { | ||||||
|  |                         for (i=0;i<ev.subflows.length;i++) { | ||||||
|  |                             RED.nodes.removeSubflow(ev.subflows[i]); | ||||||
|  |                             RED.workspaces.remove(ev.subflows[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflow) { | ||||||
|  |                         if (ev.subflow.instances) { | ||||||
|  |                             ev.subflow.instances.forEach(function(n) { | ||||||
|  |                                 var node = RED.nodes.node(n.id); | ||||||
|  |                                 if (node) { | ||||||
|  |                                     node.changed = n.changed; | ||||||
|  |                                     node.dirty = true; | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  |                         if (ev.subflow.hasOwnProperty('changed')) { | ||||||
|  |                             subflow = RED.nodes.subflow(ev.subflow.id); | ||||||
|  |                             if (subflow) { | ||||||
|  |                                 subflow.changed = ev.subflow.changed; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.removedLinks) { | ||||||
|  |                         for (i=0;i<ev.removedLinks.length;i++) { | ||||||
|  |                             RED.nodes.addLink(ev.removedLinks[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 } else if (ev.t == "delete") { | ||||||
|  |                     if (ev.workspaces) { | ||||||
|  |                         for (i=0;i<ev.workspaces.length;i++) { | ||||||
|  |                             RED.nodes.addWorkspace(ev.workspaces[i]); | ||||||
|  |                             RED.workspaces.add(ev.workspaces[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflow && ev.subflow.subflow) { | ||||||
|  |                         RED.nodes.addSubflow(ev.subflow.subflow); | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflowInputs && ev.subflowInputs.length > 0) { | ||||||
|  |                         subflow = RED.nodes.subflow(ev.subflowInputs[0].z); | ||||||
|  |                         subflow.in.push(ev.subflowInputs[0]); | ||||||
|  |                         subflow.in[0].dirty = true; | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflowOutputs && ev.subflowOutputs.length > 0) { | ||||||
|  |                         subflow = RED.nodes.subflow(ev.subflowOutputs[0].z); | ||||||
|  |                         ev.subflowOutputs.sort(function(a,b) { return a.i-b.i}); | ||||||
|  |                         for (i=0;i<ev.subflowOutputs.length;i++) { | ||||||
|  |                             var output = ev.subflowOutputs[i]; | ||||||
|  |                             subflow.out.splice(output.i,0,output); | ||||||
|  |                             for (var j=output.i+1;j<subflow.out.length;j++) { | ||||||
|  |                                 subflow.out[j].i++; | ||||||
|  |                                 subflow.out[j].dirty = true; | ||||||
|  |                             } | ||||||
|  |                             RED.nodes.eachLink(function(l) { | ||||||
|  |                                 if (l.source.type == "subflow:"+subflow.id) { | ||||||
|  |                                     if (l.sourcePort >= output.i) { | ||||||
|  |                                         l.sourcePort++; | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflow && ev.subflow.hasOwnProperty('instances')) { | ||||||
|  |                         ev.subflow.instances.forEach(function(n) { | ||||||
|  |                             var node = RED.nodes.node(n.id); | ||||||
|  |                             if (node) { | ||||||
|  |                                 node.changed = n.changed; | ||||||
|  |                                 node.dirty = true; | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |                     if (subflow) { | ||||||
|  |                         RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) { | ||||||
|  |                             n.inputs = subflow.in.length; | ||||||
|  |                             n.outputs = subflow.out.length; | ||||||
|  |                             while (n.outputs > n.ports.length) { | ||||||
|  |                                 n.ports.push(n.ports.length); | ||||||
|  |                             } | ||||||
|  |                             n.resize = true; | ||||||
|  |                             n.dirty = true; | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |                     if (ev.nodes) { | ||||||
|  |                         for (i=0;i<ev.nodes.length;i++) { | ||||||
|  |                             RED.nodes.add(ev.nodes[i]); | ||||||
|  |                             modifiedTabs[ev.nodes[i].z] = true; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.links) { | ||||||
|  |                         for (i=0;i<ev.links.length;i++) { | ||||||
|  |                             RED.nodes.addLink(ev.links[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.changes) { | ||||||
|  |                         for (i in ev.changes) { | ||||||
|  |                             if (ev.changes.hasOwnProperty(i)) { | ||||||
|  |                                 node = RED.nodes.node(i); | ||||||
|  |                                 if (node) { | ||||||
|  |                                     for (var d in ev.changes[i]) { | ||||||
|  |                                         if (ev.changes[i].hasOwnProperty(d)) { | ||||||
|  |                                             node[d] = ev.changes[i][d]; | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                     node.dirty = true; | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 } else if (ev.t == "move") { | ||||||
|  |                     for (i=0;i<ev.nodes.length;i++) { | ||||||
|  |                         var n = ev.nodes[i]; | ||||||
|  |                         n.n.x = n.ox; | ||||||
|  |                         n.n.y = n.oy; | ||||||
|  |                         n.n.dirty = true; | ||||||
|  |                         n.n.changed = n.changed; | ||||||
|  |                     } | ||||||
|  |                     // A move could have caused a link splice | ||||||
|  |                     if (ev.links) { | ||||||
|  |                         for (i=0;i<ev.links.length;i++) { | ||||||
|  |                             RED.nodes.removeLink(ev.links[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.removedLinks) { | ||||||
|  |                         for (i=0;i<ev.removedLinks.length;i++) { | ||||||
|  |                             RED.nodes.addLink(ev.removedLinks[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else if (ev.t == "edit") { | ||||||
|  |                     for (i in ev.changes) { | ||||||
|  |                         if (ev.changes.hasOwnProperty(i)) { | ||||||
|  |                             if (ev.node._def.defaults[i].type) { | ||||||
|  |                                 // This is a config node property | ||||||
|  |                                 var currentConfigNode = RED.nodes.node(ev.node[i]); | ||||||
|  |                                 if (currentConfigNode) { | ||||||
|  |                                     currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); | ||||||
|  |                                 } | ||||||
|  |                                 var newConfigNode = RED.nodes.node(ev.changes[i]); | ||||||
|  |                                 if (newConfigNode) { | ||||||
|  |                                     newConfigNode.users.push(ev.node); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             ev.node[i] = ev.changes[i]; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.subflow) { | ||||||
|  |                         if (ev.subflow.hasOwnProperty('inputCount')) { | ||||||
|  |                             if (ev.node.in.length > ev.subflow.inputCount) { | ||||||
|  |                                 ev.node.in.splice(ev.subflow.inputCount); | ||||||
|  |                             } else if (ev.subflow.inputs.length > 0) { | ||||||
|  |                                 ev.node.in = ev.node.in.concat(ev.subflow.inputs); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         if (ev.subflow.hasOwnProperty('outputCount')) { | ||||||
|  |                             if (ev.node.out.length > ev.subflow.outputCount) { | ||||||
|  |                                 ev.node.out.splice(ev.subflow.outputCount); | ||||||
|  |                             } else if (ev.subflow.outputs.length > 0) { | ||||||
|  |                                 ev.node.out = ev.node.out.concat(ev.subflow.outputs); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         if (ev.subflow.hasOwnProperty('instances')) { | ||||||
|  |                             ev.subflow.instances.forEach(function(n) { | ||||||
|  |                                 var node = RED.nodes.node(n.id); | ||||||
|  |                                 if (node) { | ||||||
|  |                                     node.changed = n.changed; | ||||||
|  |                                     node.dirty = true; | ||||||
|  |                                 } | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  |                         RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) { | ||||||
|  |                             n.inputs = ev.node.in.length; | ||||||
|  |                             n.outputs = ev.node.out.length; | ||||||
|  |                             RED.editor.updateNodeProperties(n); | ||||||
|  |                         }); | ||||||
|  |                     } else { | ||||||
|  |                         RED.editor.updateNodeProperties(ev.node); | ||||||
|  |                         RED.editor.validateNode(ev.node); | ||||||
|  |                     } | ||||||
|  |                     if (ev.links) { | ||||||
|  |                         for (i=0;i<ev.links.length;i++) { | ||||||
|  |                             RED.nodes.addLink(ev.links[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     ev.node.dirty = true; | ||||||
|  |                     ev.node.changed = ev.changed; | ||||||
|  |                 } else if (ev.t == "createSubflow") { | ||||||
|  |                     if (ev.nodes) { | ||||||
|  |                         RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) { | ||||||
|  |                             n.z = ev.activeWorkspace; | ||||||
|  |                             n.dirty = true; | ||||||
|  |                         }); | ||||||
|  |                         for (i=0;i<ev.nodes.length;i++) { | ||||||
|  |                             RED.nodes.remove(ev.nodes[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (ev.links) { | ||||||
|  |                         for (i=0;i<ev.links.length;i++) { | ||||||
|  |                             RED.nodes.removeLink(ev.links[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     RED.nodes.removeSubflow(ev.subflow.subflow); | ||||||
|  |                     RED.workspaces.remove(ev.subflow.subflow); | ||||||
|  |  | ||||||
|  |                     if (ev.removedLinks) { | ||||||
|  |                         for (i=0;i<ev.removedLinks.length;i++) { | ||||||
|  |                             RED.nodes.addLink(ev.removedLinks[i]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } else if (ev.t == "reorder") { | ||||||
|  |                     if (ev.order) { | ||||||
|  |                         RED.workspaces.order(ev.order); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Object.keys(modifiedTabs).forEach(function(id) { | ||||||
|  |                     var subflow = RED.nodes.subflow(id); | ||||||
|  |                     if (subflow) { | ||||||
|  |                         RED.editor.validateNode(subflow); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 RED.nodes.dirty(ev.dirty); | ||||||
|  |                 RED.view.redraw(true); | ||||||
|  |                 RED.palette.refresh(); | ||||||
|  |                 RED.workspaces.refresh(); | ||||||
|  |                 RED.sidebar.config.refresh(); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         peek: function() { | ||||||
|  |             return undo_history[undo_history.length-1]; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | })(); | ||||||
							
								
								
									
										43
									
								
								editor/js/i18n.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,43 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2013, 2015 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  |  | ||||||
|  | RED.i18n = (function() { | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         init: function(done) { | ||||||
|  |             i18n.init({ | ||||||
|  |                 resGetPath: 'locales/__ns__', | ||||||
|  |                 dynamicLoad: false, | ||||||
|  |                 load:'current', | ||||||
|  |                 ns: { | ||||||
|  |                     namespaces: ["editor","node-red"], | ||||||
|  |                     defaultNs: "editor" | ||||||
|  |                 }, | ||||||
|  |                 fallbackLng: ['en-US'], | ||||||
|  |                 useCookie: false | ||||||
|  |             },function() { | ||||||
|  |                 done(); | ||||||
|  |             }); | ||||||
|  |             RED["_"] = function() { | ||||||
|  |                 return i18n.t.apply(null,arguments); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         }, | ||||||
|  |         loadCatalog: function(namespace,done) { | ||||||
|  |             i18n.loadNamespace(namespace,done); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | })(); | ||||||
							
								
								
									
										283
									
								
								editor/js/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,283 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2013, 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | var RED = (function() { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     function loadNodeList() { | ||||||
|  |         $.ajax({ | ||||||
|  |             headers: { | ||||||
|  |                 "Accept":"application/json" | ||||||
|  |             }, | ||||||
|  |             cache: false, | ||||||
|  |             url: 'nodes', | ||||||
|  |             success: function(data) { | ||||||
|  |                 RED.nodes.setNodeList(data); | ||||||
|  |  | ||||||
|  |                 var nsCount = 0; | ||||||
|  |                 for (var i=0;i<data.length;i++) { | ||||||
|  |                     var ns = data[i]; | ||||||
|  |                     if (ns.module != "node-red") { | ||||||
|  |                         nsCount++; | ||||||
|  |                         RED.i18n.loadCatalog(ns.id, function() { | ||||||
|  |                             nsCount--; | ||||||
|  |                             if (nsCount === 0) { | ||||||
|  |                                 loadNodes(); | ||||||
|  |                             } | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (nsCount === 0) { | ||||||
|  |                     loadNodes(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function loadNodes() { | ||||||
|  |         $.ajax({ | ||||||
|  |             headers: { | ||||||
|  |                 "Accept":"text/html" | ||||||
|  |             }, | ||||||
|  |             cache: false, | ||||||
|  |             url: 'nodes', | ||||||
|  |             success: function(data) { | ||||||
|  |                 $("body").append(data); | ||||||
|  |                 $("body").i18n(); | ||||||
|  |                 $("#palette > .palette-spinner").hide(); | ||||||
|  |                 $(".palette-scroll").removeClass("hide"); | ||||||
|  |                 $("#palette-search").removeClass("hide"); | ||||||
|  |                 loadFlows(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function loadFlows() { | ||||||
|  |         $.ajax({ | ||||||
|  |             headers: { | ||||||
|  |                 "Accept":"application/json", | ||||||
|  |             }, | ||||||
|  |             cache: false, | ||||||
|  |             url: 'flows', | ||||||
|  |             success: function(nodes) { | ||||||
|  |                 var currentHash = window.location.hash; | ||||||
|  |                 RED.nodes.version(nodes.rev); | ||||||
|  |                 RED.nodes.import(nodes.flows); | ||||||
|  |                 RED.nodes.dirty(false); | ||||||
|  |                 RED.view.redraw(true); | ||||||
|  |                 if (/^#flow\/.+$/.test(currentHash)) { | ||||||
|  |                     RED.workspaces.show(currentHash.substring(6)); | ||||||
|  |                 } | ||||||
|  |                 RED.comms.subscribe("status/#",function(topic,msg) { | ||||||
|  |                     var parts = topic.split("/"); | ||||||
|  |                     var node = RED.nodes.node(parts[1]); | ||||||
|  |                     if (node) { | ||||||
|  |                         if (msg.hasOwnProperty("text")) { | ||||||
|  |                             msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()}); | ||||||
|  |                         } | ||||||
|  |                         node.status = msg; | ||||||
|  |                         if (statusEnabled) { | ||||||
|  |                             node.dirty = true; | ||||||
|  |                             RED.view.redraw(); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 RED.comms.subscribe("node/#",function(topic,msg) { | ||||||
|  |                     var i,m; | ||||||
|  |                     var typeList; | ||||||
|  |                     var info; | ||||||
|  |  | ||||||
|  |                     if (topic == "node/added") { | ||||||
|  |                         var addedTypes = []; | ||||||
|  |                         for (i=0;i<msg.length;i++) { | ||||||
|  |                             m = msg[i]; | ||||||
|  |                             var id = m.id; | ||||||
|  |                             RED.nodes.addNodeSet(m); | ||||||
|  |                             addedTypes = addedTypes.concat(m.types); | ||||||
|  |                             RED.i18n.loadCatalog(id, function() { | ||||||
|  |                                 $.get('nodes/'+id, function(data) { | ||||||
|  |                                     $("body").append(data); | ||||||
|  |                                 }); | ||||||
|  |                             }); | ||||||
|  |                         } | ||||||
|  |                         if (addedTypes.length) { | ||||||
|  |                             typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>"; | ||||||
|  |                             RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success"); | ||||||
|  |                         } | ||||||
|  |                     } else if (topic == "node/removed") { | ||||||
|  |                         for (i=0;i<msg.length;i++) { | ||||||
|  |                             m = msg[i]; | ||||||
|  |                             info = RED.nodes.removeNodeSet(m.id); | ||||||
|  |                             if (info.added) { | ||||||
|  |                                 typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>"; | ||||||
|  |                                 RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success"); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else if (topic == "node/enabled") { | ||||||
|  |                         if (msg.types) { | ||||||
|  |                             info = RED.nodes.getNodeSet(msg.id); | ||||||
|  |                             if (info.added) { | ||||||
|  |                                 RED.nodes.enableNodeSet(msg.id); | ||||||
|  |                                 typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||||
|  |                                 RED.notify(RED._("palette.event.nodeEnabled", {count:msg.types.length})+typeList,"success"); | ||||||
|  |                             } else { | ||||||
|  |                                 $.get('nodes/'+msg.id, function(data) { | ||||||
|  |                                     $("body").append(data); | ||||||
|  |                                     typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||||
|  |                                     RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success"); | ||||||
|  |                                 }); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } else if (topic == "node/disabled") { | ||||||
|  |                         if (msg.types) { | ||||||
|  |                             RED.nodes.disableNodeSet(msg.id); | ||||||
|  |                             typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||||
|  |                             RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success"); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     // Refresh flow library to ensure any examples are updated | ||||||
|  |                     RED.library.loadFlowLibrary(); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function showAbout() { | ||||||
|  |         $.get('red/about', function(data) { | ||||||
|  |             var aboutHeader = '<div style="text-align:center;">'+ | ||||||
|  |                                 '<img width="50px" src="red/images/node-red-icon.svg" />'+ | ||||||
|  |                               '</div>'; | ||||||
|  |  | ||||||
|  |             RED.sidebar.info.set(aboutHeader+marked(data)); | ||||||
|  |             RED.sidebar.info.show(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var statusEnabled = false; | ||||||
|  |     function toggleStatus(state) { | ||||||
|  |         statusEnabled = state; | ||||||
|  |         RED.view.status(statusEnabled); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function loadEditor() { | ||||||
|  |  | ||||||
|  |         var menuOptions = []; | ||||||
|  |         menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[ | ||||||
|  |             {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid}, | ||||||
|  |             {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid}, | ||||||
|  |             {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true}, | ||||||
|  |             null, | ||||||
|  |             {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[ | ||||||
|  |                 {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if (s) {RED.bidi.setTextDirection("")}}}, | ||||||
|  |                 {id:"menu-item-bidi-ltr",toggle:"text-direction",label:RED._("menu.label.view.ltr"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("ltr")}}}, | ||||||
|  |                 {id:"menu-item-bidi-rtl",toggle:"text-direction",label:RED._("menu.label.view.rtl"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("rtl")}}}, | ||||||
|  |                 {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if (s) {RED.bidi.setTextDirection("auto")}}} | ||||||
|  |             ]}, | ||||||
|  |             {id:"menu-item-numerals",label:RED._("menu.label.view.numericShaping"),options:[ | ||||||
|  |                {id:"menu-item-numerals-default",toggle:"numeral-type",label:RED._("menu.label.view.defaultNumeral"),selected: true, onselect:function(s) { if(s){RED.bidi.setNumericShapingType("defaultNumeral")}}}, | ||||||
|  |                {id:"menu-item-numerals-national",toggle:"numeral-type",label:RED._("menu.label.view.national"), onselect:function(s) { if (s) {RED.bidi.setNumericShapingType("national")}}}, | ||||||
|  |                {id:"menu-item-numerals-contextual",toggle:"numeral-type",label:RED._("menu.label.view.contextual"), onselect:function(s) { if (s){RED.bidi.setNumericShapingType("contextual")}}}  | ||||||
|  |             ]}, | ||||||
|  |             {id:"menu-item-calendars",label:RED._("menu.label.view.calendar"),options:[ | ||||||
|  |                {id:"menu-item-calendars-default",toggle:"calendar-type",label:RED._("menu.label.view.defaultCalendar"),selected: true, onselect:function(s) { if(s) {RED.bidi.setCalendarType("gregorian")}}}, | ||||||
|  |                {id:"menu-item-calendars-hijri",toggle:"calendar-type",label:RED._("menu.label.view.hijri"), onselect:function(s) { if (s) {RED.bidi.setCalendarType("hijri")}}}, | ||||||
|  |                {id:"menu-item-calendars-hebrew",toggle:"calendar-type",label:RED._("menu.label.view.hebrew"), onselect:function(s) { if (s) {RED.bidi.setCalendarType("hebrew")}}} | ||||||
|  |             ]}, | ||||||
|  |             null, | ||||||
|  |             {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true} | ||||||
|  |         ]}); | ||||||
|  |         menuOptions.push(null); | ||||||
|  |         menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[ | ||||||
|  |             {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import}, | ||||||
|  |             {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]} | ||||||
|  |         ]}); | ||||||
|  |         menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[ | ||||||
|  |             {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export}, | ||||||
|  |             {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export} | ||||||
|  |         ]}); | ||||||
|  |         menuOptions.push(null); | ||||||
|  |         menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:RED.search.show}); | ||||||
|  |         menuOptions.push(null); | ||||||
|  |         menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function() {}}); | ||||||
|  |         menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[ | ||||||
|  |             {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add}, | ||||||
|  |             {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit}, | ||||||
|  |             {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove} | ||||||
|  |         ]}); | ||||||
|  |         menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [ | ||||||
|  |             {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow}, | ||||||
|  |             {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow}, | ||||||
|  |         ]}); | ||||||
|  |         menuOptions.push(null); | ||||||
|  |         if (RED.settings.theme('palette.editable') !== false) { | ||||||
|  |             RED.palette.editor.init(); | ||||||
|  |             menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:RED.palette.editor.show}); | ||||||
|  |             menuOptions.push(null); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp}); | ||||||
|  |         menuOptions.push({id:"menu-item-help", | ||||||
|  |             label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"), | ||||||
|  |             href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs") | ||||||
|  |         }); | ||||||
|  |         menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: showAbout }); | ||||||
|  |  | ||||||
|  |         RED.menu.init({id:"btn-sidemenu",options: menuOptions}); | ||||||
|  |  | ||||||
|  |         //apply rtl direction on body tag in case of GUI mirroring is enabled | ||||||
|  |         if (RED.bidi.isMirroringEnabled()) { | ||||||
|  |             $("body").attr("dir","rtl"); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         RED.user.init(); | ||||||
|  |  | ||||||
|  |         RED.library.init(); | ||||||
|  |         RED.palette.init(); | ||||||
|  |         RED.sidebar.init(); | ||||||
|  |         RED.subflow.init(); | ||||||
|  |         RED.workspaces.init(); | ||||||
|  |         RED.clipboard.init(); | ||||||
|  |         RED.search.init(); | ||||||
|  |         RED.view.init(); | ||||||
|  |         RED.editor.init(); | ||||||
|  |  | ||||||
|  |         RED.deploy.init(RED.settings.theme("deployButton",null)); | ||||||
|  |  | ||||||
|  |         RED.keyboard.add("workspace", /* ? */ 191,{shift:true},function() {RED.keyboard.showHelp();d3.event.preventDefault();}); | ||||||
|  |         RED.comms.connect(); | ||||||
|  |  | ||||||
|  |         $("#main-container").show(); | ||||||
|  |         $(".header-toolbar").show(); | ||||||
|  |  | ||||||
|  |         loadNodeList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $(function() { | ||||||
|  |  | ||||||
|  |         if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) { | ||||||
|  |             document.title = document.title+" : "+window.location.hostname; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         ace.require("ace/ext/language_tools"); | ||||||
|  |  | ||||||
|  |         RED.i18n.init(function() { | ||||||
|  |             RED.settings.init(loadEditor); | ||||||
|  |         }) | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |     }; | ||||||
|  | })(); | ||||||
							
								
								
									
										1249
									
								
								editor/js/nodes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										152
									
								
								editor/js/settings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,152 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2014 IBM, Antoine Aflalo | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | RED.settings = (function () { | ||||||
|  |  | ||||||
|  |     var loadedSettings = {}; | ||||||
|  |  | ||||||
|  |     var hasLocalStorage = function () { | ||||||
|  |         try { | ||||||
|  |             return 'localStorage' in window && window['localStorage'] !== null; | ||||||
|  |         } catch (e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var set = function (key, value) { | ||||||
|  |         if (!hasLocalStorage()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         localStorage.setItem(key, JSON.stringify(value)); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * If the key is not set in the localStorage it returns <i>undefined</i> | ||||||
|  |      * Else return the JSON parsed value | ||||||
|  |      * @param key | ||||||
|  |      * @returns {*} | ||||||
|  |      */ | ||||||
|  |     var get = function (key) { | ||||||
|  |         if (!hasLocalStorage()) { | ||||||
|  |             return undefined; | ||||||
|  |         } | ||||||
|  |         return JSON.parse(localStorage.getItem(key)); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var remove = function (key) { | ||||||
|  |         if (!hasLocalStorage()) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         localStorage.removeItem(key); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var setProperties = function(data) { | ||||||
|  |         for (var prop in loadedSettings) { | ||||||
|  |             if (loadedSettings.hasOwnProperty(prop) && RED.settings.hasOwnProperty(prop)) { | ||||||
|  |                 delete RED.settings[prop]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (prop in data) { | ||||||
|  |             if (data.hasOwnProperty(prop)) { | ||||||
|  |                 RED.settings[prop] = data[prop]; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         loadedSettings = data; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     var init = function (done) { | ||||||
|  |         var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search); | ||||||
|  |         if (accessTokenMatch) { | ||||||
|  |             var accessToken = accessTokenMatch[1]; | ||||||
|  |             RED.settings.set("auth-tokens",{access_token: accessToken}); | ||||||
|  |             window.location.search = ""; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $.ajaxSetup({ | ||||||
|  |             beforeSend: function(jqXHR,settings) { | ||||||
|  |                 // Only attach auth header for requests to relative paths | ||||||
|  |                 if (!/^\s*(https?:|\/|\.)/.test(settings.url)) { | ||||||
|  |                     var auth_tokens = RED.settings.get("auth-tokens"); | ||||||
|  |                     if (auth_tokens) { | ||||||
|  |                         jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token); | ||||||
|  |                     } | ||||||
|  |                     jqXHR.setRequestHeader("Node-RED-API-Version","v2"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         load(done); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     var load = function(done) { | ||||||
|  |         $.ajax({ | ||||||
|  |             headers: { | ||||||
|  |                 "Accept": "application/json" | ||||||
|  |             }, | ||||||
|  |             dataType: "json", | ||||||
|  |             cache: false, | ||||||
|  |             url: 'settings', | ||||||
|  |             success: function (data) { | ||||||
|  |                 setProperties(data); | ||||||
|  |                 if (RED.settings.user && RED.settings.user.anonymous) { | ||||||
|  |                     RED.settings.remove("auth-tokens"); | ||||||
|  |                 } | ||||||
|  |                 console.log("Node-RED: " + data.version); | ||||||
|  |                 done(); | ||||||
|  |             }, | ||||||
|  |             error: function(jqXHR,textStatus,errorThrown) { | ||||||
|  |                 if (jqXHR.status === 401) { | ||||||
|  |                     if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) { | ||||||
|  |                         window.location.search = ""; | ||||||
|  |                     } | ||||||
|  |                     RED.user.login(function() { load(done); }); | ||||||
|  |                 } else { | ||||||
|  |                     console.log("Unexpected error:",jqXHR.status,textStatus); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     function theme(property,defaultValue) { | ||||||
|  |         if (!RED.settings.editorTheme) { | ||||||
|  |             return defaultValue; | ||||||
|  |         } | ||||||
|  |         var parts = property.split("."); | ||||||
|  |         var v = RED.settings.editorTheme; | ||||||
|  |         try { | ||||||
|  |             for (var i=0;i<parts.length;i++) { | ||||||
|  |                 v = v[parts[i]]; | ||||||
|  |             } | ||||||
|  |             if (v === undefined) { | ||||||
|  |                 return defaultValue; | ||||||
|  |             } | ||||||
|  |             return v; | ||||||
|  |         } catch(err) { | ||||||
|  |             return defaultValue; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         init: init, | ||||||
|  |         load: load, | ||||||
|  |         set: set, | ||||||
|  |         get: get, | ||||||
|  |         remove: remove, | ||||||
|  |         theme: theme | ||||||
|  |     } | ||||||
|  | }) | ||||||
|  | (); | ||||||
							
								
								
									
										307
									
								
								editor/js/ui/clipboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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 | ||||||
|  |     } | ||||||
|  | })(); | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| /** | /** | ||||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 |  * Copyright 2016 IBM Corp. | ||||||
|  * |  * | ||||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with the License. |  * you may not use this file except in compliance with the License. | ||||||
| @@ -18,7 +18,6 @@ | |||||||
| /** | /** | ||||||
|  * options: |  * options: | ||||||
|  *   - addButton : boolean|string - text for add label, default 'add' |  *   - addButton : boolean|string - text for add label, default 'add' | ||||||
|  *   - buttons : array - list of custom buttons (objects with fields 'id', 'label', 'icon', 'title', 'click') |  | ||||||
|  *   - height : number|'auto' |  *   - height : number|'auto' | ||||||
|  *   - resize : function - called when list as a whole is resized |  *   - resize : function - called when list as a whole is resized | ||||||
|  *   - resizeItem : function(item) - called to resize individual item |  *   - resizeItem : function(item) - called to resize individual item | ||||||
| @@ -28,22 +27,13 @@ | |||||||
|  *   - removable : boolean - whether to display delete button on items |  *   - removable : boolean - whether to display delete button on items | ||||||
|  *   - addItem : function(row,index,itemData) - when an item is added |  *   - addItem : function(row,index,itemData) - when an item is added | ||||||
|  *   - removeItem : function(itemData) - called when an item is removed |  *   - removeItem : function(itemData) - called when an item is removed | ||||||
|  *   - filter : function(itemData) - called for each item to determine if it should be shown |  | ||||||
|  *   - sort : function(itemDataA,itemDataB) - called to sort items |  | ||||||
|  *   - scrollOnAdd : boolean - whether to scroll to newly added items |  | ||||||
|  * methods: |  * methods: | ||||||
|  *   - addItem(itemData) |  *   - addItem(itemData) | ||||||
|  *   - insertItemAt : function(data,index) - add an item at the specified index |  *   - removeItem(itemData) | ||||||
|  *   - removeItem(itemData, detach) - remove the item. Optionally detach to preserve any event handlers on the item's label |  | ||||||
|  *   - getItemAt(index) |  | ||||||
|  *   - indexOf(itemData) |  | ||||||
|  *   - width(width) |  *   - width(width) | ||||||
|  *   - height(height) |  *   - height(height) | ||||||
|  *   - items() |  *   - items() | ||||||
|  *   - empty() |  *   - empty() | ||||||
|  *   - filter(filter) |  | ||||||
|  *   - sort(sort) |  | ||||||
|  *   - length() |  | ||||||
|  */ |  */ | ||||||
|     $.widget( "nodered.editableList", { |     $.widget( "nodered.editableList", { | ||||||
|         _create: function() { |         _create: function() { | ||||||
| @@ -54,78 +44,34 @@ | |||||||
|             this.uiContainer = this.element |             this.uiContainer = this.element | ||||||
|                 .wrap( "<div>" ) |                 .wrap( "<div>" ) | ||||||
|                 .parent(); |                 .parent(); | ||||||
| 
 |  | ||||||
|             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 = this.uiContainer.wrap("<div>").parent(); | ||||||
|             } |  | ||||||
|             this.topContainer.addClass('red-ui-editableList'); |  | ||||||
|             if (this.options.class) { |  | ||||||
|                 this.topContainer.addClass(this.options.class); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             var buttons = this.options.buttons || []; |             this.topContainer.addClass('red-ui-editableList'); | ||||||
| 
 | 
 | ||||||
|             if (this.options.addButton !== false) { |             if (this.options.addButton !== false) { | ||||||
|                 var addLabel, addTitle; |                 var addLabel; | ||||||
|                 if (typeof this.options.addButton === 'string') { |                 if (typeof this.options.addButton === 'string') { | ||||||
|                     addLabel = this.options.addButton |                     addLabel = this.options.addButton | ||||||
|                 } else { |                 } else { | ||||||
|                     if (RED && RED._) { |                     if (RED && RED._) { | ||||||
|                         addLabel = RED._("editableList.add"); |                         addLabel = RED._("editableList.add"); | ||||||
|                         addTitle = RED._("editableList.addTitle"); |  | ||||||
|                     } else { |                     } else { | ||||||
|                         addLabel = 'add'; |                         addLabel = 'add'; | ||||||
|                         addTitle = 'add new item'; |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 buttons.unshift({ |                 $('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>') | ||||||
|                     label: addLabel, |                     .appendTo(this.topContainer) | ||||||
|                     icon: "fa fa-plus", |                     .click(function(evt) { | ||||||
|                     click: function(evt) { |  | ||||||
|                         that.addItem({}); |  | ||||||
|                     }, |  | ||||||
|                     title: addTitle |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             buttons.forEach(function(button) { |  | ||||||
|                 var element = $('<button type="button" class="red-ui-button red-ui-button-small red-ui-editableList-addButton" style="margin-top: 4px; margin-right: 5px;"></button>') |  | ||||||
|                     .appendTo(that.topContainer) |  | ||||||
|                     .on("click", function(evt) { |  | ||||||
|                         evt.preventDefault(); |                         evt.preventDefault(); | ||||||
|                         if (button.click !== undefined) { |                         that.addItem({}); | ||||||
|                             button.click(evt); |  | ||||||
|                         } |  | ||||||
|                     }); |                     }); | ||||||
| 
 |  | ||||||
|                 if (button.id) { |  | ||||||
|                     element.attr("id", button.id); |  | ||||||
|             } |             } | ||||||
|                 if (button.title) { |  | ||||||
|                     element.attr("title", button.title); |  | ||||||
|                 } |  | ||||||
|                 if (button.icon) { |  | ||||||
|                     element.append($("<i></i>").attr("class", button.icon)); |  | ||||||
|                 } |  | ||||||
|                 if (button.label) { |  | ||||||
|                     element.append($("<span></span>").text(" " + button.label)); |  | ||||||
|                 } |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             if (this.element.css("position") === "absolute") { |             if (this.element.css("position") === "absolute") { | ||||||
|                 ["top","left","bottom","right"].forEach(function(s) { |                 ["top","left","bottom","right"].forEach(function(s) { | ||||||
|                     var v = that.element.css(s); |                     var v = that.element.css(s); | ||||||
|                     if (v!=="auto" && v!=="") { |                     if (s!=="auto" && s!=="") { | ||||||
|                         that.topContainer.css(s,v); |                         that.topContainer.css(s,v); | ||||||
|                         that.uiContainer.css(s,"0"); |                         that.uiContainer.css(s,"0"); | ||||||
|                         if (s === "top" && that.options.header) { |  | ||||||
|                             that.uiContainer.css(s,"20px") |  | ||||||
|                         } |  | ||||||
|                         that.element.css(s,'auto'); |                         that.element.css(s,'auto'); | ||||||
|                     } |                     } | ||||||
|                 }) |                 }) | ||||||
| @@ -134,11 +80,6 @@ | |||||||
|                 this.uiContainer.css("position","absolute"); |                 this.uiContainer.css("position","absolute"); | ||||||
| 
 | 
 | ||||||
|             } |             } | ||||||
|             if (this.options.header) { |  | ||||||
|                 this.borderContainer.addClass("red-ui-editableList-border"); |  | ||||||
|             } else { |  | ||||||
|                 this.uiContainer.addClass("red-ui-editableList-border"); |  | ||||||
|             } |  | ||||||
|             this.uiContainer.addClass("red-ui-editableList-container"); |             this.uiContainer.addClass("red-ui-editableList-container"); | ||||||
| 
 | 
 | ||||||
|             this.uiHeight = this.element.height(); |             this.uiHeight = this.element.height(); | ||||||
| @@ -154,13 +95,8 @@ | |||||||
|                 this.uiContainer.css("minHeight",minHeight); |                 this.uiContainer.css("minHeight",minHeight); | ||||||
|                 this.element.css("minHeight",0); |                 this.element.css("minHeight",0); | ||||||
|             } |             } | ||||||
|             var maxHeight = this.element.css("maxHeight"); |  | ||||||
|             if (maxHeight !== '0px') { |  | ||||||
|                 this.uiContainer.css("maxHeight",maxHeight); |  | ||||||
|                 this.element.css("maxHeight",null); |  | ||||||
|             } |  | ||||||
|             if (this.options.height !== 'auto') { |             if (this.options.height !== 'auto') { | ||||||
|                 this.uiContainer.css("overflow-y","auto"); |                 this.uiContainer.css("overflow-y","scroll"); | ||||||
|                 if (!isNaN(this.options.height)) { |                 if (!isNaN(this.options.height)) { | ||||||
|                     this.uiHeight = this.options.height; |                     this.uiHeight = this.options.height; | ||||||
|                 } |                 } | ||||||
| @@ -218,22 +154,17 @@ | |||||||
|             if (this.options.resizeItem) { |             if (this.options.resizeItem) { | ||||||
|                 var that = this; |                 var that = this; | ||||||
|                 this.element.children().each(function(i) { |                 this.element.children().each(function(i) { | ||||||
|                     that.options.resizeItem($(this).children(".red-ui-editableList-item-content"),i); |                     that.options.resizeItem($(this).find(".red-ui-editableList-item-content"),i); | ||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         _destroy: function() { |         _destroy: function() { | ||||||
|             if (this.topContainer) { |  | ||||||
|                 var tc = this.topContainer; |  | ||||||
|                 delete this.topContainer; |  | ||||||
|                 tc.remove(); |  | ||||||
|             } |  | ||||||
|         }, |         }, | ||||||
|         _refreshFilter: function() { |         _refreshFilter: function() { | ||||||
|             var that = this; |             var that = this; | ||||||
|             var count = 0; |             var count = 0; | ||||||
|             if (!this.activeFilter) { |             if (!this.activeFilter) { | ||||||
|                 return this.element.children().show(); |                 this.element.children().show(); | ||||||
|             } |             } | ||||||
|             var items = this.items(); |             var items = this.items(); | ||||||
|             items.each(function (i,el) { |             items.each(function (i,el) { | ||||||
| @@ -258,7 +189,7 @@ | |||||||
|                 var items = this.element.children(); |                 var items = this.element.children(); | ||||||
|                 var that = this; |                 var that = this; | ||||||
|                 items.sort(function(A,B) { |                 items.sort(function(A,B) { | ||||||
|                     return that.activeSort($(A).children(".red-ui-editableList-item-content").data('data'),$(B).children(".red-ui-editableList-item-content").data('data')); |                     return that.activeSort($(A).find(".red-ui-editableList-item-content").data('data'),$(B).find(".red-ui-editableList-item-content").data('data')); | ||||||
|                 }); |                 }); | ||||||
|                 $.each(items,function(idx,li) { |                 $.each(items,function(idx,li) { | ||||||
|                     that.element.append(li); |                     that.element.append(li); | ||||||
| @@ -273,49 +204,10 @@ | |||||||
|             this.uiHeight = desiredHeight; |             this.uiHeight = desiredHeight; | ||||||
|             this._resize(); |             this._resize(); | ||||||
|         }, |         }, | ||||||
|         getItemAt: function(index) { |         addItem: function(data) { | ||||||
|             var items = this.items(); |  | ||||||
|             if (index >= 0 && index < items.length) { |  | ||||||
|                 return $(items[index]).data('data'); |  | ||||||
|             } else { |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         indexOf: function(data) { |  | ||||||
|             var items = this.items(); |  | ||||||
|             for (var i=0;i<items.length;i++) { |  | ||||||
|                 if ($(items[i]).data('data') === data) { |  | ||||||
|                     return i |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             return -1 |  | ||||||
|         }, |  | ||||||
|         insertItemAt: function(data,index) { |  | ||||||
|             var that = this; |             var that = this; | ||||||
|             data = data || {}; |             data = data || {}; | ||||||
|             var li = $('<li>'); |             var li = $('<li>'); | ||||||
|             var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li); |  | ||||||
|             row.data('data',data); |  | ||||||
|             if (this.options.sortable === true) { |  | ||||||
|                 $('<i class="red-ui-editableList-item-handle fa fa-bars"></i>').appendTo(li); |  | ||||||
|                 li.addClass("red-ui-editableList-item-sortable"); |  | ||||||
|             } |  | ||||||
|             if (this.options.removable) { |  | ||||||
|                 var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove red-ui-button red-ui-button-small"}).appendTo(li); |  | ||||||
|                 $('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton); |  | ||||||
|                 li.addClass("red-ui-editableList-item-removable"); |  | ||||||
|                 deleteButton.on("click", function(evt) { |  | ||||||
|                     evt.preventDefault(); |  | ||||||
|                     var data = row.data('data'); |  | ||||||
|                     li.addClass("red-ui-editableList-item-deleting") |  | ||||||
|                     li.fadeOut(300, function() { |  | ||||||
|                         $(this).remove(); |  | ||||||
|                         if (that.options.removeItem) { |  | ||||||
|                             that.options.removeItem(data); |  | ||||||
|                         } |  | ||||||
|                     }); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|             var added = false; |             var added = false; | ||||||
|             if (this.activeSort) { |             if (this.activeSort) { | ||||||
|                 var items = this.items(); |                 var items = this.items(); | ||||||
| @@ -330,17 +222,32 @@ | |||||||
|                 }); |                 }); | ||||||
|             } |             } | ||||||
|             if (!added) { |             if (!added) { | ||||||
|                 if (index <= 0) { |  | ||||||
|                     li.prependTo(this.element); |  | ||||||
|                 } else if (index > that.element.children().length-1) { |  | ||||||
|                 li.appendTo(this.element); |                 li.appendTo(this.element); | ||||||
|                 } else { |  | ||||||
|                     li.insertBefore(this.element.children().eq(index)); |  | ||||||
|             } |             } | ||||||
|  |             var row = $('<div/>').addClass("red-ui-editableList-item-content").appendTo(li); | ||||||
|  |             row.data('data',data); | ||||||
|  |             if (this.options.sortable === true) { | ||||||
|  |                 $('<i class="red-ui-editableList-item-handle fa fa-bars"></i>').appendTo(li); | ||||||
|  |                 li.addClass("red-ui-editableList-item-sortable"); | ||||||
|  |             } | ||||||
|  |             if (this.options.removable) { | ||||||
|  |                 var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(li); | ||||||
|  |                 $('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton); | ||||||
|  |                 li.addClass("red-ui-editableList-item-removable"); | ||||||
|  |                 deleteButton.click(function() { | ||||||
|  |                     var data = row.data('data'); | ||||||
|  |                     li.addClass("red-ui-editableList-item-deleting") | ||||||
|  |                     li.fadeOut(300, function() { | ||||||
|  |                         $(this).remove(); | ||||||
|  |                         if (that.options.removeItem) { | ||||||
|  |                             that.options.removeItem(data); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                 }); | ||||||
|             } |             } | ||||||
|             if (this.options.addItem) { |             if (this.options.addItem) { | ||||||
|                 var index = that.element.children().length-1; |                 var index = that.element.children().length-1; | ||||||
|                 // setTimeout(function() {
 |                 setTimeout(function() { | ||||||
|                     that.options.addItem(row,index,data); |                     that.options.addItem(row,index,data); | ||||||
|                     if (that.activeFilter) { |                     if (that.activeFilter) { | ||||||
|                         try { |                         try { | ||||||
| @@ -356,36 +263,23 @@ | |||||||
|                             that.uiContainer.scrollTop(that.element.height()); |                             that.uiContainer.scrollTop(that.element.height()); | ||||||
|                         },0); |                         },0); | ||||||
|                     } |                     } | ||||||
|                 // },0);
 |                 },0); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         addItem: function(data) { |         removeItem: function(data) { | ||||||
|             this.insertItemAt(data,this.element.children().length) |  | ||||||
|         }, |  | ||||||
|         addItems: function(items) { |  | ||||||
|             for (var i=0; i<items.length;i++) { |  | ||||||
|                 this.addItem(items[i]); |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         removeItem: function(data,detach) { |  | ||||||
|             var items = this.element.children().filter(function(f) { |             var items = this.element.children().filter(function(f) { | ||||||
|                 return data === $(this).children(".red-ui-editableList-item-content").data('data'); |                 return data === $(this).find(".red-ui-editableList-item-content").data('data'); | ||||||
|             }); |             }); | ||||||
|             if (detach) { |  | ||||||
|                 items.detach(); |  | ||||||
|             } else { |  | ||||||
|             items.remove(); |             items.remove(); | ||||||
|             } |  | ||||||
|             if (this.options.removeItem) { |             if (this.options.removeItem) { | ||||||
|                 this.options.removeItem(data); |                 this.options.removeItem(data); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         items: function() { |         items: function() { | ||||||
|             return this.element.children().map(function(i) { return $(this).children(".red-ui-editableList-item-content"); }); |             return this.element.children().map(function(i) { return $(this).find(".red-ui-editableList-item-content"); }); | ||||||
|         }, |         }, | ||||||
|         empty: function() { |         empty: function() { | ||||||
|             this.element.empty(); |             this.element.empty(); | ||||||
|             this.uiContainer.scrollTop(0); |  | ||||||
|         }, |         }, | ||||||
|         filter: function(filter) { |         filter: function(filter) { | ||||||
|             if (filter !== undefined) { |             if (filter !== undefined) { | ||||||
| @@ -401,25 +295,6 @@ | |||||||
|         }, |         }, | ||||||
|         length: function() { |         length: function() { | ||||||
|             return this.element.children().length; |             return this.element.children().length; | ||||||
|         }, |  | ||||||
|         show: function(item) { |  | ||||||
|             var items = this.element.children().filter(function(f) { |  | ||||||
|                 return item === $(this).children(".red-ui-editableList-item-content").data('data'); |  | ||||||
|             }); |  | ||||||
|             if (items.length > 0) { |  | ||||||
|                 this.uiContainer.scrollTop(this.uiContainer.scrollTop()+items.position().top) |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         getItem: function(li) { |  | ||||||
|             var el = li.children(".red-ui-editableList-item-content"); |  | ||||||
|             if (el.length) { |  | ||||||
|                 return el.data('data'); |  | ||||||
|             } else { |  | ||||||
|                 return null; |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         cancel: function() { |  | ||||||
|             this.element.sortable("cancel"); |  | ||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
| })(jQuery); | })(jQuery); | ||||||
							
								
								
									
										257
									
								
								editor/js/ui/common/menu.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,257 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2014, 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | RED.menu = (function() { | ||||||
|  |  | ||||||
|  |     var menuItems = {}; | ||||||
|  |  | ||||||
|  |     function createMenuItem(opt) { | ||||||
|  |         var item; | ||||||
|  |  | ||||||
|  |         if (opt !== null && opt.id) { | ||||||
|  |             var themeSetting = RED.settings.theme("menu."+opt.id); | ||||||
|  |             if (themeSetting === false) { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         function setInitialState() { | ||||||
|  |             var savedStateActive = isSavedStateActive(opt.id); | ||||||
|  |             if (savedStateActive) { | ||||||
|  |                 link.addClass("active"); | ||||||
|  |                 opt.onselect.call(opt, true); | ||||||
|  |             } else if (savedStateActive === false) { | ||||||
|  |                 link.removeClass("active"); | ||||||
|  |                 opt.onselect.call(opt, false); | ||||||
|  |             } else if (opt.hasOwnProperty("selected")) { | ||||||
|  |                 if (opt.selected) { | ||||||
|  |                     link.addClass("active"); | ||||||
|  |                 } else { | ||||||
|  |                     link.removeClass("active"); | ||||||
|  |                 } | ||||||
|  |                 opt.onselect.call(opt, opt.selected); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (opt === null) { | ||||||
|  |             item = $('<li class="divider"></li>'); | ||||||
|  |         } else { | ||||||
|  |             item = $('<li></li>'); | ||||||
|  |  | ||||||
|  |             if (opt.group) { | ||||||
|  |                 item.addClass("menu-group-"+opt.group); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             var linkContent = '<a '+(opt.id?'id="'+opt.id+'" ':'')+'tabindex="-1" href="#">'; | ||||||
|  |             if (opt.toggle) { | ||||||
|  |                 linkContent += '<i class="fa fa-square pull-left"></i>'; | ||||||
|  |                 linkContent += '<i class="fa fa-check-square pull-left"></i>'; | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             if (opt.icon !== undefined) { | ||||||
|  |                 if (/\.png/.test(opt.icon)) { | ||||||
|  |                     linkContent += '<img src="'+opt.icon+'"/> '; | ||||||
|  |                 } else { | ||||||
|  |                     linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> '; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (opt.sublabel) { | ||||||
|  |                 linkContent += '<span class="menu-label-container"><span class="menu-label">'+opt.label+'</span>'+ | ||||||
|  |                                '<span class="menu-sublabel">'+opt.sublabel+'</span></span>' | ||||||
|  |             } else { | ||||||
|  |                 linkContent += '<span class="menu-label">'+RED.bidi.applyBidiSupport(opt.label, RED.bidi.flags.NS)+'</span>' | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             linkContent += '</a>'; | ||||||
|  |  | ||||||
|  |             var link = $(linkContent).appendTo(item); | ||||||
|  |  | ||||||
|  |             menuItems[opt.id] = opt; | ||||||
|  |  | ||||||
|  |             if (opt.onselect) { | ||||||
|  |                 link.click(function() { | ||||||
|  |                     if ($(this).parent().hasClass("disabled")) { | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |                     if (opt.toggle) { | ||||||
|  |                         var selected = isSelected(opt.id); | ||||||
|  |                         if (typeof opt.toggle === "string") { | ||||||
|  |                             if (!selected) { | ||||||
|  |                                 for (var m in menuItems) { | ||||||
|  |                                     if (menuItems.hasOwnProperty(m)) { | ||||||
|  |                                         var mi = menuItems[m]; | ||||||
|  |                                         if (mi.id != opt.id && opt.toggle == mi.toggle) { | ||||||
|  |                                             setSelected(mi.id,false); | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                                 setSelected(opt.id,true); | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             setSelected(opt.id, !selected); | ||||||
|  |                         } | ||||||
|  |                     } else { | ||||||
|  |                         opt.onselect.call(opt); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 setInitialState(); | ||||||
|  |             } else if (opt.href) { | ||||||
|  |                 link.attr("target","_blank").attr("href",opt.href); | ||||||
|  |             } else if (!opt.options) { | ||||||
|  |                 item.addClass("disabled"); | ||||||
|  |                 link.click(function(event) { | ||||||
|  |                     event.preventDefault(); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             if (opt.options) { | ||||||
|  |                 item.addClass("dropdown-submenu pull-left"); | ||||||
|  |                 var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item); | ||||||
|  |  | ||||||
|  |                 for (var i=0;i<opt.options.length;i++) { | ||||||
|  |                     var li = createMenuItem(opt.options[i]); | ||||||
|  |                     if (li) { | ||||||
|  |                         li.appendTo(submenu); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (opt.disabled) { | ||||||
|  |                 item.addClass("disabled"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         return item; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     function createMenu(options) { | ||||||
|  |  | ||||||
|  |         var button = $("#"+options.id); | ||||||
|  |  | ||||||
|  |         //button.click(function(event) { | ||||||
|  |         //    $("#"+options.id+"-submenu").show(); | ||||||
|  |         //    event.preventDefault(); | ||||||
|  |         //}); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var topMenu = $("<ul/>",{id:options.id+"-submenu", class:"dropdown-menu pull-right"}).insertAfter(button); | ||||||
|  |  | ||||||
|  |         var lastAddedSeparator = false; | ||||||
|  |         for (var i=0;i<options.options.length;i++) { | ||||||
|  |             var opt = options.options[i]; | ||||||
|  |             if (opt !== null || !lastAddedSeparator) { | ||||||
|  |                 var li = createMenuItem(opt); | ||||||
|  |                 if (li) { | ||||||
|  |                     li.appendTo(topMenu); | ||||||
|  |                     lastAddedSeparator = (opt === null); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function isSavedStateActive(id) { | ||||||
|  |         return RED.settings.get("menu-" + id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function isSelected(id) { | ||||||
|  |         return $("#" + id).hasClass("active"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function setSavedState(id, state) { | ||||||
|  |         RED.settings.set("menu-" + id, state); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function setSelected(id,state) { | ||||||
|  |         if (isSelected(id) == state) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         var opt = menuItems[id]; | ||||||
|  |         if (state) { | ||||||
|  |             $("#"+id).addClass("active"); | ||||||
|  |         } else { | ||||||
|  |             $("#"+id).removeClass("active"); | ||||||
|  |         } | ||||||
|  |         if (opt && opt.onselect) { | ||||||
|  |             opt.onselect.call(opt,state); | ||||||
|  |         } | ||||||
|  |         setSavedState(id, state); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function setDisabled(id,state) { | ||||||
|  |         if (state) { | ||||||
|  |             $("#"+id).parent().addClass("disabled"); | ||||||
|  |         } else { | ||||||
|  |             $("#"+id).parent().removeClass("disabled"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function addItem(id,opt) { | ||||||
|  |         var item = createMenuItem(opt); | ||||||
|  |         if (opt.group) { | ||||||
|  |             var groupItems = $("#"+id+"-submenu").children(".menu-group-"+opt.group); | ||||||
|  |             if (groupItems.length === 0) { | ||||||
|  |                 item.appendTo("#"+id+"-submenu"); | ||||||
|  |             } else { | ||||||
|  |                 for (var i=0;i<groupItems.length;i++) { | ||||||
|  |                     var groupItem = groupItems[i]; | ||||||
|  |                     var label = $(groupItem).find(".menu-label").html(); | ||||||
|  |                     if (opt.label < label) { | ||||||
|  |                         $(groupItem).before(item); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (i === groupItems.length) { | ||||||
|  |                     item.appendTo("#"+id+"-submenu"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             item.appendTo("#"+id+"-submenu"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     function removeItem(id) { | ||||||
|  |         $("#"+id).parent().remove(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function setAction(id,action) { | ||||||
|  |         var opt = menuItems[id]; | ||||||
|  |         if (opt) { | ||||||
|  |             opt.onselect = action; | ||||||
|  |             // $("#"+id).click(function() { | ||||||
|  |             //     if ($(this).parent().hasClass("disabled")) { | ||||||
|  |             //         return; | ||||||
|  |             //     } | ||||||
|  |             //     if (menuItems[id].toggle) { | ||||||
|  |             //         setSelected(id,!isSelected(id)); | ||||||
|  |             //     } else { | ||||||
|  |             //         menuItems[id].onselect.call(menuItems[id]); | ||||||
|  |             //     } | ||||||
|  |             // }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         init: createMenu, | ||||||
|  |         setSelected: setSelected, | ||||||
|  |         isSelected: isSelected, | ||||||
|  |         setDisabled: setDisabled, | ||||||
|  |         addItem: addItem, | ||||||
|  |         removeItem: removeItem, | ||||||
|  |         setAction: setAction | ||||||
|  |         //TODO: add an api for replacing a submenu - see library.js:loadFlowLibrary | ||||||
|  |     } | ||||||
|  | })(); | ||||||
							
								
								
									
										79
									
								
								editor/js/ui/common/popover.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,79 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2015 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  |  | ||||||
|  | RED.popover = (function() { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     function createPopover(options) { | ||||||
|  |         var target = options.target; | ||||||
|  |  | ||||||
|  |         var content = options.content; | ||||||
|  |         var delay = options.delay; | ||||||
|  |         var timer = null; | ||||||
|  |         var active; | ||||||
|  |         var div; | ||||||
|  |  | ||||||
|  |         var openPopup = function() { | ||||||
|  |             if (active) { | ||||||
|  |                 div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body"); | ||||||
|  |                 var targetPos = target.offset(); | ||||||
|  |                 var targetWidth = target.width(); | ||||||
|  |                 var targetHeight = target.height(); | ||||||
|  |  | ||||||
|  |                 var divHeight = div.height(); | ||||||
|  |                 var popoverPos = ((RED.bidi.isMirroringEnabled()) ? targetPos.left-targetWidth-210 : targetPos.left+targetWidth+17); | ||||||
|  |                 div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:popoverPos}); | ||||||
|  |                 div.fadeIn("fast"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         var closePopup = function() { | ||||||
|  |             if (!active) { | ||||||
|  |                 if (div) { | ||||||
|  |                     div.fadeOut("fast",function() { | ||||||
|  |                         $(this).remove(); | ||||||
|  |                     }); | ||||||
|  |                     div = null; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         target.on('mouseenter',function(e) { | ||||||
|  |             clearTimeout(timer); | ||||||
|  |             active = true; | ||||||
|  |             timer = setTimeout(openPopup,delay.show); | ||||||
|  |         }); | ||||||
|  |         target.on('mouseleave', function(e) { | ||||||
|  |             if (timer) { | ||||||
|  |                 clearTimeout(timer); | ||||||
|  |             } | ||||||
|  |             active = false; | ||||||
|  |             setTimeout(closePopup,delay.hide); | ||||||
|  |         }); | ||||||
|  |         var res = { | ||||||
|  |             setContent: function(_content) { | ||||||
|  |                 content = _content; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         target.data('popover',res); | ||||||
|  |         return res; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         create: createPopover | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | })(); | ||||||
							
								
								
									
										97
									
								
								editor/js/ui/common/searchBox.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,97 @@ | |||||||
|  | /** | ||||||
|  |  * Copyright 2016 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  * http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  **/ | ||||||
|  | (function($) { | ||||||
|  |  | ||||||
|  |     $.widget( "nodered.searchBox", { | ||||||
|  |         _create: function() { | ||||||
|  |             var that = this; | ||||||
|  |  | ||||||
|  |             this.currentTimeout = null; | ||||||
|  |             this.lastSent = ""; | ||||||
|  |             this.element.val(""); | ||||||
|  |             this.uiContainer = this.element.wrap("<div>").parent(); | ||||||
|  |             this.uiContainer.addClass("red-ui-searchBox-container"); | ||||||
|  |  | ||||||
|  |             $('<i class="fa fa-search"></i>').prependTo(this.uiContainer); | ||||||
|  |             this.clearButton = $('<a href="#"><i class="fa fa-times"></i></a>').appendTo(this.uiContainer); | ||||||
|  |             this.clearButton.on("click",function(e) { | ||||||
|  |                 e.preventDefault(); | ||||||
|  |                 that.element.val(""); | ||||||
|  |                 that._change("",true); | ||||||
|  |                 that.element.focus(); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             this.resultCount = $('<span>',{class:"red-ui-searchBox-resultCount hide"}).appendTo(this.uiContainer); | ||||||
|  |  | ||||||
|  |             this.element.val(""); | ||||||
|  |             this.element.on("keydown",function(evt) { | ||||||
|  |                 if (evt.keyCode === 27) { | ||||||
|  |                     that.element.val(""); | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             this.element.on("keyup",function(evt) { | ||||||
|  |                 that._change($(this).val()); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             this.element.on("focus",function() { | ||||||
|  |                 $("body").one("mousedown",function() { | ||||||
|  |                     that.element.blur(); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |         }, | ||||||
|  |         _change: function(val,instant) { | ||||||
|  |             var fireEvent = false; | ||||||
|  |             if (val === "") { | ||||||
|  |                 this.clearButton.hide(); | ||||||
|  |                 fireEvent = true; | ||||||
|  |             } else { | ||||||
|  |                 this.clearButton.show(); | ||||||
|  |                 fireEvent = (val.length >= (this.options.minimumLength||0)); | ||||||
|  |             } | ||||||
|  |             var current = this.element.val(); | ||||||
|  |             fireEvent = fireEvent && current !== this.lastSent; | ||||||
|  |             if (fireEvent) { | ||||||
|  |                 if (!instant && this.options.delay > 0) { | ||||||
|  |                     clearTimeout(this.currentTimeout); | ||||||
|  |                     var that = this; | ||||||
|  |                     this.currentTimeout = setTimeout(function() { | ||||||
|  |                         that.lastSent = that.element.val(); | ||||||
|  |                         that._trigger("change"); | ||||||
|  |                     },this.options.delay); | ||||||
|  |                 } else { | ||||||
|  |                     this._trigger("change"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         value: function(val) { | ||||||
|  |             if (val === undefined) { | ||||||
|  |                 return this.element.val(); | ||||||
|  |             } else { | ||||||
|  |                 this.element.val(val); | ||||||
|  |                 this._change(val); | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         count: function(val) { | ||||||
|  |             if (val === undefined || val === null || val === "") { | ||||||
|  |                 this.resultCount.text("").hide(); | ||||||
|  |             } else { | ||||||
|  |             	val = RED.bidi.applyBidiSupport(val,RED.bidi.flags.NS); | ||||||
|  |                 this.resultCount.text(val).show(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | })(jQuery); | ||||||