Compare commits
	
		
			4 Commits
		
	
	
		
	
	| 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 `grunt` to verify the unit tests pass | ||||
| - [ ] I have added suitable unit tests to cover the new/changed functionality | ||||
							
								
								
									
										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@v2 | ||||
|         with: | ||||
|             path: 'node-red' | ||||
|       - name: Check out node-red-docker repository | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|             repository: 'node-red/node-red-docker' | ||||
|             path: 'node-red-docker' | ||||
|       - name: Check out node-red.github.io repository | ||||
|         uses: actions/checkout@v2 | ||||
|         with: | ||||
|             repository: 'node-red/node-red.github.io' | ||||
|             path: 'node-red.github.io' | ||||
|       - uses: actions/setup-node@v1 | ||||
|         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: [14, 16] | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - name: Use Node.js ${{ matrix.node-version }} | ||||
|       uses: actions/setup-node@v2 | ||||
|       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 == 14 }} | ||||
|       uses: coverallsapp/github-action@v1.1.2 | ||||
|       with: | ||||
|         github-token: ${{ github.token }} | ||||
							
								
								
									
										10
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -7,9 +7,7 @@ | ||||
| .sessions.json | ||||
| .settings | ||||
| .tern-project | ||||
| .i18n-editor-metadata | ||||
| *.backup | ||||
| *.bak | ||||
| *_cred* | ||||
| coverage | ||||
| credentials.json | ||||
| @@ -19,11 +17,3 @@ node_modules | ||||
| public | ||||
| locales/zz-ZZ | ||||
| nodes/core/locales/zz-ZZ | ||||
| !packages/node_modules | ||||
| packages/node_modules/@node-red/editor-client/public | ||||
| !test/**/node_modules | ||||
| docs | ||||
| !packages/node_modules/**/docs | ||||
| .vscode | ||||
| .nyc_output | ||||
| sync.ffs_db | ||||
|   | ||||
| @@ -2,9 +2,6 @@ | ||||
|     "asi": true,        // allow missing semicolons | ||||
|     "curly": true,      // require braces | ||||
|     "eqnull": true,     // ignore ==null | ||||
|     //"eqeqeq": true,   // enforce === | ||||
|     "freeze": true,     // don't allow override | ||||
|     "indent": 4,        // default indent of 4 | ||||
|     "forin": true,      // require property filtering in "for in" loops | ||||
|     "immed": true,      // require immediate functions to be wrapped in ( ) | ||||
|     "nonbsp": true,     // warn on unexpected whitespace breaking chars | ||||
| @@ -12,8 +9,6 @@ | ||||
|     //"unused": true,   // Check for unused functions and variables | ||||
|     "loopfunc": true,   // allow functions to be defined in loops | ||||
|     //"expr": true,     // allow ternery operator syntax... | ||||
|     "shadow": true,     // allow variable shadowing (re-use of names...) | ||||
|     "sub": true,        // don't warn that foo['bar'] should be written as foo.bar | ||||
|     "proto": true,      // allow setting of __proto__ in node < v0.12, | ||||
|     "esversion": 11      // allow es11(ES2020) | ||||
|     "proto": true       // allow setting of __proto__ in node < v0.12 | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| /Gruntfile.js | ||||
| /.git/* | ||||
| /lib/* | ||||
| *.backup | ||||
| /public/* | ||||
|   | ||||
							
								
								
									
										7
									
								
								.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| .settings | ||||
| .jshintignore | ||||
| .jshintrc | ||||
| .project | ||||
| .tern-project | ||||
| .travis.yml | ||||
| .git | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										793
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						| @@ -1,353 +1,498 @@ | ||||
| #### 3.1.0-beta.1: Beta Release | ||||
| #### 0.15.1: Maintenance Release | ||||
|  | ||||
|  - 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 | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - NEW: Locking Flows (#3938) @knolleary | ||||
|  - NEW: Improve UX around hiding flows via context menu (#3930) @knolleary | ||||
|  - 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 | ||||
|  | ||||
|  - Remember compact/pretty flow export user choice (#3974) @Steve-Mcl | ||||
|  - fix .red-ui-notification class (#4035) @xiaobinqt | ||||
|  - Fix border radius on Modules list header (#4038) @bonanitech | ||||
|  - fix workspace reference error in case of empty tabs (#4029) @HiroyasuNishiyama | ||||
|  - Disable delete tab menu when single tab exists (#4030) @HiroyasuNishiyama | ||||
|  - Disable hide all menu if all tabs hidden (#4031) @HiroyasuNishiyama | ||||
|  - fix hide subflow tooltip (#4033) @HiroyasuNishiyama | ||||
|  - Fix disabled menu items in project feature (#4027) @kazuhitoyokoi | ||||
|  - Let themes change radialMenu text colors (#3995) @bonanitech | ||||
|  - Add Japanese translations for v3.0.3 (#4012) @kazuhitoyokoi | ||||
|  - Add Japanese translation for v3.1.0-beta.0 (#3997) @kazuhitoyokoi | ||||
|  - Add Japanese translation for v3.1.0-beta.0 (#3916) @kazuhitoyokoi | ||||
|  - Hide subflow category after deleting subflow (#3980) @kazuhitoyokoi | ||||
|  - Prevent dbl-click opening node edit dialog with text selected (#3970) @knolleary | ||||
|  - Handle replacing unknown node inside group or subflow (#3921) @knolleary | ||||
|  - Fix #3939, red border red-ui-typedInput-container (#3949) @Steveorevo | ||||
|  - i18n item URL copy notification & add Japanese message (#3946) @HiroyasuNishiyama | ||||
|  - add Japanese message for item url copy actions (#3947) @HiroyasuNishiyama | ||||
|  - Fix autocomplete entry for responseUrl (#3884) @knolleary | ||||
|  - Fix Japanese translation for JSONata editor (#3872) @HiroyasuNishiyama | ||||
|  - Fix search type with spaces (#3841) @Steve-Mcl | ||||
|  - Fix error hanndling of JSONata expression editor for extended functions (#3871) @HiroyasuNishiyama | ||||
|  - Add button type to the adding SSH key button (#3866) @kazuhitoyokoi | ||||
|  - Check radio button as default in project dialog (#3879) @kazuhitoyokoi | ||||
|  - Add $clone as supported function (#3874) @HiroyasuNishiyama | ||||
|  - Env var jsonata (#3807) @HiroyasuNishiyama | ||||
|  - Add Japanese translation for v3.0.2 (#3852) @kazuhitoyokoi | ||||
| #### 0.15.0: Milestone Release | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - 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 | ||||
|  - Rename package var to avoid strict mode error (#4020) @knolleary | ||||
|  - Fix typos in settings.js (#4013) @ypid | ||||
|  - Ensure credentials object is removed before returning node in getFlow request (#3971) @knolleary | ||||
|  - Ignore commit error in project feature (#3987) @kazuhitoyokoi | ||||
|  - Update dependencies (#3969) @knolleary | ||||
|  - Add check that node sends object rather than primitive type (#3909) @knolleary | ||||
|  - Ensure key_path is quoted in GIT_SSH_COMMAND in case of spaces in pathname (#3912) @knolleary | ||||
|  - 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 | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - Catch: fix typo in catch.html (#3965) @we11adam | ||||
|  - Change: Fix change node overwriting msg with itself (#3899) @dceejay | ||||
|  - Comment node: Clarify where the text will appear (#4004) @dirkjanfaber | ||||
|  - 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 | ||||
|  - Function: Limit number of ports in function node (#3886) @kazuhitoyokoi | ||||
|  - Function: Remove dot from variable name for external module in function node (#3880) @kazuhitoyokoi | ||||
|  - Function: add function node monaco types util and promisify (#3868) @Steve-Mcl | ||||
|  - HTTP In: Ensure msg.req.headers is enumerable (#3908) @knolleary | ||||
|  - HTTP Request: Support form-data arrays (#3991) @hardillb | ||||
|  - HTTP Request: Fix httprequest tests to be more lenient on error message (#3922) @knolleary | ||||
|  - HTTP Request: Add missing property to node object HTTPRequest (#3842) @hardillb | ||||
|  - HTTP Request/Response: Support sortable list on property UI of http request and http response nodes (#3857) @kazuhitoyokoi | ||||
|  - 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 | ||||
|  - MQTT: Fix pull-down menus of MQTT configuration node (#3890) @kazuhitoyokoi | ||||
|  - MQTT: Prevent invalid mqtt birth topic crashing node-red (#3869) @Steve-Mcl | ||||
|  - MQTT: ensure sessionExpiry(Interval) is applied (#3840) @Steve-Mcl | ||||
|  - MQTT: Fix mqtt nodes not reconnecting on modified-flows deploy (#3992) @knolleary | ||||
|  - MQTT: fix single subscription mqtt node status (#3966) @Steve-Mcl | ||||
|  - Range: Add drop mode to range node (#3935) @dceejay | ||||
|  - Remove done from describe (#3873) @HiroyasuNishiyama | ||||
|  - Split node: avoid duplicate done call for buffer split (#4000) @knolleary | ||||
|  - Status: Fix typo in 25-status.html (#3981) @kazuhitoyokoi | ||||
|  - TCP Node: ensure newline substitution applies to whole message (#4009) @dceejay | ||||
|  - 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 | ||||
|  | ||||
| #### 3.0.2: Maintenance Release | ||||
|  - Increase default apiMaxLength to 5mb and add to default settings Closes #1001 | ||||
|  - Add v2 /flows api and deploy-overwrite protection | ||||
|  - Encrypt credentials by default | ||||
|  - Ensure errors thrown by RED.events handlers don't percolate up | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - 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 | ||||
|  - Hide scrollbars until they're needed (#3808) @bonanitech | ||||
|  - Include junctions/groups when exporting subflows plus related fixes (#3816) @knolleary | ||||
|  - remove console.log (#3820) @Steve-Mcl | ||||
|  - Mark nodes as changed when they are moved | ||||
|  - Added parent containment option for draggable. (#1006) | ||||
|  - Ignore bidi event handling on non-existent and non-Input elements Closes #999 | ||||
|  - Remove list of flows from menu | ||||
|  - Allow nodes to be imported with their credentials | ||||
|  - Add workspace search option | ||||
|  - Add scrollOnAdd option to editableList | ||||
|  - Add swift markup to editor for open whisk node | ||||
|  - 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 | ||||
|  | ||||
|  - Register subflow module instance node with parent flow (#3818) @knolleary | ||||
|  - Always log node warnings on start without requiring -v | ||||
|  - Add support for loading scoped node modules. Closes #885 | ||||
|  - Add process.env.PORT to settings.js | ||||
|  - Clear node context on deploy. Closes #870 | ||||
|  - Enable finer grained permissions in adminAuth | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - HTTP Request: Allow HTTP Headers not in spec (#3776) @hardillb | ||||
|  | ||||
| #### 3.0.1: Maintenance Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Allow codeEditor theme to be set even if `codeEditor` is not set in settings.js (#3794) @Steve-Mcl | ||||
|  - Sys info (diagnostics report) amendments (#3793) @Steve-Mcl | ||||
|  - 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 | ||||
|  - Properly position quick-add dialog in all cases (#3786) @knolleary | ||||
|  - Ensure quick-add dialog does not obscure ghost node when shifted (#3785) @knolleary | ||||
|  - Remove use of Object.hasOwn (#3784) @knolleary | ||||
|  | ||||
| #### 3.0.0: Milestone Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - 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 | ||||
|  - 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 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 | ||||
|  - Do not generate new node-ids when pasting a cut flow (#3729) @knolleary | ||||
|  - Fix to prevent node from moving out of workspace (#3731) @HiroyasuNishiyama | ||||
|  - Don't let themes change disabled config node background color (#3736) @bonanitech | ||||
|  - Move colors left behind in #3692 to CSS variables (#3737) @bonanitech | ||||
|  - Fix handling of global debug message (#3733) @HiroyasuNishiyama | ||||
|  - Fix label overflow @ config-node palette (#3730) @ralphwetzel | ||||
|  - Fix defaulting to monaco if settings does not contain codeEditor (#3732) @knolleary | ||||
|  - Disable keyboard shortcut mapping when showing Edit[..]Dialog (#3700) @ralphwetzel | ||||
|  - 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 | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Do not remove unknown credentials of Subflow Modules (#3728) @knolleary | ||||
|  - Add missing entries from beta.4 changelog (#3721) @knolleary | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - Change: Fix change node, not handling from field properly when using context (#3754) @Fadoli | ||||
|  - 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 | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Move all colours to CSS variables (#3692) @bonanitech | ||||
|  - Fix clicking on node in workspace to hide context menu (#3696) @knolleary | ||||
|  - Fix credential type input item of subflow template (#3703) @HiroyasuNishiyama | ||||
|  - Add option flag `reimport` to `importNodes` (#3718) @Steve-Mcl | ||||
|  - Update german translation (#3691) @Dennis14e | ||||
|  - List welcome tours in help sidebar (#3717) @knolleary | ||||
|  - Ensure 'hidden flow' count doesn't include subflows (#3715) @knolleary | ||||
|  - Fix Chinese translate (#3706) @hotlong | ||||
|  - Fix use default button for node icon (#3714) @kazuhitoyokoi | ||||
|  - Fix select boxes vertical alignment (#3698) @bonanitech | ||||
|  - Ensure workspace clean after undoing dropped node (#3708) @Steve-Mcl | ||||
|  - Use solid colour as config node icon background to hide text overflow (#3710) @Steve-Mcl | ||||
|  - Increase quick-add height to reveal 2 most recent entries (#3711) @Steve-Mcl | ||||
|  - Set default editor to monaco in absence of user preference (#3702) @knolleary | ||||
|  - Add Japanese translations for v3.0-beta.3 (#3688) @kazuhitoyokoi | ||||
|  - Fix handling of spacebar inside JSON visual editor (#3687) @knolleary | ||||
|  - Fix menu padding to handle both icons and submenus (#3686) @knolleary | ||||
|  - Include scroll offset when positioning quick-add dialog (#3685) @knolleary | ||||
|  | ||||
| Runtime | ||||
|  | ||||
|  - Allow flows to be stopped and started manually (#3719) @knolleary | ||||
|  - Import default export if node is a transpiled es module (#3669) @dschmidt | ||||
|  - Leave Monaco theme commented out by default (#3704) @bonanitech | ||||
|  | ||||
| Nodes | ||||
|  | ||||
|  - CSV: Fix CSV node to handle when outputting text fields (#3716) @dceejay | ||||
|  - Delay: Fix delay rate limit last timing when empty (#3709) @dceejay | ||||
|  - Link: Ensure link-call cache is updated when link-in is modified (#3695) @Steve-Mcl | ||||
|  - Join: Join node in reduce mode doesn't keep existing msg properties (#3670) @dceejay | ||||
|  - Template: Add support for evalulating {{env.<var>}} within a template node (#3690) @cow0w | ||||
|  | ||||
| #### 3.0.0-beta.3: Beta Release | ||||
|  | ||||
| Editor | ||||
|  | ||||
|  - Add Right-Click content menu (#3678) @knolleary | ||||
|  - 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 | ||||
|  - Fix undoing junction to subflow (#3653) @HiroyasuNishiyama | ||||
|  - Fix conversion of junction to subflow (#3652) @HiroyasuNishiyama | ||||
|  - Fix to include junction to exported nodes (#3650) @HiroyasuNishiyama | ||||
|  - Fix z-index value for shade to cover nodes in palette (#3649) @kazuhitoyokoi | ||||
|  - Fix to extend escaped subflow category characters (#3647) @HiroyasuNishiyama | ||||
|  - Fix to sanitize tab name (#3646) @HiroyasuNishiyama | ||||
|  - Fix selector placement (#3644) @bonanitech | ||||
|  - Add Japanese translations for v3.0-beta.2 (#3622) @kazuhitoyokoi | ||||
|  - Fix new folder menu of save to library dialog (#3633) @HiroyasuNishiyama | ||||
|  - Fix layer of palette node (#3638) @HiroyasuNishiyama | ||||
|  - Fix to place a node dragged from palette within the workspace (#3637) @HiroyasuNishiyama | ||||
|  - Fix typo in CSS (#3628) @bonanitech | ||||
|  - Use the correct variable for the gutter text color (#3615) @bonanitech | ||||
|  - Enable config nodes to reference other config nodes | ||||
|  - Add Split/Join nodes | ||||
|  - Add Link nodes | ||||
|  - Add support to HTTP In node for PATCH requests. Closes #904 | ||||
|  - Add cookie handling to HTTP In and HTTP Response nodes | ||||
|  - Add repeat indicator to inject node label. Closes #887 | ||||
|  - Add javascript highlighter to template node | ||||
|  - Add optional timeout to exec node | ||||
|  - Add TLS node and update MQTT/HTTP nodes to use it | ||||
|  - Let trigger node also send last payload to arrive | ||||
|  - Add timestamp as a default typedInput and update Inject and change nodes to match, | ||||
|  - Add QoS option to MQTT In node | ||||
|  - Add status to exec spawn mode | ||||
|  - Add Move capability to Change node | ||||
|  - Update Serial node to support custom baud rates | ||||
|  - Add support for array-syntax in typedInput msg properties | ||||
|  - Add RED.util to Function node sandbox | ||||
|  - Capture error stack on node.error. Closes #879 | ||||
|  | ||||
|  | ||||
| Runtime | ||||
| Fixes | ||||
|  | ||||
|  - 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 | ||||
|  - Add error handling to all node definition api calls | ||||
|  - Handle null return from Function node in array of messages | ||||
|  - Defer loading of token sessions until they are accessed. Fixes #895 | ||||
|  - set pi gpio pin status correctly if set on start | ||||
|  - Prevent parent window scrolling when view is focused. Fixes #635 | ||||
|  - Handle missing tab nodes in a loaded flow config | ||||
|  - Ensure typedInput dropdown doesn't fall off the page | ||||
|  - Protect against node types with reserved names such as toString. Fixes #880 | ||||
|  - Do not rely on the HTML file to identify where nodes are registered from | ||||
|  - Preserve node properties on import | ||||
|  - Fix regression in delay node. topic based queue was emptying all the time instead of spreading out messages. | ||||
|  - Throw an error if a Function node adds an input event listener | ||||
|  - Fix hang on partial deploy with disconnected mqtt node | ||||
|  - TypedInput: preload type icons to ensure width calc correct | ||||
|  - Ensure tcp node creates a buffer of size 1 at least | ||||
|  - Return editorTheme default if value is undefined | ||||
|  - Fix RED.util.compareObjects for Function created objects and Buffers | ||||
|  - Ensure default settings copied to command-line specified userDir | ||||
|  | ||||
|  | ||||
| #### 3.0.0-beta.1: Beta Release | ||||
| #### 0.13.4: Maintenance 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. | ||||
|  - Add timed release mode to delay node | ||||
|  - Enable link splicing for when import_dragging nodes. Closes #811 | ||||
|  - Fix uncaught exception on deploy whilst node sending messages | ||||
|  - Deprecate old mqtt client and connection pool modules | ||||
|  - Change node: add bool/num types to change mode Closes #835 | ||||
|  - Validate fields that are `$(env-vars)` Closes #825 | ||||
|  - Handle missing config nodes when validating node properties | ||||
|  - Pi node - don't try to send data if closing | ||||
|  - Load node message catalog when added dynamically | ||||
|  - Split palette labels on spaces and hyphens when laying out | ||||
|  - Warn if editor routes are accessed but runtime not started Closes #816 | ||||
|  - Better handling of zero-length flow files Closes #819 | ||||
|  - Allow runtime calls to RED._ to specify other namespace | ||||
|  - Better right alignment of numerics in delay and trigger nodes | ||||
|  - Allow node modules to include example flows | ||||
|  - Create node_modules in userDir | ||||
|  - Ensure errors in node def functions don't break view rendering Fixes #815 | ||||
|  - Updated Inject node info with instructions for flow and global options | ||||
|  | ||||
|  | ||||
| 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 | ||||
| #### 0.13.3: Maintenance Release | ||||
|  | ||||
| Runtime | ||||
|  - Fix crash on repeated inject of invalid json payload | ||||
|  - Add binary mode to tail node | ||||
|  - Revert Cheerio to somewhat smaller version | ||||
|  - Add os/platform info to default debug | ||||
|  | ||||
|  - 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 | ||||
| #### 0.13.2: Maintenance Release | ||||
|  | ||||
| #### Older Releases | ||||
|  - Don't force reconnect mqtt client if message arrives (fixes the MQTT connect/disconnect endless cycle) | ||||
|  - Add -p/--port option to override listening port | ||||
|  - Invert config node filter toggle button colours so state is more obvious | ||||
|  - Add timeout to httprequest node | ||||
|  - Tidy up of all node info content - make style consistent | ||||
|  - Make jquery spinner element css consistent with other inputs | ||||
|  - tcp node add reply (to all) capability | ||||
|  - Allow the template node to be treated as plain text | ||||
|  - Validate MQTT In topics Fixes #792 | ||||
|  - httpNodeAuth should not block http options requests Fixes #793 | ||||
|  - Disable perMessageDeflate on WS servers - fixes 'zlib binding closed' error | ||||
|  - Clear trigger status icon on re-deploy | ||||
|  - Don't default inject payload to blank string | ||||
|  - Trigger node, add configurable reset | ||||
|  - Allow function properties in settings Fixes #790 - fixes use of httpNodeMiddleware | ||||
|  - Fix order of config dialog calls to save/creds/validate | ||||
|  - Add debounce to Pi GPIO node | ||||
|  | ||||
| Change logs for older releases are available on GitHub: https://github.com/node-red/node-red/releases | ||||
|  | ||||
|  | ||||
| #### 0.13.1: Maintenance Release | ||||
|  | ||||
|  - Revert wrapping of http request object | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.13.0: Milestone Release | ||||
|  | ||||
|  - Add 'previous value' option to Switch node | ||||
|  - Allow existing nodes to splice into links on drag | ||||
|  - CORS not properly configured on multiple http routes Fixes #783 | ||||
|  - Restore shift-drag to snap/unsnap to grid | ||||
|  - Moving nodes with keyboard should flag workspace dirty | ||||
|  - Notifications flagged as fixed should not be click-closable | ||||
|  - Rework config sidebar and deploy warning | ||||
|  - Wrap http request object to match http response object | ||||
|  - Add 'view' menu and reorganise a few things | ||||
|  - Allow shift-click to detach existing wires | ||||
|  - Splice nodes dragged from palette into links | ||||
|  - try to trim imported/dragged flows to [ ] | ||||
|  - Move version number as title of NR logo | ||||
|  - Moving nodes mark workspace as dirty | ||||
|  - Ok/Cancel edit dialogs with Ctrl-Enter/Escape | ||||
|  - Handle OSX Meta key when selecting nodes | ||||
|  - Add grid-alignment options | ||||
|  - Add oneditresize function definition | ||||
|  - Rename propertySelect to typedInput and add boolean opt | ||||
|  - Add propertySelect to switch node | ||||
|  - Add propertySelect support to Change node | ||||
|  - Add context/flow/global support to Function node | ||||
|  - Add node context/flow/global | ||||
|  - Add propertySelect jquery widget | ||||
|  - Add add/update/delete flow apis | ||||
|  - Allow core nodes dir to be provided to runtime via settings | ||||
|  - Tidy up API passed to node modules | ||||
|  - Move locale files under api/runtime components | ||||
|  - Add flow reload admin api | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.5: Maintenance Release | ||||
|  | ||||
|  - Add attribute capability to HTML parser node | ||||
|  - Add Pi Keyboard code node | ||||
|  - Fix for MQTT client connection cycling on partial deploy | ||||
|  - Fix for tcp node properly closing connections | ||||
|  - Update sentiment node dependencies | ||||
|  - Fix for file node handling of UTF8 extended characters | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.4: Maintenance Release | ||||
|  | ||||
|  - Add readOnly setting to prevent file writes in localfilesystem storage | ||||
|  - Support bcrypt for httpNodeAuth | ||||
|  - Pi no longer needs root workaround to access gpio | ||||
|  - Fix: Input File node will not retain the file name | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.3: Maintenance Release | ||||
|  | ||||
|  - Fixes for TCP Get node reconnect handling | ||||
|  - Clear delay node status on re-deploy | ||||
|  - Update Font-Awesome to v4.5 | ||||
|  - Fix trigger to block properly until reset | ||||
|  - Update example auth properties in settings.js | ||||
|  - Ensure httpNodeAuth doesn't get applied to admin routes | ||||
|  - TCP Get node not passing on existing msg properties | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.2: Maintenance Release | ||||
|  | ||||
|  - Enable touch-menu for links so they can be deleted | ||||
|  - Allow nodes to be installed by path name | ||||
|  - Fix basic authentication on httpNode/Admin/Static | ||||
|  - Handle errors thrown in Function node setTimeout/Interval | ||||
|  - Fix mqtt node lifecycle with partial deployments | ||||
|  - Update tcp node status on reconnect after timeout | ||||
|  - Debug node not handling null messages | ||||
|  - Kill processes run with exec node when flows redeployed | ||||
|  - Inject time spinner incrementing value incorrectly | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.1: Maintenance Release | ||||
|  | ||||
|  - Enable touch-menu for links so they can be deleted | ||||
|  - Allow nodes to be installed by path name | ||||
|  - Fix basic authentication on httpNode/Admin/Static | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.12.0: Milestone Release | ||||
|  | ||||
|  - Change/Switch rules now resize with dialog width | ||||
|  - Support for node 4.x | ||||
|  - Move to Express 4.x | ||||
|  - Copy default settings file to user dir on start up | ||||
|  - Config nodes can be scoped to a particular subflow/tab | ||||
|  - Comms link tolerates <5 second breaks in connection before notifying user | ||||
|  - MQTT node overhaul - add will/tls/birth message support | ||||
|  - Status node - to report status events from other nodes | ||||
|  - Error node can be targeted to specific other nodes | ||||
|  - JSON node can encode Array types | ||||
|  - Switch node regular expression rule can now be set to be case-insensitive | ||||
|  - HTTP In node can accept non-UTF8 payloads - will return a Buffer when appropriate | ||||
|  - Exec node configuration consistent regardless of the spawn option | ||||
|  - Function node can now display status icon/text | ||||
|  - CSV node can now handle arrays | ||||
|  - setInterval/clearInterval add to Function node | ||||
|  - Function node automatically clears all timers (setInterval/setTimeout) when the node is stopped | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.11.2: Maintenance Release | ||||
|  | ||||
|  - Allow XML parser options be set on the message | ||||
|  - Add 'mobile' category to the palette (no core nodes included) | ||||
|  - Allow a message catalog provide a partial translation | ||||
|  - Fix HTTP Node nls message id | ||||
|  - Remove delay spinner upper limit | ||||
|  - Update debug node output to include length of payload | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.11.1: Maintenance Release | ||||
|  | ||||
|  - Fix exclusive config node check when type not registered (prevented HTTP In node from being editable unless the swagger node was also installed) | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.11.0: Milestone Release | ||||
|  | ||||
|  - Add Node 0.12 support | ||||
|  - Internationalization support | ||||
|  - Editor UI refresh | ||||
|  - Add RBE node | ||||
|  - File node optionally creates path to file | ||||
|  - Function node can access `clearTimeout` | ||||
|  - Fix: Unable to login with 'read' permission | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.10: Maintenance Release | ||||
|  | ||||
|  - Fix permissions issue with packaged nrgpio script | ||||
| - Add better help message if deprecated node missing | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.9: Maintenance Release | ||||
|  | ||||
| Fix packaging of bin scripts | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.8: Maintenance Release | ||||
|  | ||||
| - Nodes moved out of core | ||||
|   - still included as a dependency: twitter, serial, email, feedparser | ||||
|  - no longer included: mongo, arduino, irc, redis | ||||
| - node icon defn can be a function | ||||
| - http_proxy support | ||||
| - httpNodeMiddleware setting | ||||
| - Trigger node ui refresh | ||||
| - editorTheme setting | ||||
| - Warn on deploy of unused config nodes | ||||
| - catch node prevents error loops | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.6: Maintenance Release | ||||
|  | ||||
| Changes: | ||||
|  - Performance improvements in editor | ||||
|  - Palette appearance update | ||||
|  - Warn on navigation with undeployed changes | ||||
|  - Disable undeployed node action buttons | ||||
|  - Disable subflow node action buttons | ||||
|  - Add Catch node | ||||
|  - Add logging functions to Function node | ||||
|  - Add send function to Function node | ||||
|  - Update Change node to support multiple rules | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.4: Maintenance Release | ||||
|  | ||||
| Changes: | ||||
|  | ||||
| - http request node passes on request url as msg.url | ||||
| - 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 | ||||
| - fix labelling of Raspberry Pi pins | ||||
| - allow email node to mark mail as read | ||||
| - fix saving library content | ||||
| - add node-red and node-red-pi start scripts | ||||
| - use $HOME/.node-red for user data unless specified otherwise (or existing data is found in install dir) | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.3: Maintenance Release | ||||
|  | ||||
| Fixes: | ||||
|  | ||||
|  - httpAdminAuth was too aggressively deprecated (ie removed); restoring with a console warning when used | ||||
|  - adds reporting of node.js version on start-up | ||||
|  - mongo node skip/limit options can be strings or numbers | ||||
|  - CSV parser passes through provided message object | ||||
|  | ||||
|  | ||||
|  | ||||
| #### 0.10.2: Maintenance Release | ||||
|  | ||||
| Fixes: | ||||
|  - subflow info sidebar more useful | ||||
|  - adds missing font-awesome file | ||||
|  - inject node day selection defaulted to invalid selection | ||||
|  - loading a flow with no tabs failed to add nodes to default tab | ||||
|   | ||||
| @@ -9,7 +9,7 @@ We welcome contributions, but request you follow these guidelines. | ||||
|  | ||||
| This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | ||||
| By participating, you are expected to uphold this code. Please report unacceptable | ||||
| behavior to the project's core team at team@nodered.org. | ||||
| behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core). | ||||
|  | ||||
| ## Raising issues | ||||
|  | ||||
| @@ -26,30 +26,33 @@ relevant nodes, press Ctrl-E and copy the flow data from the Export dialog. | ||||
| At a minimum, please include: | ||||
|  | ||||
|  - Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly. | ||||
|  - Version of Node.js - what does `node -v` say? | ||||
|  - Version of node.js - what does `node -v` say? | ||||
|  | ||||
| ## Feature requests | ||||
|  | ||||
| For feature requests, please raise them on the [forum](https://discourse.nodered.org). | ||||
| For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||
|  | ||||
| ## Pull-Requests | ||||
|  | ||||
| If you want to raise a pull-request with a new feature, or a refactoring | ||||
| of existing code, it may well get rejected if you haven't discussed it on | ||||
| the [forum](https://discourse.nodered.org) first. | ||||
| the [mailing list](https://groups.google.com/forum/#!forum/node-red) first. | ||||
|  | ||||
| All contributors need to sign the OpenJS Foundation's Contributor License Agreement. | ||||
| It is an online process and quick to do. If you raise a pull-request without | ||||
| having signed the CLA, you will be prompted to do so automatically. | ||||
| ### Contributor License Agreement | ||||
|  | ||||
| In order for us to accept pull-requests, the contributor must first complete | ||||
| a Contributor License Agreement (CLA). This clarifies the intellectual | ||||
| property license granted with any contribution. It is for your protection as a | ||||
| Contributor as well as the protection of IBM and its customers; it does not | ||||
| change your rights to use your own Contributions for any other purpose. | ||||
|  | ||||
| ### Code Branches | ||||
| You can download the CLAs here: | ||||
|  | ||||
| When raising a PR for a fix or a new feature, it is important to target the right branch. | ||||
|  - [individual](http://nodered.org/cla/node-red-cla-individual.pdf) | ||||
|  - [corporate](http://nodered.org/cla/node-red-cla-corporate.pdf) | ||||
|  | ||||
|  - `master` - this is the main branch for the latest stable release of Node-RED. All bug fixes for that release should target this branch. | ||||
|  - `v1.x` - this is the maintenance branch for the 1.x stream. If a fix *only* applies to 1.x, then it should target this branch. If it applies to the current stable release as well, target `master` first. We will then decide if it needs to be back ported to the 1.x stream. | ||||
|  - `dev` - this is the branch for new feature development targeting the next milestone release. | ||||
| If you are an IBMer, please contact us directly as the contribution process is | ||||
| slightly different. | ||||
|  | ||||
| ### Coding standards | ||||
|  | ||||
|   | ||||
							
								
								
									
										573
									
								
								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"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -15,35 +15,17 @@ | ||||
|  **/ | ||||
|  | ||||
| var path = require("path"); | ||||
| var fs = require("fs-extra"); | ||||
| var sass = require("sass"); | ||||
|  | ||||
| module.exports = function(grunt) { | ||||
|  | ||||
|     var nodemonArgs = ["-V"]; | ||||
|     var nodemonArgs = ["-v"]; | ||||
|     var flowFile = grunt.option('flowFile'); | ||||
|     if (flowFile) { | ||||
|         nodemonArgs.push(flowFile); | ||||
|         process.env.NODE_RED_ENABLE_PROJECTS=false; | ||||
|     } | ||||
|     var userDir = grunt.option('userDir'); | ||||
|     if (userDir) { | ||||
|         nodemonArgs.push("-u"); | ||||
|         nodemonArgs.push(userDir); | ||||
|     } | ||||
|  | ||||
|     var browserstack = grunt.option('browserstack'); | ||||
|     if (browserstack) { | ||||
|         process.env.BROWSERSTACK = true; | ||||
|     } | ||||
|     var nonHeadless = grunt.option('non-headless'); | ||||
|     if (nonHeadless) { | ||||
|         process.env.NODE_RED_NON_HEADLESS = true; | ||||
|     } | ||||
|     const pkg = grunt.file.readJSON('package.json'); | ||||
|     process.env.NODE_RED_PACKAGE_VERSION = pkg.version; | ||||
|     grunt.initConfig({ | ||||
|         pkg: pkg, | ||||
|         pkg: grunt.file.readJSON('package.json'), | ||||
|         paths: { | ||||
|             dist: ".dist" | ||||
|         }, | ||||
| @@ -55,29 +37,10 @@ module.exports = function(grunt) { | ||||
|                 ui: 'bdd', | ||||
|                 reporter: 'spec' | ||||
|             }, | ||||
|             all: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js","test/nodes/**/*_spec.js"] }, | ||||
|             core: { src: ["test/unit/_spec.js","test/unit/**/*_spec.js"]}, | ||||
|             all: { src: ['test/**/*_spec.js'] }, | ||||
|             core: { src: ["test/_spec.js","test/red/**/*_spec.js"]}, | ||||
|             nodes: { src: ["test/nodes/**/*_spec.js"]} | ||||
|         }, | ||||
|         webdriver: { | ||||
|             all: { | ||||
|                 configFile: 'test/editor/wdio.conf.js' | ||||
|             } | ||||
|         }, | ||||
|         nyc: { | ||||
|             options: { | ||||
|                 cwd: '.', | ||||
|                 include: ['packages/node_modules/**'], | ||||
|                 excludeNodeModules: false, | ||||
|                 exclude: ['packages/node_modules/@node-red/editor-client/**'], | ||||
|                 reporter: ['lcov', 'html','text-summary'], | ||||
|                 reportDir: 'coverage', | ||||
|                 all: true | ||||
|             }, | ||||
|             all:   { cmd: false, args: ['grunt', 'simplemocha:all'] }, | ||||
|             core:  { options: { exclude:['packages/node_modules/@node-red/editor-client/**', 'packages/node_modules/@node-red/nodes/**']},cmd: false, args: ['grunt', 'simplemocha:core'] }, | ||||
|             nodes: { cmd: false, args: ['grunt', 'simplemocha:nodes'] } | ||||
|         }, | ||||
|         jshint: { | ||||
|             options: { | ||||
|                 jshintrc:true | ||||
| @@ -92,20 +55,22 @@ module.exports = function(grunt) { | ||||
|                 //"loopfunc": true, // allow functions to be defined in loops | ||||
|                 //"sub": true       // don't warn that foo['bar'] should be written as foo.bar | ||||
|             }, | ||||
|             // all: [ | ||||
|             //     'Gruntfile.js', | ||||
|             //     'red.js', | ||||
|             //     'packages/**/*.js' | ||||
|             // ], | ||||
|             // core: { | ||||
|             //     files: { | ||||
|             //         src: [ | ||||
|             //             'Gruntfile.js', | ||||
|             //             'red.js', | ||||
|             //             'packages/**/*.js', | ||||
|             //         ] | ||||
|             //     } | ||||
|             // }, | ||||
|             all: [ | ||||
|                 'Gruntfile.js', | ||||
|                 'red.js', | ||||
|                 'red/**/*.js', | ||||
|                 'nodes/core/*/*.js', | ||||
|                 'editor/js/**/*.js' | ||||
|             ], | ||||
|             core: { | ||||
|                 files: { | ||||
|                     src: [ | ||||
|                         'Gruntfile.js', | ||||
|                         'red.js', | ||||
|                         'red/**/*.js' | ||||
|                     ] | ||||
|                 } | ||||
|             }, | ||||
|             nodes: { | ||||
|                 files: { | ||||
|                     src: [ 'nodes/core/*/*.js' ] | ||||
| @@ -113,7 +78,7 @@ module.exports = function(grunt) { | ||||
|             }, | ||||
|             editor: { | ||||
|                 files: { | ||||
|                     src: [ 'packages/node_modules/@node-red/editor-client/src/js/**/*.js' ] | ||||
|                     src: [ 'editor/js/**/*.js' ] | ||||
|                 } | ||||
|             }, | ||||
|             tests: { | ||||
| @@ -133,111 +98,60 @@ module.exports = function(grunt) { | ||||
|                 src: [ | ||||
|                     // Ensure editor source files are concatenated in | ||||
|                     // the right order | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/polyfills.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/jquery-addons.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/red.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/events.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/hooks.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/i18n.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/settings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/user.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/comms.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/runtime.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/text/bidi.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/text/format.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/state.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/plugins.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/nodes.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/font-awesome.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/history.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/validators.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/mermaid.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/utils.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/treeList.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/checkboxSet.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/menu.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/panels.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/popover.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/searchBox.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/stack.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/toggleButton.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/statusBar.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-annotations.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-navigator.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-info-outliner.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editor.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/panes/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/*.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/event-log.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tray.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/library.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/notifications.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/search.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actionList.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/group.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projects.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js" | ||||
|                     "editor/js/main.js", | ||||
|                     "editor/js/events.js", | ||||
|                     "editor/js/i18n.js", | ||||
|                     "editor/js/settings.js", | ||||
|                     "editor/js/user.js", | ||||
|                     "editor/js/comms.js", | ||||
|                     "editor/js/bidi/bidi.js", | ||||
|                     "editor/js/bidi/base-text-dir.js", | ||||
|                     "editor/js/bidi/format.js", | ||||
|                     "editor/js/bidi/numeric-shaping.js", | ||||
|                     "editor/js/ui/state.js", | ||||
|                     "editor/js/nodes.js", | ||||
|                     "editor/js/history.js", | ||||
|                     "editor/js/validators.js", | ||||
|                     "editor/js/ui/common/editableList.js", | ||||
|                     "editor/js/ui/common/menu.js", | ||||
|                     "editor/js/ui/common/popover.js", | ||||
|                     "editor/js/ui/common/searchBox.js", | ||||
|                     "editor/js/ui/common/tabs.js", | ||||
|                     "editor/js/ui/common/typedInput.js", | ||||
|                     "editor/js/ui/deploy.js", | ||||
|                     "editor/js/ui/keyboard.js", | ||||
|                     "editor/js/ui/workspaces.js", | ||||
|                     "editor/js/ui/view.js", | ||||
|                     "editor/js/ui/sidebar.js", | ||||
|                     "editor/js/ui/palette.js", | ||||
|                     "editor/js/ui/tab-info.js", | ||||
|                     "editor/js/ui/tab-config.js", | ||||
|                     "editor/js/ui/palette-editor.js", | ||||
|                     "editor/js/ui/editor.js", | ||||
|                     "editor/js/ui/tray.js", | ||||
|                     "editor/js/ui/clipboard.js", | ||||
|                     "editor/js/ui/library.js", | ||||
|                     "editor/js/ui/notifications.js", | ||||
|                     "editor/js/ui/search.js", | ||||
|                     "editor/js/ui/subflow.js", | ||||
|                     "editor/js/ui/touch/radialMenu.js" | ||||
|                 ], | ||||
|                 dest: "packages/node_modules/@node-red/editor-client/public/red/red.js" | ||||
|                 dest: "public/red/red.js" | ||||
|             }, | ||||
|             vendor: { | ||||
|                 files: { | ||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor/vendor.js": [ | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-3.5.1.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-migrate-3.3.0.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery-ui.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jquery/js/jquery.ui.touch-punch.min.js", | ||||
|                         "node_modules/marked/marked.min.js", | ||||
|                         "node_modules/dompurify/dist/purify.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/d3/d3.v3.min.js", | ||||
|                         "node_modules/i18next/i18next.min.js", | ||||
|                         "node_modules/i18next-http-backend/i18nextHttpBackend.min.js", | ||||
|                         "node_modules/jquery-i18next/jquery-i18next.min.js", | ||||
|                         "node_modules/jsonata/jsonata-es5.min.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/jsonata/formatter.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/ace/ace.js", | ||||
|                         "packages/node_modules/@node-red/editor-client/src/vendor/ace/ext-language_tools.js" | ||||
|                     "public/vendor/vendor.js": [ | ||||
|                         "editor/vendor/jquery/js/jquery-1.11.3.min.js", | ||||
|                         "editor/vendor/bootstrap/js/bootstrap.min.js", | ||||
|                         "editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js", | ||||
|                         "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js", | ||||
|                         "editor/vendor/marked/marked.min.js", | ||||
|                         "editor/vendor/d3/d3.v3.min.js", | ||||
|                         "editor/vendor/i18next/i18next.min.js" | ||||
|                     ], | ||||
|                     // "packages/node_modules/@node-red/editor-client/public/vendor/vendor.css": [ | ||||
|                     //     // TODO: resolve relative resource paths in | ||||
|                     //     //       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" | ||||
|                     "public/vendor/vendor.css": [ | ||||
|                         // TODO: resolve relative resource paths in | ||||
|                         //       bootstrap/FA/jquery | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
| @@ -245,62 +159,54 @@ module.exports = function(grunt) { | ||||
|         uglify: { | ||||
|             build: { | ||||
|                 files: { | ||||
|                     'packages/node_modules/@node-red/editor-client/public/red/red.min.js': 'packages/node_modules/@node-red/editor-client/public/red/red.js', | ||||
|                     'packages/node_modules/@node-red/editor-client/public/red/main.min.js': 'packages/node_modules/@node-red/editor-client/public/red/main.js', | ||||
|                     'packages/node_modules/@node-red/editor-client/public/vendor/ace/mode-jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/mode-jsonata.js', | ||||
|                     'packages/node_modules/@node-red/editor-client/public/vendor/ace/snippets/jsonata.js': 'packages/node_modules/@node-red/editor-client/src/vendor/jsonata/snippets-jsonata.js' | ||||
|                     'public/red/red.min.js': 'public/red/red.js' | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         sass: { | ||||
|             build: { | ||||
|                 options: { | ||||
|                     implementation: sass, | ||||
|                     outputStyle: 'compressed' | ||||
|                 }, | ||||
|                 files: [{ | ||||
|                     dest: 'packages/node_modules/@node-red/editor-client/public/red/style.min.css', | ||||
|                     src: 'packages/node_modules/@node-red/editor-client/src/sass/style.scss' | ||||
|                     dest: 'public/red/style.min.css', | ||||
|                     src: 'editor/sass/style.scss' | ||||
|                 }, | ||||
|                 { | ||||
|                     dest: 'public/vendor/bootstrap/css/bootstrap.min.css', | ||||
|                     src: 'editor/vendor/bootstrap/css/bootstrap.css' | ||||
|                 }] | ||||
|             } | ||||
|         }, | ||||
|         jsonlint: { | ||||
|             messages: { | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/nodes/locales/**/*.json', | ||||
|                     'packages/node_modules/@node-red/editor-client/locales/**/*.json', | ||||
|                     'packages/node_modules/@node-red/runtime/locales/**/*.json' | ||||
|                 ] | ||||
|             }, | ||||
|             keymaps: { | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/js/keymap.json' | ||||
|                     'nodes/core/locales/en-US/messages.json', | ||||
|                     'red/api/locales/en-US/editor.json', | ||||
|                     'red/runtime/locales/en-US/runtime.json' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         attachCopyright: { | ||||
|             js: { | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/public/red/red.min.js', | ||||
|                     'packages/node_modules/@node-red/editor-client/public/red/main.min.js' | ||||
|                     'public/red/red.min.js' | ||||
|                 ] | ||||
|             }, | ||||
|             css: { | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/public/red/style.min.css' | ||||
|                     'public/red/style.min.css' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         clean: { | ||||
|             build: { | ||||
|                 src: [ | ||||
|                     "packages/node_modules/@node-red/editor-client/public/red", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/index.html", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/favicon.ico", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/icons", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/vendor", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node", | ||||
|                     "packages/node_modules/@node-red/editor-client/public/types/node-red", | ||||
|                     "public/red", | ||||
|                     "public/index.html", | ||||
|                     "public/favicon.ico", | ||||
|                     "public/icons", | ||||
|                     "public/vendor" | ||||
|                 ] | ||||
|             }, | ||||
|             release: { | ||||
| @@ -312,36 +218,24 @@ module.exports = function(grunt) { | ||||
|         watch: { | ||||
|             js: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/js/**/*.js' | ||||
|                     'editor/js/**/*.js' | ||||
|                 ], | ||||
|                 tasks: ['copy:build','concat',/*'uglify',*/ 'attachCopyright:js'] | ||||
|                 tasks: ['concat','uglify','attachCopyright:js'] | ||||
|             }, | ||||
|             sass: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/sass/**/*.scss' | ||||
|                     'editor/sass/**/*.scss' | ||||
|                 ], | ||||
|                 tasks: ['sass','attachCopyright:css'] | ||||
|             }, | ||||
|             json: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/nodes/locales/**/*.json', | ||||
|                     'packages/node_modules/@node-red/editor-client/locales/**/*.json', | ||||
|                     'packages/node_modules/@node-red/runtime/locales/**/*.json' | ||||
|                     'nodes/core/locales/en-US/messages.json', | ||||
|                     'red/api/locales/en-US/editor.json', | ||||
|                     'red/runtime/locales/en-US/runtime.json' | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:messages'] | ||||
|             }, | ||||
|             keymaps: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/js/keymap.json' | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:keymaps','copy:build'] | ||||
|             }, | ||||
|             tours: { | ||||
|                 files: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/tours/**/*.js' | ||||
|                 ], | ||||
|                 tasks: ['copy:build'] | ||||
|             }, | ||||
|             misc: { | ||||
|                 files: [ | ||||
|                     'CHANGELOG.md' | ||||
| @@ -353,13 +247,12 @@ module.exports = function(grunt) { | ||||
|         nodemon: { | ||||
|             /* uses .nodemonignore */ | ||||
|             dev: { | ||||
|                 script: 'packages/node_modules/node-red/red.js', | ||||
|                 script: 'red.js', | ||||
|                 options: { | ||||
|                     args: nodemonArgs, | ||||
|                     ext: 'js,html,json', | ||||
|                     watch: [ | ||||
|                         'packages/node_modules', | ||||
|                         '!packages/node_modules/@node-red/editor-client' | ||||
|                         'red','nodes' | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
| @@ -376,77 +269,62 @@ module.exports = function(grunt) { | ||||
|  | ||||
|         copy: { | ||||
|             build: { | ||||
|                 files:[ | ||||
|                     { | ||||
|                         src: 'packages/node_modules/@node-red/editor-client/src/js/main.js', | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/main.js' | ||||
|                     }, | ||||
|                     { | ||||
|                         src: 'packages/node_modules/@node-red/editor-client/src/js/keymap.json', | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/keymap.json' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/images', | ||||
|                 files:[{ | ||||
|                     cwd: 'editor/images', | ||||
|                     src: '**', | ||||
|                     expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/images/' | ||||
|                     dest: 'public/red/images/' | ||||
|                 }, | ||||
|                 { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/vendor', | ||||
|                     cwd: 'editor/vendor', | ||||
|                     src: [ | ||||
|                         'ace/**', | ||||
|                             'jquery/css/base/**', | ||||
|                             'font-awesome/**', | ||||
|                             'monaco/dist/**', | ||||
|                             'monaco/types/extraLibs.js', | ||||
|                             'monaco/style.css', | ||||
|                             'monaco/monaco-bootstrap.js' | ||||
|                         //'bootstrap/css/**', | ||||
|                         'bootstrap/img/**', | ||||
|                         'jquery/css/**', | ||||
|                         'font-awesome/**' | ||||
|                     ], | ||||
|                     expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/' | ||||
|                     dest: 'public/vendor/' | ||||
|                 }, | ||||
|                 { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src', | ||||
|                         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', | ||||
|                     cwd: 'editor/icons', | ||||
|                     src: '**', | ||||
|                     expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/icons/' | ||||
|                     dest: 'public/icons/' | ||||
|                 }, | ||||
|                 { | ||||
|                     expand: true, | ||||
|                         src: ['packages/node_modules/@node-red/editor-client/src/index.html','packages/node_modules/@node-red/editor-client/src/favicon.ico'], | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/', | ||||
|                     src: ['editor/index.html','editor/favicon.ico'], | ||||
|                     dest: 'public/', | ||||
|                     flatten: true | ||||
|                 }, | ||||
|                 { | ||||
|                     src: 'CHANGELOG.md', | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/about' | ||||
|                     }, | ||||
|                     { | ||||
|                         src: 'CHANGELOG.md', | ||||
|                         dest: 'packages/node_modules/node-red/' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/ace/bin/', | ||||
|                         src: '**', | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/' | ||||
|                     }, | ||||
|                     { | ||||
|                         cwd: 'packages/node_modules/@node-red/editor-client/src/tours', | ||||
|                         src: '**', | ||||
|                         expand: true, | ||||
|                         dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/' | ||||
|                     dest: 'public/red/about' | ||||
|                 } | ||||
|                 ] | ||||
|             }, | ||||
|             release: { | ||||
|                 files: [{ | ||||
|                     mode: true, | ||||
|                     expand: true, | ||||
|                     src: [ | ||||
|                         '*.md', | ||||
|                         'LICENSE', | ||||
|                         'package.json', | ||||
|                         'settings.js', | ||||
|                         'red.js', | ||||
|                         'lib/.gitignore', | ||||
|                         'nodes/*.demo', | ||||
|                         'nodes/core/**', | ||||
|                         'red/**', | ||||
|                         'public/**', | ||||
|                         'editor/templates/**', | ||||
|                         'bin/**' | ||||
|                     ], | ||||
|                     dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>') | ||||
|                 }] | ||||
|             } | ||||
|         }, | ||||
|         chmod: { | ||||
| @@ -454,96 +332,20 @@ module.exports = function(grunt) { | ||||
|                 mode: '755' | ||||
|             }, | ||||
|             release: { | ||||
|                 // Target-specific file/dir lists and/or options go here. | ||||
|                 src: [ | ||||
|                     "packages/node_modules/@node-red/nodes/core/hardware/nrgpio", | ||||
|                     "packages/node_modules/@node-red/runtime/lib/storage/localfilesystem/projects/git/node-red-*sh" | ||||
|                     path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*') | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         'npm-command': { | ||||
|             options: { | ||||
|                 cmd: "pack", | ||||
|                 cwd: "<%= paths.dist %>/modules" | ||||
|             }, | ||||
|             'node-red': { options: { args: [__dirname+'/packages/node_modules/node-red'] } }, | ||||
|             '@node-red/editor-api': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-api'] } }, | ||||
|             '@node-red/editor-client': { options: { args: [__dirname+'/packages/node_modules/@node-red/editor-client'] } }, | ||||
|             '@node-red/nodes': { options: { args: [__dirname+'/packages/node_modules/@node-red/nodes'] } }, | ||||
|             '@node-red/registry': { options: { args: [__dirname+'/packages/node_modules/@node-red/registry'] } }, | ||||
|             '@node-red/runtime': { options: { args: [__dirname+'/packages/node_modules/@node-red/runtime'] } }, | ||||
|             '@node-red/util': { options: { args: [__dirname+'/packages/node_modules/@node-red/util'] } } | ||||
|  | ||||
|  | ||||
|         }, | ||||
|         mkdir: { | ||||
|             release: { | ||||
|                 options: { | ||||
|                     create: ['<%= paths.dist %>/modules'] | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         compress: { | ||||
|             release: { | ||||
|                 options: { | ||||
|                     archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip' | ||||
|                 }, | ||||
|                 expand: true, | ||||
|                 cwd: 'packages/node_modules/', | ||||
|                 src: [ | ||||
|                     '**', | ||||
|                     '!@node-red/editor-client/src/**' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         jsdoc : { | ||||
|             modules: { | ||||
|                 src: [ | ||||
|                     'API.md', | ||||
|                     'packages/node_modules/node-red/lib/red.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/index.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/api/*.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/events.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/hooks.js', | ||||
|                     'packages/node_modules/@node-red/util/**/*.js', | ||||
|                     'packages/node_modules/@node-red/editor-api/lib/index.js', | ||||
|                     'packages/node_modules/@node-red/editor-api/lib/auth/index.js', | ||||
|                     'packages/node_modules/@node-red/registry/lib/index.js' | ||||
|                 ], | ||||
|                 options: { | ||||
|                     destination: 'docs', | ||||
|                     configure: './jsdoc.json', | ||||
|                     fred: "hi there" | ||||
|                 } | ||||
|             }, | ||||
|             _editor: { | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/editor-client/src/js' | ||||
|                     ], | ||||
|                 options: { | ||||
|                     destination: 'packages/node_modules/@node-red/editor-client/docs', | ||||
|                     configure: './jsdoc.json' | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|         jsdoc2md: { | ||||
|             runtimeAPI: { | ||||
|                 options: { | ||||
|                     separators: true | ||||
|                 }, | ||||
|                 src: [ | ||||
|                     'packages/node_modules/@node-red/runtime/lib/index.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/api/*.js', | ||||
|                     'packages/node_modules/@node-red/runtime/lib/events.js' | ||||
|                 ], | ||||
|                 dest: 'packages/node_modules/@node-red/runtime/docs/api.md' | ||||
|             }, | ||||
|             nodeREDUtil: { | ||||
|                 options: { | ||||
|                     separators: true | ||||
|                 }, | ||||
|                 src: 'packages/node_modules/@node-red/util/**/*.js', | ||||
|                 dest: 'packages/node_modules/@node-red/util/docs/api.md' | ||||
|                 cwd: '<%= paths.dist %>/', | ||||
|                 src: ['node-red-<%= pkg.version %>/**'] | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| @@ -556,42 +358,16 @@ module.exports = function(grunt) { | ||||
|     grunt.loadNpmTasks('grunt-contrib-watch'); | ||||
|     grunt.loadNpmTasks('grunt-concurrent'); | ||||
|     grunt.loadNpmTasks('grunt-sass'); | ||||
|     grunt.loadNpmTasks('grunt-nodemon'); | ||||
|     grunt.loadNpmTasks('grunt-contrib-compress'); | ||||
|     grunt.loadNpmTasks('grunt-contrib-copy'); | ||||
|     grunt.loadNpmTasks('grunt-chmod'); | ||||
|     grunt.loadNpmTasks('grunt-jsonlint'); | ||||
|     if (fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { | ||||
|         grunt.loadNpmTasks('grunt-webdriver'); | ||||
|     } | ||||
|     grunt.loadNpmTasks('grunt-jsdoc'); | ||||
|     grunt.loadNpmTasks('grunt-jsdoc-to-markdown'); | ||||
|     grunt.loadNpmTasks('grunt-npm-command'); | ||||
|     grunt.loadNpmTasks('grunt-mkdir'); | ||||
|     grunt.loadNpmTasks('grunt-simple-nyc'); | ||||
|  | ||||
|     grunt.registerMultiTask('nodemon', 'Runs a nodemon monitor of your node.js server.', function () { | ||||
|         const nodemon = require('nodemon'); | ||||
|         this.async(); | ||||
|         const options = this.options(); | ||||
|         options.script = this.data.script; | ||||
|         let callback; | ||||
|         if (options.callback) { | ||||
|             callback = options.callback; | ||||
|             delete options.callback; | ||||
|         } else { | ||||
|             callback = function(nodemonApp) { | ||||
|                 nodemonApp.on('log', function (event) { | ||||
|                     console.log(event.colour); | ||||
|                 }); | ||||
|             }; | ||||
|         } | ||||
|         callback(nodemon(options)); | ||||
|     }); | ||||
|  | ||||
|     grunt.registerMultiTask('attachCopyright', function() { | ||||
|         var files = this.data.src; | ||||
|         var copyright = "/**\n"+ | ||||
|             " * Copyright OpenJS Foundation and other contributors, https://openjsf.org/\n"+ | ||||
|             " * Copyright 2013, 2015 IBM Corp.\n"+ | ||||
|             " *\n"+ | ||||
|             " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"+ | ||||
|             " * you may not use this file except in compliance with the License.\n"+ | ||||
| @@ -607,7 +383,7 @@ module.exports = function(grunt) { | ||||
|             " **/\n"; | ||||
|  | ||||
|         if (files) { | ||||
|             for (var i=0; i<files.length; i++) { | ||||
|             for (var i=0;i<files.length;i++) { | ||||
|                 var file = files[i]; | ||||
|                 if (!grunt.file.exists(file)) { | ||||
|                     grunt.log.warn('File '+ file + ' not found'); | ||||
| @@ -628,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', | ||||
|         'Sets NODE_ENV=development so non-minified assets are used', | ||||
|             function () { | ||||
| @@ -668,42 +412,23 @@ module.exports = function(grunt) { | ||||
|  | ||||
|     grunt.registerTask('default', | ||||
|         'Builds editor content then runs code style checks and unit tests on all components', | ||||
|         ['build','verifyPackageDependencies','jshint:editor','nyc:all']); | ||||
|  | ||||
|     grunt.registerTask('no-coverage', | ||||
|         'Builds editor content then runs code style checks and unit tests on all components without code coverage', | ||||
|         ['build','verifyPackageDependencies','jshint:editor','simplemocha:all']); | ||||
|  | ||||
|         ['build','test-core','test-editor','test-nodes']); | ||||
|  | ||||
|     grunt.registerTask('test-core', | ||||
|         'Runs code style check and unit tests on core runtime code', | ||||
|         ['build','nyc:core']); | ||||
|         ['jshint:core','simplemocha:core']); | ||||
|  | ||||
|     grunt.registerTask('test-editor', | ||||
|         'Runs code style check on editor code', | ||||
|         ['jshint:editor']); | ||||
|  | ||||
|     if (!fs.existsSync(path.join("node_modules", "grunt-webdriver"))) { | ||||
|         grunt.registerTask('test-ui', | ||||
|             'Builds editor content then runs unit tests on editor ui', | ||||
|             ['verifyUiTestDependencies']); | ||||
|     } else { | ||||
|         grunt.registerTask('test-ui', | ||||
|             'Builds editor content then runs unit tests on editor ui', | ||||
|             ['verifyUiTestDependencies','build','jshint:editor','webdriver:all']); | ||||
|     } | ||||
|  | ||||
|     grunt.registerTask('test-nodes', | ||||
|         'Runs unit tests on core nodes', | ||||
|         ['build','nyc:nodes']); | ||||
|         ['simplemocha:nodes']); | ||||
|  | ||||
|     grunt.registerTask('build', | ||||
|         'Builds editor content', | ||||
|         ['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']); | ||||
|  | ||||
|     grunt.registerTask('build-dev', | ||||
|         'Developer mode: build dev version', | ||||
|         ['clean:build','concat:build','concat:vendor','copy:build','sass:build','setDevEnv']); | ||||
|         ['clean:build','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy:build','attachCopyright']); | ||||
|  | ||||
|     grunt.registerTask('dev', | ||||
|         'Developer mode: run node-red, watch for source changes and build/restart', | ||||
| @@ -711,18 +436,6 @@ module.exports = function(grunt) { | ||||
|  | ||||
|     grunt.registerTask('release', | ||||
|         'Create distribution zip file', | ||||
|         ['build','verifyPackageDependencies','clean:release','mkdir:release','chmod:release','compress:release','pack-modules','generatePublishScript']); | ||||
|         ['build','clean:release','copy:release','chmod:release','compress:release']); | ||||
|  | ||||
|     grunt.registerTask('pack-modules', | ||||
|         'Create module pack files for release', | ||||
|         ['mkdir:release','npm-command']); | ||||
|  | ||||
|  | ||||
|     grunt.registerTask('coverage', | ||||
|         'Run Istanbul code test coverage task', | ||||
|         ['build','nyc:all']); | ||||
|  | ||||
|     grunt.registerTask('docs', | ||||
|         'Generates API documentation', | ||||
|         ['jsdoc']); | ||||
| }; | ||||
|   | ||||
							
								
								
									
										1
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						| @@ -1,4 +1,3 @@ | ||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org/ | ||||
|  | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|   | ||||
							
								
								
									
										28
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -2,19 +2,19 @@ | ||||
|  | ||||
| http://nodered.org | ||||
|  | ||||
| [](https://travis-ci.org/node-red/node-red) | ||||
| [](https://travis-ci.org/node-red/node-red) | ||||
| [](https://coveralls.io/r/node-red/node-red?branch=master) | ||||
|  | ||||
| Low-code programming for event-driven applications. | ||||
| A visual tool for wiring the Internet of Things. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
| Check out http://nodered.org/docs/getting-started/ for full instructions on getting | ||||
| started. | ||||
|  | ||||
| 1. `sudo npm install -g --unsafe-perm node-red` | ||||
| 1. `sudo npm install -g node-red` | ||||
| 2. `node-red` | ||||
| 3. Open <http://localhost:1880> | ||||
|  | ||||
| @@ -22,7 +22,8 @@ started. | ||||
|  | ||||
| More documentation can be found [here](http://nodered.org/docs). | ||||
|  | ||||
| For further help, or general discussion, please use the [Node-RED Forum](https://discourse.nodered.org) or [slack team](https://nodered.org/slack). | ||||
| For further help, or general discussion, please use the | ||||
| [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||
|  | ||||
| ## Developers | ||||
|  | ||||
| @@ -44,6 +45,9 @@ If you want to run the latest code from git, here's how to get started: | ||||
| 4. Run | ||||
|  | ||||
|         npm start | ||||
|    or | ||||
|  | ||||
|         node red.js | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| @@ -52,19 +56,17 @@ Before raising a pull-request, please read our | ||||
|  | ||||
| This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). | ||||
|  By participating, you are expected to uphold this code. Please report unacceptable | ||||
|  behavior to any of the project's core team at team@nodered.org. | ||||
|  behavior to any of the [project's core team](https://github.com/orgs/node-red/teams/core). | ||||
|  | ||||
| ## Authors | ||||
|  | ||||
| Node-RED is a project of the [OpenJS Foundation](http://openjsf.org). | ||||
| Node-RED is a creation of [IBM Emerging Technology](https://www.ibm.com/blogs/emerging-technology/). | ||||
|  | ||||
| It is maintained by: | ||||
|  | ||||
|  * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | ||||
|  * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | ||||
|  * And many others... | ||||
| * Nick O'Leary [@knolleary](http://twitter.com/knolleary) | ||||
| * Dave Conway-Jones [@ceejay](http://twitter.com/ceejay) | ||||
|  | ||||
| For more open-source projects from IBM, head over [here](http://ibm.github.io). | ||||
|  | ||||
| ## Copyright and license | ||||
|  | ||||
| Copyright OpenJS Foundation and other contributors, https://openjsf.org under [the Apache 2.0 license](LICENSE). | ||||
| Copyright 2013, 2016 IBM Corp. under [the Apache 2.0 license](LICENSE). | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| # Security Policy | ||||
|  | ||||
| ## Reporting a Vulnerability | ||||
|  | ||||
| Please report any potential security issues to `team@nodered.org`. This will notify the core project team who will respond accordingly. | ||||
| @@ -1,6 +1,6 @@ | ||||
| #!/bin/sh | ||||
| #!/bin/bash | ||||
| # | ||||
| # Copyright JS Foundation and other contributors, http://js.foundation | ||||
| # Copyright 2015 IBM Corp. | ||||
| # | ||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| # you may not use this file except in compliance with the License. | ||||
| @@ -29,16 +29,15 @@ do | ||||
| done | ||||
| 
 | ||||
| # Find the real location of this script | ||||
| CURRENT_PATH=$(pwd) | ||||
| SCRIPT_PATH=$(readlink -f "$0") | ||||
| while [ -h "${SCRIPT_PATH}" ]; do | ||||
|     cd "$(dirname "${SCRIPT_PATH}")" || exit 1 | ||||
|     P=$(basename "${SCRIPT_PATH}") | ||||
|     SCRIPT_PATH=$(readlink "${P}") | ||||
| CURRENT_PATH=`pwd` | ||||
| SCRIPT_PATH="${BASH_SOURCE[0]}"; | ||||
| while([ -h "${SCRIPT_PATH}" ]); do | ||||
|     cd "`dirname "${SCRIPT_PATH}"`" | ||||
|     SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")"; | ||||
| done | ||||
| cd "$(dirname "${SCRIPT_PATH}")" > /dev/null || exit 1 | ||||
| SCRIPT_PATH=$(pwd) | ||||
| cd "$CURRENT_PATH" || exit 1 | ||||
| cd "`dirname "${SCRIPT_PATH}"`" > /dev/null | ||||
| SCRIPT_PATH="`pwd`"; | ||||
| cd $CURRENT_PATH | ||||
| 
 | ||||
| # Run Node-RED | ||||
| exec /usr/bin/env node ${OPTIONS} ${SCRIPT_PATH}/../red.js ${ARGS} | ||||
| 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 | 
| Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B | 
| Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B | 
| Before Width: | Height: | Size: 2.3 KiB 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 | 
| Before Width: | Height: | Size: 1.6 KiB 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 | 
| Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 414 B | 
| Before Width: | Height: | Size: 671 B 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 | 
| Before Width: | Height: | Size: 736 B 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"); | ||||
|  * 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 | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| RED.text.format = (function() { | ||||
| 
 | ||||
| RED.bidi.format = (function() { | ||||
| 
 | ||||
|     var TextSegment = (function() { | ||||
|         var TextSegment = function (obj) { | ||||
| @@ -758,7 +759,7 @@ RED.text.format = (function() { | ||||
|                 { | ||||
|                         guiDir: isRtl ? "rtl" : "ltr", | ||||
|                         dir: "ltr", | ||||
|                         points: "/\\:." | ||||
|                         points: "/\\:.&<>" | ||||
|                 }; | ||||
|                 if (!parseOnly) { | ||||
|                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); | ||||
| @@ -876,7 +877,7 @@ RED.text.format = (function() { | ||||
|                 { | ||||
|                         guiDir: isRtl ? "rtl" : "ltr", | ||||
|                         dir: "ltr", | ||||
|                         points: ":?#/@.[]=" | ||||
|                         points: ":?#/@.[]=&<>" | ||||
|                 }; | ||||
|                 if (!parseOnly) { | ||||
|                     return stext.parseAndDisplayStructure(text, fArgs, !!isHtml, locale); | ||||
| @@ -1304,7 +1305,19 @@ RED.text.format = (function() { | ||||
|     } | ||||
| 
 | ||||
|     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 | ||||
|         * @param text - the structured text | ||||
|         * @param type - could be one of filepath, url, email | ||||
| @@ -1315,7 +1328,7 @@ RED.text.format = (function() { | ||||
|         getHtml: function (text, type, args, isRtl, locale) { | ||||
|             return getHandler(type).format(text, args, isRtl, true, locale); | ||||
|         }, | ||||
|         /*! | ||||
|         /** | ||||
|         * Handle Structured text correct display for a given HTML element. | ||||
|         * @param element - the element  : should be of type div contenteditable=true | ||||
|         * @param type - could be one of filepath, url, email | ||||
							
								
								
									
										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"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -28,15 +28,6 @@ RED.comms = (function() { | ||||
| 
 | ||||
|     function connectWS() { | ||||
|         active = true; | ||||
|         var wspath; | ||||
| 
 | ||||
|         if (RED.settings.apiRootUrl) { | ||||
|             var m = /^(https?):\/\/(.*)$/.exec(RED.settings.apiRootUrl); | ||||
|             if (m) { | ||||
|                 console.log(m); | ||||
|                 wspath = "ws"+(m[1]==="https"?"s":"")+"://"+m[2]+"comms"; | ||||
|             } | ||||
|         } else { | ||||
|         var path = location.hostname; | ||||
|         var port = location.port; | ||||
|         if (port.length !== 0) { | ||||
| @@ -44,8 +35,7 @@ RED.comms = (function() { | ||||
|         } | ||||
|         path = path+document.location.pathname; | ||||
|         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"); | ||||
|         pendingAuth = (auth_tokens!=null); | ||||
| @@ -58,7 +48,7 @@ RED.comms = (function() { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         ws = new WebSocket(wspath); | ||||
|         ws = new WebSocket(path); | ||||
|         ws.onopen = function() { | ||||
|             reconnectAttempts = 0; | ||||
|             if (errornotification) { | ||||
| @@ -74,31 +64,19 @@ RED.comms = (function() { | ||||
|             } | ||||
|         } | ||||
|         ws.onmessage = function(event) { | ||||
|             var message = JSON.parse(event.data); | ||||
|             if (message.auth) { | ||||
|                 if (pendingAuth) { | ||||
|                     if (message.auth === "ok") { | ||||
|             var msg = JSON.parse(event.data); | ||||
|             if (pendingAuth && msg.auth) { | ||||
|                 if (msg.auth === "ok") { | ||||
|                     pendingAuth = false; | ||||
|                     completeConnection(); | ||||
|                     } else if (message.auth === "fail") { | ||||
|                 } else if (msg.auth === "fail") { | ||||
|                     // anything else is an error...
 | ||||
|                     active = false; | ||||
|                     RED.user.login({updateMenu:true},function() { | ||||
|                         connectWS(); | ||||
|                     }) | ||||
|                 } | ||||
|                 } else if (message.auth === "fail") { | ||||
|                     // Our current session has expired
 | ||||
|                     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) { | ||||
|             } else if (msg.topic) { | ||||
|                 for (var t in subscriptions) { | ||||
|                     if (subscriptions.hasOwnProperty(t)) { | ||||
|                         var re = new RegExp("^"+t.replace(/([\[\]\?\(\)\\\\$\^\*\.|])/g,"\\$1").replace(/\+/g,"[^/]+").replace(/\/#$/,"(\/.*)?")+"$"); | ||||
| @@ -113,8 +91,6 @@ RED.comms = (function() { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|         ws.onclose = function() { | ||||
|             if (!active) { | ||||
| @@ -142,10 +118,10 @@ RED.comms = (function() { | ||||
|                         connectWS(); | ||||
|                     } else { | ||||
|                         var msg = RED._("notification.errors.lostConnectionReconnect",{time: connectCountdown})+' <a href="#">'+ RED._("notification.errors.lostConnectionTry")+'</a>'; | ||||
|                         errornotification.update(msg,{silent:true}); | ||||
|                         $(errornotification).find("a").on("click", function(e) { | ||||
|                         errornotification.update(msg); | ||||
|                         $(errornotification).find("a").click(function(e) { | ||||
|                             e.preventDefault(); | ||||
|                             errornotification.update(RED._("notification.errors.lostConnection"),{silent:true}); | ||||
|                             errornotification.update(RED._("notification.errors.lostConnection")); | ||||
|                             clearInterval(connectCountdownTimer); | ||||
|                             connectWS(); | ||||
|                         }) | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 | ||||
|  * Copyright 2015, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -32,19 +32,14 @@ | ||||
|              } | ||||
|          } | ||||
|      } | ||||
|      function emit() { | ||||
|          var evt = arguments[0] | ||||
|          var args = Array.prototype.slice.call(arguments,1); | ||||
|          if (RED.events.DEBUG) { | ||||
|              console.warn(evt,args); | ||||
|          } | ||||
|      function emit(evt,arg) { | ||||
|          if (handlers[evt]) { | ||||
|              for (var i=0;i<handlers[evt].length;i++) { | ||||
|                  try { | ||||
|                      handlers[evt][i].apply(null, args); | ||||
|                      handlers[evt][i](arg); | ||||
|                  } catch(err) { | ||||
|                      console.warn("RED.events.emit error: ["+evt+"] "+(err.toString())); | ||||
|                      console.warn(err); | ||||
|                      console.log("RED.events.emit error: ["+evt+"] "+(err.toString())); | ||||
|                      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
									
								
							
							
						
						| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright JS Foundation and other contributors, http://js.foundation
 | ||||
|  * Copyright 2014 IBM, Antoine Aflalo | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -18,8 +18,6 @@ | ||||
| RED.settings = (function () { | ||||
| 
 | ||||
|     var loadedSettings = {}; | ||||
|     var userSettings = {}; | ||||
|     var pendingSave; | ||||
| 
 | ||||
|     var hasLocalStorage = function () { | ||||
|         try { | ||||
| @@ -33,50 +31,27 @@ RED.settings = (function () { | ||||
|         if (!hasLocalStorage()) { | ||||
|             return; | ||||
|         } | ||||
|         if (key === "auth-tokens") { | ||||
|         localStorage.setItem(key, JSON.stringify(value)); | ||||
|         } else { | ||||
|             RED.utils.setMessageProperty(userSettings,key,value); | ||||
|             saveUserSettings(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     /** | ||||
|      * If the key is not set in the localStorage it returns <i>undefined</i> | ||||
|      * Else return the JSON parsed value | ||||
|      * @param key | ||||
|      * @param defaultIfUndefined | ||||
|      * @returns {*} | ||||
|      */ | ||||
|     var get = function (key,defaultIfUndefined) { | ||||
|     var get = function (key) { | ||||
|         if (!hasLocalStorage()) { | ||||
|             return undefined; | ||||
|         } | ||||
|         if (key === "auth-tokens") { | ||||
|         return JSON.parse(localStorage.getItem(key)); | ||||
|         } else { | ||||
|             var v; | ||||
|             try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {} | ||||
|             if (v === undefined) { | ||||
|                 try { v = RED.utils.getMessageProperty(RED.settings,key); } catch(err) {} | ||||
|             } | ||||
|             if (v === undefined) { | ||||
|                 v = defaultIfUndefined; | ||||
|             } | ||||
|             return v; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     var remove = function (key) { | ||||
|         if (!hasLocalStorage()) { | ||||
|             return; | ||||
|         } | ||||
|         if (key === "auth-tokens") { | ||||
|         localStorage.removeItem(key); | ||||
|         } else { | ||||
|             delete userSettings[key]; | ||||
|             saveUserSettings(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     var setProperties = function(data) { | ||||
| @@ -93,26 +68,18 @@ RED.settings = (function () { | ||||
|         loadedSettings = data; | ||||
|     }; | ||||
| 
 | ||||
|     var setUserSettings = function(data) { | ||||
|         userSettings = data; | ||||
|     } | ||||
| 
 | ||||
|     var init = function (options, done) { | ||||
|     var init = function (done) { | ||||
|         var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search); | ||||
|         if (accessTokenMatch) { | ||||
|             var accessToken = accessTokenMatch[1]; | ||||
|             RED.settings.set("auth-tokens",{access_token: accessToken}); | ||||
|             window.location.search = ""; | ||||
|         } | ||||
|         RED.settings.apiRootUrl = options.apiRootUrl; | ||||
| 
 | ||||
|         $.ajaxSetup({ | ||||
|             beforeSend: function(jqXHR,settings) { | ||||
|                 // Only attach auth header for requests to relative paths
 | ||||
|                 if (!/^\s*(https?:|\/|\.)/.test(settings.url)) { | ||||
|                     if (options.apiRootUrl) { | ||||
|                         settings.url = options.apiRootUrl+settings.url; | ||||
|                     } | ||||
|                     var auth_tokens = RED.settings.get("auth-tokens"); | ||||
|                     if (auth_tokens) { | ||||
|                         jqXHR.setRequestHeader("Authorization","Bearer "+auth_tokens.access_token); | ||||
| @@ -125,7 +92,7 @@ RED.settings = (function () { | ||||
|         load(done); | ||||
|     } | ||||
| 
 | ||||
|     var refreshSettings = function(done) { | ||||
|     var load = function(done) { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept": "application/json" | ||||
| @@ -135,79 +102,25 @@ RED.settings = (function () { | ||||
|             url: 'settings', | ||||
|             success: function (data) { | ||||
|                 setProperties(data); | ||||
|                 done(null, data); | ||||
|                 if (RED.settings.user && RED.settings.user.anonymous) { | ||||
|                     RED.settings.remove("auth-tokens"); | ||||
|                 } | ||||
|                 console.log("Node-RED: " + data.version); | ||||
|                 done(); | ||||
|             }, | ||||
|             error: function(jqXHR,textStatus,errorThrown) { | ||||
|                 if (jqXHR.status === 401) { | ||||
|                     if (/[?&]access_token=(.*?)(?:$|&)/.test(window.location.search)) { | ||||
|                         window.location.search = ""; | ||||
|                     } | ||||
|                     RED.user.login(function() { refreshSettings(done); }); | ||||
|                     RED.user.login(function() { load(done); }); | ||||
|                 } else { | ||||
|                     console.log("Unexpected error loading settings:",jqXHR.status,textStatus); | ||||
|                     console.log("Unexpected error:",jqXHR.status,textStatus); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     var load = function(done) { | ||||
|         refreshSettings(function(err, data) { | ||||
|             if (!err) { | ||||
|                 if (!RED.settings.user || RED.settings.user.anonymous) { | ||||
|                     RED.settings.remove("auth-tokens"); | ||||
|                 } | ||||
|                 console.log("Node-RED: " + data.version); | ||||
|                 console.groupCollapsed("Versions"); | ||||
|                 console.log("jQuery",$().jquery) | ||||
|                 console.log("jQuery UI",$.ui.version); | ||||
|                 if(window.ace) { console.log("ACE",ace.version); } | ||||
|                 if(window.monaco) { console.log("MONACO",monaco.version || "unknown"); } | ||||
|                 console.log("D3",d3.version); | ||||
|                 console.groupEnd(); | ||||
|                 loadUserSettings(done); | ||||
|             } | ||||
|         }) | ||||
|     }; | ||||
| 
 | ||||
|     function loadUserSettings(done) { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept": "application/json" | ||||
|             }, | ||||
|             dataType: "json", | ||||
|             cache: false, | ||||
|             url: 'settings/user', | ||||
|             success: function (data) { | ||||
|                 setUserSettings(data); | ||||
|                 done(); | ||||
|             }, | ||||
|             error: function(jqXHR,textStatus,errorThrown) { | ||||
|                 console.log("Unexpected error loading user settings:",jqXHR.status,textStatus); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function saveUserSettings() { | ||||
|         if (RED.user.hasPermission("settings.write")) { | ||||
|             if (pendingSave) { | ||||
|                 clearTimeout(pendingSave); | ||||
|             } | ||||
|             pendingSave = setTimeout(function() { | ||||
|                 pendingSave = null; | ||||
|                 $.ajax({ | ||||
|                     method: 'POST', | ||||
|                     contentType: 'application/json', | ||||
|                     url: 'settings/user', | ||||
|                     data: JSON.stringify(userSettings), | ||||
|                     success: function (data) { | ||||
|                     }, | ||||
|                     error: function(jqXHR,textStatus,errorThrown) { | ||||
|                         console.log("Unexpected error saving user settings:",jqXHR.status,textStatus); | ||||
|                     } | ||||
|                 }); | ||||
|             },300); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function theme(property,defaultValue) { | ||||
|         if (!RED.settings.editorTheme) { | ||||
|             return defaultValue; | ||||
| @@ -226,28 +139,14 @@ RED.settings = (function () { | ||||
|             return defaultValue; | ||||
|         } | ||||
|     } | ||||
|     function getLocal(key) { | ||||
|         return localStorage.getItem(key) | ||||
|     } | ||||
|     function setLocal(key, value) { | ||||
|         localStorage.setItem(key, value); | ||||
|     } | ||||
|     function removeLocal(key) { | ||||
|         localStorage.removeItem(key) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     return { | ||||
|         init: init, | ||||
|         load: load, | ||||
|         loadUserSettings: loadUserSettings, | ||||
|         refreshSettings: refreshSettings, | ||||
|         set: set, | ||||
|         get: get, | ||||
|         remove: remove, | ||||
|         theme: theme, | ||||
|         setLocal: setLocal, | ||||
|         getLocal: getLocal, | ||||
|         removeLocal: removeLocal | ||||
|         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"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -18,7 +18,6 @@ | ||||
| /** | ||||
|  * options: | ||||
|  *   - 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' | ||||
|  *   - resize : function - called when list as a whole is resized | ||||
|  *   - resizeItem : function(item) - called to resize individual item | ||||
| @@ -28,22 +27,13 @@ | ||||
|  *   - removable : boolean - whether to display delete button on items | ||||
|  *   - addItem : function(row,index,itemData) - when an item is added | ||||
|  *   - removeItem : function(itemData) - called when an item is removed | ||||
|  *   - filter : function(itemData) - called for each item to determine if it should be shown | ||||
|  *   - sort : function(itemDataA,itemDataB) - called to sort items | ||||
|  *   - scrollOnAdd : boolean - whether to scroll to newly added items | ||||
|  * methods: | ||||
|  *   - addItem(itemData) | ||||
|  *   - insertItemAt : function(data,index) - add an item at the specified index | ||||
|  *   - removeItem(itemData, detach) - remove the item. Optionally detach to preserve any event handlers on the item's label | ||||
|  *   - getItemAt(index) | ||||
|  *   - indexOf(itemData) | ||||
|  *   - removeItem(itemData) | ||||
|  *   - width(width) | ||||
|  *   - height(height) | ||||
|  *   - items() | ||||
|  *   - empty() | ||||
|  *   - filter(filter) | ||||
|  *   - sort(sort) | ||||
|  *   - length() | ||||
|  */ | ||||
|     $.widget( "nodered.editableList", { | ||||
|         _create: function() { | ||||
| @@ -54,78 +44,34 @@ | ||||
|             this.uiContainer = this.element | ||||
|                 .wrap( "<div>" ) | ||||
|                 .parent(); | ||||
| 
 | ||||
|             if (this.options.header) { | ||||
|                 this.options.header.addClass("red-ui-editableList-header"); | ||||
|                 this.borderContainer = this.uiContainer.wrap("<div>").parent(); | ||||
|                 this.borderContainer.prepend(this.options.header); | ||||
|                 this.topContainer = this.borderContainer.wrap("<div>").parent(); | ||||
|             } else { | ||||
|             this.topContainer = this.uiContainer.wrap("<div>").parent(); | ||||
|             } | ||||
|             this.topContainer.addClass('red-ui-editableList'); | ||||
|             if (this.options.class) { | ||||
|                 this.topContainer.addClass(this.options.class); | ||||
|             } | ||||
| 
 | ||||
|             var buttons = this.options.buttons || []; | ||||
|             this.topContainer.addClass('red-ui-editableList'); | ||||
| 
 | ||||
|             if (this.options.addButton !== false) { | ||||
|                 var addLabel, addTitle; | ||||
|                 var addLabel; | ||||
|                 if (typeof this.options.addButton === 'string') { | ||||
|                     addLabel = this.options.addButton | ||||
|                 } else { | ||||
|                     if (RED && RED._) { | ||||
|                         addLabel = RED._("editableList.add"); | ||||
|                         addTitle = RED._("editableList.addTitle"); | ||||
|                     } else { | ||||
|                         addLabel = 'add'; | ||||
|                         addTitle = 'add new item'; | ||||
|                     } | ||||
|                 } | ||||
|                 buttons.unshift({ | ||||
|                     label: addLabel, | ||||
|                     icon: "fa fa-plus", | ||||
|                     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) { | ||||
|                 $('<a href="#" class="editor-button editor-button-small" style="margin-top: 4px;"><i class="fa fa-plus"></i> '+addLabel+'</a>') | ||||
|                     .appendTo(this.topContainer) | ||||
|                     .click(function(evt) { | ||||
|                         evt.preventDefault(); | ||||
|                         if (button.click !== undefined) { | ||||
|                             button.click(evt); | ||||
|                         } | ||||
|                         that.addItem({}); | ||||
|                     }); | ||||
| 
 | ||||
|                 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") { | ||||
|                 ["top","left","bottom","right"].forEach(function(s) { | ||||
|                     var v = that.element.css(s); | ||||
|                     if (v!=="auto" && v!=="") { | ||||
|                     if (s!=="auto" && s!=="") { | ||||
|                         that.topContainer.css(s,v); | ||||
|                         that.uiContainer.css(s,"0"); | ||||
|                         if (s === "top" && that.options.header) { | ||||
|                             that.uiContainer.css(s,"20px") | ||||
|                         } | ||||
|                         that.element.css(s,'auto'); | ||||
|                     } | ||||
|                 }) | ||||
| @@ -134,11 +80,6 @@ | ||||
|                 this.uiContainer.css("position","absolute"); | ||||
| 
 | ||||
|             } | ||||
|             if (this.options.header) { | ||||
|                 this.borderContainer.addClass("red-ui-editableList-border"); | ||||
|             } else { | ||||
|                 this.uiContainer.addClass("red-ui-editableList-border"); | ||||
|             } | ||||
|             this.uiContainer.addClass("red-ui-editableList-container"); | ||||
| 
 | ||||
|             this.uiHeight = this.element.height(); | ||||
| @@ -154,13 +95,8 @@ | ||||
|                 this.uiContainer.css("minHeight",minHeight); | ||||
|                 this.element.css("minHeight",0); | ||||
|             } | ||||
|             var maxHeight = this.element.css("maxHeight"); | ||||
|             if (maxHeight !== '0px') { | ||||
|                 this.uiContainer.css("maxHeight",maxHeight); | ||||
|                 this.element.css("maxHeight",null); | ||||
|             } | ||||
|             if (this.options.height !== 'auto') { | ||||
|                 this.uiContainer.css("overflow-y","auto"); | ||||
|                 this.uiContainer.css("overflow-y","scroll"); | ||||
|                 if (!isNaN(this.options.height)) { | ||||
|                     this.uiHeight = this.options.height; | ||||
|                 } | ||||
| @@ -218,22 +154,17 @@ | ||||
|             if (this.options.resizeItem) { | ||||
|                 var that = this; | ||||
|                 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() { | ||||
|             if (this.topContainer) { | ||||
|                 var tc = this.topContainer; | ||||
|                 delete this.topContainer; | ||||
|                 tc.remove(); | ||||
|             } | ||||
|         }, | ||||
|         _refreshFilter: function() { | ||||
|             var that = this; | ||||
|             var count = 0; | ||||
|             if (!this.activeFilter) { | ||||
|                 return this.element.children().show(); | ||||
|                 this.element.children().show(); | ||||
|             } | ||||
|             var items = this.items(); | ||||
|             items.each(function (i,el) { | ||||
| @@ -258,7 +189,7 @@ | ||||
|                 var items = this.element.children(); | ||||
|                 var that = this; | ||||
|                 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) { | ||||
|                     that.element.append(li); | ||||
| @@ -273,49 +204,10 @@ | ||||
|             this.uiHeight = desiredHeight; | ||||
|             this._resize(); | ||||
|         }, | ||||
|         getItemAt: function(index) { | ||||
|             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) { | ||||
|         addItem: function(data) { | ||||
|             var that = this; | ||||
|             data = data || {}; | ||||
|             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; | ||||
|             if (this.activeSort) { | ||||
|                 var items = this.items(); | ||||
| @@ -330,17 +222,32 @@ | ||||
|                 }); | ||||
|             } | ||||
|             if (!added) { | ||||
|                 if (index <= 0) { | ||||
|                     li.prependTo(this.element); | ||||
|                 } else if (index > that.element.children().length-1) { | ||||
|                 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) { | ||||
|                 var index = that.element.children().length-1; | ||||
|                 // setTimeout(function() {
 | ||||
|                 setTimeout(function() { | ||||
|                     that.options.addItem(row,index,data); | ||||
|                     if (that.activeFilter) { | ||||
|                         try { | ||||
| @@ -356,36 +263,23 @@ | ||||
|                             that.uiContainer.scrollTop(that.element.height()); | ||||
|                         },0); | ||||
|                     } | ||||
|                 // },0);
 | ||||
|                 },0); | ||||
|             } | ||||
|         }, | ||||
|         addItem: 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) { | ||||
|         removeItem: function(data) { | ||||
|             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(); | ||||
|             } | ||||
|             if (this.options.removeItem) { | ||||
|                 this.options.removeItem(data); | ||||
|             } | ||||
|         }, | ||||
|         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() { | ||||
|             this.element.empty(); | ||||
|             this.uiContainer.scrollTop(0); | ||||
|         }, | ||||
|         filter: function(filter) { | ||||
|             if (filter !== undefined) { | ||||
| @@ -401,22 +295,6 @@ | ||||
|         }, | ||||
|         length: function() { | ||||
|             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; | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| })(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); | ||||
							
								
								
									
										350
									
								
								editor/js/ui/common/tabs.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,350 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
|  | ||||
|  | ||||
| RED.tabs = (function() { | ||||
|     function createTabs(options) { | ||||
|         var tabs = {}; | ||||
|         var currentTabWidth; | ||||
|         var currentActiveTabWidth = 0; | ||||
|  | ||||
|         var ul = $("#"+options.id); | ||||
|         var wrapper = ul.wrap( "<div>" ).parent(); | ||||
|         var scrollContainer = ul.wrap( "<div>" ).parent(); | ||||
|         wrapper.addClass("red-ui-tabs"); | ||||
|         if (options.addButton && typeof options.addButton === 'function') { | ||||
|             wrapper.addClass("red-ui-tabs-add"); | ||||
|             var addButton = $('<div class="red-ui-tab-button"><a href="#"><i class="fa fa-plus"></i></a></div>').appendTo(wrapper); | ||||
|             addButton.find('a').click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 options.addButton(); | ||||
|             }) | ||||
|  | ||||
|         } | ||||
|         var scrollLeft; | ||||
|         var scrollRight; | ||||
|  | ||||
|         if (options.scrollable) { | ||||
|             wrapper.addClass("red-ui-tabs-scrollable"); | ||||
|             scrollContainer.addClass("red-ui-tabs-scroll-container"); | ||||
|             scrollContainer.scroll(updateScroll); | ||||
|             scrollLeft = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-left"><a href="#" style="display:none;"><i class="fa fa-caret-left"></i></a></div>').appendTo(wrapper).find("a"); | ||||
|             scrollLeft.on('mousedown',function(evt) { scrollEventHandler(evt,'-=150') }).on('click',function(evt){ evt.preventDefault();}); | ||||
|             scrollRight = $('<div class="red-ui-tab-button red-ui-tab-scroll red-ui-tab-scroll-right"><a href="#" style="display:none;"><i class="fa fa-caret-right"></i></a></div>').appendTo(wrapper).find("a"); | ||||
|             scrollRight.on('mousedown',function(evt) { scrollEventHandler(evt,'+=150') }).on('click',function(evt){ evt.preventDefault();}); | ||||
|         } | ||||
|         function scrollEventHandler(evt,dir) { | ||||
|             evt.preventDefault(); | ||||
|             if ($(this).hasClass('disabled')) { | ||||
|                 return; | ||||
|             } | ||||
|             var currentScrollLeft = scrollContainer.scrollLeft(); | ||||
|             scrollContainer.animate( { scrollLeft: dir }, 100); | ||||
|             var interval = setInterval(function() { | ||||
|                 var newScrollLeft = scrollContainer.scrollLeft() | ||||
|                 if (newScrollLeft === currentScrollLeft) { | ||||
|                     clearInterval(interval); | ||||
|                     return; | ||||
|                 } | ||||
|                 currentScrollLeft = newScrollLeft; | ||||
|                 scrollContainer.animate( { scrollLeft: dir }, 100); | ||||
|             },100); | ||||
|             $(this).one('mouseup',function() { | ||||
|                 clearInterval(interval); | ||||
|             }) | ||||
|         } | ||||
|  | ||||
|  | ||||
|         ul.children().first().addClass("active"); | ||||
|         ul.children().addClass("red-ui-tab"); | ||||
|  | ||||
|         function onTabClick() { | ||||
|             activateTab($(this)); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         function updateScroll() { | ||||
|             if (ul.children().length !== 0) { | ||||
|                 var sl = scrollContainer.scrollLeft(); | ||||
|                 var scWidth = scrollContainer.width(); | ||||
|                 var ulWidth = ul.width(); | ||||
|                 if (sl === 0) { | ||||
|                     scrollLeft.hide(); | ||||
|                 } else { | ||||
|                     scrollLeft.show(); | ||||
|                 } | ||||
|                 if (sl === ulWidth-scWidth) { | ||||
|                     scrollRight.hide(); | ||||
|                 } else { | ||||
|                     scrollRight.show(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         function onTabDblClick() { | ||||
|             if (options.ondblclick) { | ||||
|                 options.ondblclick(tabs[$(this).attr('href').slice(1)]); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         function activateTab(link) { | ||||
|             if (typeof link === "string") { | ||||
|                 link = ul.find("a[href='#"+link+"']"); | ||||
|             } | ||||
|             if (!link.parent().hasClass("active")) { | ||||
|                 ul.children().removeClass("active"); | ||||
|                 ul.children().css({"transition": "width 100ms"}); | ||||
|                 link.parent().addClass("active"); | ||||
|                 if (options.scrollable) { | ||||
|                     var pos = link.parent().position().left; | ||||
|                     if (pos-21 < 0) { | ||||
|                         scrollContainer.animate( { scrollLeft: '+='+(pos-50) }, 300); | ||||
|                     } else if (pos + 120 > scrollContainer.width()) { | ||||
|                         scrollContainer.animate( { scrollLeft: '+='+(pos + 140-scrollContainer.width()) }, 300); | ||||
|                     } | ||||
|                 } | ||||
|                 if (options.onchange) { | ||||
|                     options.onchange(tabs[link.attr('href').slice(1)]); | ||||
|                 } | ||||
|                 updateTabWidths(); | ||||
|                 setTimeout(function() { | ||||
|                     ul.children().css({"transition": ""}); | ||||
|                 },100); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function updateTabWidths() { | ||||
|             var tabs = ul.find("li.red-ui-tab"); | ||||
|             var width = wrapper.width(); | ||||
|             var tabCount = tabs.size(); | ||||
|             var tabWidth = (width-12-(tabCount*6))/tabCount; | ||||
|             currentTabWidth = (100*tabWidth/width)+"%"; | ||||
|             currentActiveTabWidth = currentTabWidth+"%"; | ||||
|             if (options.scrollable) { | ||||
|                 tabWidth = Math.max(tabWidth,140); | ||||
|                 currentTabWidth = tabWidth+"px"; | ||||
|                 currentActiveTabWidth = 0; | ||||
|                 var listWidth = Math.max(wrapper.width(),12+(tabWidth+6)*tabCount); | ||||
|                 ul.width(listWidth); | ||||
|                 updateScroll(); | ||||
|             } else if (options.hasOwnProperty("minimumActiveTabWidth")) { | ||||
|                 if (tabWidth < options.minimumActiveTabWidth) { | ||||
|                     tabCount -= 1; | ||||
|                     tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount; | ||||
|                     currentTabWidth = (100*tabWidth/width)+"%"; | ||||
|                     currentActiveTabWidth = options.minimumActiveTabWidth+"px"; | ||||
|                 } else { | ||||
|                     currentActiveTabWidth = 0; | ||||
|                 } | ||||
|             } | ||||
|             tabs.css({width:currentTabWidth}); | ||||
|             if (tabWidth < 50) { | ||||
|                 ul.find(".red-ui-tab-close").hide(); | ||||
|                 ul.find(".red-ui-tab-icon").hide(); | ||||
|                 ul.find(".red-ui-tab-label").css({paddingLeft:Math.min(12,Math.max(0,tabWidth-38))+"px"}) | ||||
|             } else { | ||||
|                 ul.find(".red-ui-tab-close").show(); | ||||
|                 ul.find(".red-ui-tab-icon").show(); | ||||
|                 ul.find(".red-ui-tab-label").css({paddingLeft:""}) | ||||
|             } | ||||
|             if (currentActiveTabWidth !== 0) { | ||||
|                 ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth}); | ||||
|                 ul.find("li.red-ui-tab.active .red-ui-tab-close").show(); | ||||
|                 ul.find("li.red-ui-tab.active .red-ui-tab-icon").show(); | ||||
|                 ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""}) | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick); | ||||
|         setTimeout(function() { | ||||
|             updateTabWidths(); | ||||
|         },0); | ||||
|  | ||||
|  | ||||
|         function removeTab(id) { | ||||
|             var li = ul.find("a[href='#"+id+"']").parent(); | ||||
|             if (li.hasClass("active")) { | ||||
|                 var tab = li.prev(); | ||||
|                 if (tab.size() === 0) { | ||||
|                     tab = li.next(); | ||||
|                 } | ||||
|                 activateTab(tab.find("a")); | ||||
|             } | ||||
|             li.remove(); | ||||
|             if (options.onremove) { | ||||
|                 options.onremove(tabs[id]); | ||||
|             } | ||||
|             delete tabs[id]; | ||||
|             updateTabWidths(); | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|             addTab: function(tab) { | ||||
|                 tabs[tab.id] = tab; | ||||
|                 var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul); | ||||
|                 li.data("tabId",tab.id); | ||||
|                 var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li); | ||||
|                 if (tab.icon) { | ||||
|                     $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link); | ||||
|                 } | ||||
|                 var span = $('<span/>',{class:"bidiAware"}); | ||||
|                 span.attr('dir', RED.bidi.resolveBaseTextDir(tab.label)).text(RED.bidi.applyBidiSupport(tab.label,RED.bidi.flags.NS)).appendTo(link); | ||||
|                 link.on("click",onTabClick); | ||||
|                 link.on("dblclick",onTabDblClick); | ||||
|                 if (tab.closeable) { | ||||
|                     var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li); | ||||
|                     closeLink.append('<i class="fa fa-times" />'); | ||||
|  | ||||
|                     closeLink.on("click",function(event) { | ||||
|                         removeTab(tab.id); | ||||
|                     }); | ||||
|                 } | ||||
|                 updateTabWidths(); | ||||
|                 if (options.onadd) { | ||||
|                     options.onadd(tab); | ||||
|                 } | ||||
|                 link.attr("title",tab.label); | ||||
|                 if (ul.find("li.red-ui-tab").size() == 1) { | ||||
|                     activateTab(link); | ||||
|                 } | ||||
|                 if (options.onreorder) { | ||||
|                     var originalTabOrder; | ||||
|                     var tabDragIndex; | ||||
|                     var tabElements = []; | ||||
|                     var startDragIndex; | ||||
|  | ||||
|                     li.draggable({ | ||||
|                         axis:"x", | ||||
|                         distance: 20, | ||||
|                         start: function(event,ui) { | ||||
|                             originalTabOrder = []; | ||||
|                             tabElements = []; | ||||
|                             ul.children().each(function(i) { | ||||
|                                 tabElements[i] = { | ||||
|                                     el:$(this), | ||||
|                                     text: $(this).text(), | ||||
|                                     left: $(this).position().left, | ||||
|                                     width: $(this).width() | ||||
|                                 }; | ||||
|                                 if ($(this).is(li)) { | ||||
|                                     tabDragIndex = i; | ||||
|                                     startDragIndex = i; | ||||
|                                 } | ||||
|                                 originalTabOrder.push($(this).data("tabId")); | ||||
|                             }); | ||||
|                             ul.children().each(function(i) { | ||||
|                                 if (i!==tabDragIndex) { | ||||
|                                     $(this).css({ | ||||
|                                         position: 'absolute', | ||||
|                                         left: tabElements[i].left+"px", | ||||
|                                         width: tabElements[i].width+2, | ||||
|                                         transition: "left 0.3s" | ||||
|                                     }); | ||||
|                                 } | ||||
|  | ||||
|                             }) | ||||
|                             if (!li.hasClass('active')) { | ||||
|                                 li.css({'zIndex':1}); | ||||
|                             } | ||||
|                         }, | ||||
|                         drag: function(event,ui) { | ||||
|                             ui.position.left += tabElements[tabDragIndex].left+scrollContainer.scrollLeft(); | ||||
|                             var tabCenter = ui.position.left + tabElements[tabDragIndex].width/2 - scrollContainer.scrollLeft(); | ||||
|                             for (var i=0;i<tabElements.length;i++) { | ||||
|                                 if (i === tabDragIndex) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 if (tabCenter > tabElements[i].left && tabCenter < tabElements[i].left+tabElements[i].width) { | ||||
|                                     if (i < tabDragIndex) { | ||||
|                                         tabElements[i].left += tabElements[tabDragIndex].width+8; | ||||
|                                         tabElements[tabDragIndex].el.detach().insertBefore(tabElements[i].el); | ||||
|                                     } else { | ||||
|                                         tabElements[i].left -= tabElements[tabDragIndex].width+8; | ||||
|                                         tabElements[tabDragIndex].el.detach().insertAfter(tabElements[i].el); | ||||
|                                     } | ||||
|                                     tabElements[i].el.css({left:tabElements[i].left+"px"}); | ||||
|  | ||||
|                                     tabElements.splice(i, 0, tabElements.splice(tabDragIndex, 1)[0]); | ||||
|  | ||||
|                                     tabDragIndex = i; | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                         }, | ||||
|                         stop: function(event,ui) { | ||||
|                             ul.children().css({position:"relative",left:"",transition:""}); | ||||
|                             if (!li.hasClass('active')) { | ||||
|                                 li.css({zIndex:""}); | ||||
|                             } | ||||
|                             updateTabWidths(); | ||||
|                             if (startDragIndex !== tabDragIndex) { | ||||
|                                 options.onreorder(originalTabOrder, $.makeArray(ul.children().map(function() { return $(this).data('tabId');}))); | ||||
|                             } | ||||
|                             activateTab(tabElements[tabDragIndex].el.data('tabId')); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             }, | ||||
|             removeTab: removeTab, | ||||
|             activateTab: activateTab, | ||||
|             resize: updateTabWidths, | ||||
|             count: function() { | ||||
|                 return ul.find("li.red-ui-tab").size(); | ||||
|             }, | ||||
|             contains: function(id) { | ||||
|                 return ul.find("a[href='#"+id+"']").length > 0; | ||||
|             }, | ||||
|             renameTab: function(id,label) { | ||||
|                 tabs[id].label = label; | ||||
|                 var tab = ul.find("a[href='#"+id+"']"); | ||||
|                 tab.attr("title",label); | ||||
|                 tab.find("span").attr('dir', RED.bidi.resolveBaseTextDir(label)).text(RED.bidi.applyBidiSupport(label,RED.bidi.flags.NS)); | ||||
|                 updateTabWidths(); | ||||
|             }, | ||||
|             order: function(order) { | ||||
|                 var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); | ||||
|                 if (existingTabOrder.length !== order.length) { | ||||
|                     return | ||||
|                 } | ||||
|                 var i; | ||||
|                 var match = true; | ||||
|                 for (i=0;i<order.length;i++) { | ||||
|                     if (order[i] !== existingTabOrder[i]) { | ||||
|                         match = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (match) { | ||||
|                     return; | ||||
|                 } | ||||
|                 var existingTabMap = {}; | ||||
|                 var existingTabs = ul.children().detach().each(function() { | ||||
|                     existingTabMap[$(this).data("tabId")] = $(this); | ||||
|                 }); | ||||
|                 for (i=0;i<order.length;i++) { | ||||
|                     existingTabMap[order[i]].appendTo(ul); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         create: createTabs | ||||
|     } | ||||
| })(); | ||||