Compare commits
	
		
			399 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d0f7e5ca4d | ||
|  | 4eb5058e68 | ||
|  | 1054193298 | ||
|  | 38c6cf0450 | ||
|  | 5b04b86867 | ||
|  | a074bcfd56 | ||
|  | f93179d946 | ||
|  | 2c347bc092 | ||
|  | 0f7119f468 | ||
|  | 2685a24705 | ||
|  | 371f72f4f1 | ||
|  | c70c00043b | ||
|  | 50d0a88276 | ||
|  | 5bbf576dae | ||
|  | 5d334e9619 | ||
|  | 98f9353338 | ||
|  | d3de7037e5 | ||
|  | 64431c6711 | ||
|  | d4ce193dc8 | ||
|  | 606305aec4 | ||
|  | a95f44d68b | ||
|  | ef2dc4b9e1 | ||
|  | 9baca1772b | ||
|  | 04cd19349d | ||
|  | 1280e5bc8b | ||
|  | dda90f956d | ||
|  | bc4b599513 | ||
|  | 090d52d678 | ||
|  | a47ad4842a | ||
|  | 3d5ed840dc | ||
|  | 11d75ff581 | ||
|  | 306fb7a3d1 | ||
|  | 0839b6f58e | ||
|  | fceca703b3 | ||
|  | 4dc60d2477 | ||
|  | d840d0b67d | ||
|  | 43dad4c465 | ||
|  | 60812b2d8a | ||
|  | 35e2caff13 | ||
|  | 1d9d5c6bc7 | ||
|  | 4d99536ea7 | ||
|  | 34537180c3 | ||
|  | cb01920ee6 | ||
|  | 437b01a0ff | ||
|  | 075a2abf71 | ||
|  | 985875cc75 | ||
|  | 1c45bc615f | ||
|  | fa7f2606fb | ||
|  | 12b95f1c72 | ||
|  | a0aee2021d | ||
|  | c90fd1e6d8 | ||
|  | 3b769fd2de | ||
|  | 71ecb89abc | ||
|  | 7b6bc1d3bc | ||
|  | 9c3be40fbe | ||
|  | ab87fa9ce4 | ||
|  | d1940a023a | ||
|  | 5a176a037c | ||
|  | ec25191c98 | ||
|  | 20b321f928 | ||
|  | 425b016d63 | ||
|  | b2c7189ce4 | ||
|  | f66886dbdb | ||
|  | 712f8b4680 | ||
|  | f626ee060a | ||
|  | 86aa7c97be | ||
|  | 30e3525987 | ||
|  | ad44f838da | ||
|  | 2569a35b6c | ||
|  | 1ee5e50d50 | ||
|  | 1dbec5eca8 | ||
|  | 2bc8db308c | ||
|  | f196740426 | ||
|  | 20121b79c5 | ||
|  | 741a4cfe53 | ||
|  | 0343de9f34 | ||
|  | 4772bca14a | ||
|  | 6ae1a5ba0d | ||
|  | 217c9718e4 | ||
|  | 61d7893467 | ||
|  | 8f26c01f4b | ||
|  | 61045ddd7f | ||
|  | 1bf72a0bc3 | ||
|  | 6d84b1bb8d | ||
|  | 8abd0b1fdf | ||
|  | 81e125b7ba | ||
|  | d5e1468718 | ||
|  | c232bf5ed6 | ||
|  | 21b25ffaee | ||
|  | ca0a93df08 | ||
|  | 699a22c757 | ||
|  | 8b2b1669b5 | ||
|  | c1e8370916 | ||
|  | ddedea8b90 | ||
|  | 8f414ce458 | ||
|  | 453f23da20 | ||
|  | 9e91e42a1b | ||
|  | b666734c79 | ||
|  | a2297f303d | ||
|  | ecde942255 | ||
|  | d668d43a0a | ||
|  | ca91a5dd95 | ||
|  | 5f9780d71c | ||
|  | ee37464741 | ||
|  | 8d73f927db | ||
|  | 4a0222bd1c | ||
|  | c0b8f5e3e1 | ||
|  | a9a0b263dc | ||
|  | f2b73187d8 | ||
|  | ef10ade0cc | ||
|  | 719bb4263e | ||
|  | b3602b268e | ||
|  | 66ec9bae27 | ||
|  | d96b6e77c0 | ||
|  | da64c018ac | ||
|  | f9fb97adf2 | ||
|  | 8316bc6480 | ||
|  | 08021e039a | ||
|  | cc6e0937a0 | ||
|  | c1d694a97c | ||
|  | fcf4f40c36 | ||
|  | 380b03399c | ||
|  | c33d02c53f | ||
|  | fa5e37993e | ||
|  | 437b2d506b | ||
|  | 4ed09f6431 | ||
|  | 0b98a6acf8 | ||
|  | 1d73c86cb2 | ||
|  | 40fe0f3239 | ||
|  | d1ea689999 | ||
|  | a6644ad5ff | ||
|  | 658746d2a3 | ||
|  | cbdd4de630 | ||
|  | 00c612485b | ||
|  | 3a6192bf73 | ||
|  | c64b5c2850 | ||
|  | fdbf079896 | ||
|  | d1a5395727 | ||
|  | 83a3642c0e | ||
|  | 9932d34304 | ||
|  | 7aa37a1976 | ||
|  | fa42fbdab8 | ||
|  | caa83ac830 | ||
|  | 3963fa9738 | ||
|  | 005a98d020 | ||
|  | 9560dc9408 | ||
|  | 4ac9a5edf0 | ||
|  | 37e62597ae | ||
|  | 90bfe378d0 | ||
|  | ce22b494ec | ||
|  | f9e0420647 | ||
|  | 2fe568d9ba | ||
|  | 2d4979df4d | ||
|  | b555b014b8 | ||
|  | 999b888c54 | ||
|  | 5193d7bddb | ||
|  | 6b03379e4e | ||
|  | 08d687ad60 | ||
|  | eb57089f06 | ||
|  | a76e4fede1 | ||
|  | 705d043540 | ||
|  | 5462e251f8 | ||
|  | 50788af6ca | ||
|  | 1a07c5a329 | ||
|  | 9fb81b2814 | ||
|  | 10ad7fbf6e | ||
|  | 811c4f2630 | ||
|  | f7e3b0a64f | ||
|  | 7d83d76fb3 | ||
|  | d3c41b38f7 | ||
|  | 57d6b16d5c | ||
|  | 27aa5ae7db | ||
|  | 62e8f564b9 | ||
|  | a1d7bb4208 | ||
|  | 5d8dae05c4 | ||
|  | 6bde07b5a0 | ||
|  | 846ab08661 | ||
|  | 999cf66b27 | ||
|  | 60539d890b | ||
|  | e5a0f25d94 | ||
|  | fde9d40098 | ||
|  | f70e9ea076 | ||
|  | d0af4aac4d | ||
|  | 7de3704210 | ||
|  | 1c33b837b8 | ||
|  | 95d20d7fba | ||
|  | 90b8806e7c | ||
|  | 39df80bf99 | ||
|  | bac4beae03 | ||
|  | a7b68c18b5 | ||
|  | ef2360baee | ||
|  | 8716e7e601 | ||
|  | 00c2dae969 | ||
|  | 8782bc5896 | ||
|  | 6359b90352 | ||
|  | 6cfa4976fe | ||
|  | 35cd7cf2b8 | ||
|  | b2d7f079b7 | ||
|  | 726069bc4b | ||
|  | fbccf01933 | ||
|  | c9f3c6f4a3 | ||
|  | c47da013ff | ||
|  | bc76499957 | ||
|  | 24afcff0ea | ||
|  | 6777f24845 | ||
|  | 61e0923fc4 | ||
|  | 757caeb9a4 | ||
|  | fe9dc6b272 | ||
|  | 24efdbe4da | ||
|  | 9ca102cf81 | ||
|  | 48df31d7b7 | ||
|  | 539afb1e1d | ||
|  | bdcba44ca5 | ||
|  | 99a51b07ac | ||
|  | 5fbaca75b4 | ||
|  | a6974371b0 | ||
|  | 409fa49234 | ||
|  | 9bbd6a70b8 | ||
|  | f0b4cb608a | ||
|  | 284d7e26d1 | ||
|  | 965c0937ac | ||
|  | d9cf6a4431 | ||
|  | e6ed8ee509 | ||
|  | 2563649b3e | ||
|  | 203bc41b06 | ||
|  | cb1d18c7c8 | ||
|  | 5ea68dafc4 | ||
|  | 68bb8252af | ||
|  | 406f742d29 | ||
|  | 5522e57f65 | ||
|  | 94e27dbfc5 | ||
|  | f5fc8f763f | ||
|  | 9058bf615c | ||
|  | 9d17137dec | ||
|  | 4a318553f7 | ||
|  | 0017074d38 | ||
|  | a39a26fcc2 | ||
|  | 2fe859b111 | ||
|  | c105b2df37 | ||
|  | 4fb86ab55a | ||
|  | 1ed98a5963 | ||
|  | a4a29ceb3c | ||
|  | 6249083431 | ||
|  | aa18c65fa8 | ||
|  | a7900940da | ||
|  | 008bc98070 | ||
|  | 0705589cc2 | ||
|  | b2caba593f | ||
|  | 6d4c64fcd5 | ||
|  | 7d41781fb4 | ||
|  | 0760facb77 | ||
|  | a0ce095807 | ||
|  | df0110913a | ||
|  | 06731374a4 | ||
|  | 6c8b7c0082 | ||
|  | 93136961b9 | ||
|  | 529a691e1d | ||
|  | 9f0b3eba47 | ||
|  | 6d897793cb | ||
|  | 0e12fc6b02 | ||
|  | c00558ea1b | ||
|  | bacf27a3ca | ||
|  | 6560ea0630 | ||
|  | 8338231ce5 | ||
|  | 5813a91244 | ||
|  | dfd8ab3545 | ||
|  | a5b9b949a8 | ||
|  | 72570e4510 | ||
|  | 3002aead6b | ||
|  | 841edbe6fb | ||
|  | 2b9aca0c56 | ||
|  | 3a17c3ee6d | ||
|  | df09252ee0 | ||
|  | f51778d417 | ||
|  | 119fc63794 | ||
|  | ee3425d3be | ||
|  | 31c979f30f | ||
|  | 89f2c26cd6 | ||
|  | 0dbf43d0aa | ||
|  | 8a6d11b191 | ||
|  | d8eb926e2c | ||
|  | 21d0adbdae | ||
|  | c5fd3a5753 | ||
|  | eae4e3d983 | ||
|  | bb1fe8daef | ||
|  | c5d8e09b41 | ||
|  | 369eae3d92 | ||
|  | e2fa457ca2 | ||
|  | 871f764e98 | ||
|  | f8853af902 | ||
|  | 4248d20f39 | ||
|  | 5cda08e7b0 | ||
|  | 7e9d96ee87 | ||
|  | 974ac31d33 | ||
|  | 0658b70631 | ||
|  | 53258eeede | ||
|  | 4f174308b9 | ||
|  | 98c9e40349 | ||
|  | 3a4756bd83 | ||
|  | 6ccb05cb2c | ||
|  | 3c2d32b867 | ||
|  | 956050434f | ||
|  | 38ab1550d2 | ||
|  | e852d1e57c | ||
|  | 7de0216976 | ||
|  | 911288e695 | ||
|  | 72e1f20383 | ||
|  | d28a6eaf9d | ||
|  | 17f3366556 | ||
|  | 0bef04ae0a | ||
|  | f11b906fd9 | ||
|  | 518358d9dc | ||
|  | 5ffde21d83 | ||
|  | 052302b3e7 | ||
|  | fe1ce21114 | ||
|  | ce5c9da107 | ||
|  | cf25b2866e | ||
|  | 07fd5a5f5f | ||
|  | 913fdac671 | ||
|  | 7dc838dea6 | ||
|  | 7112fd2a22 | ||
|  | 56e8c143dd | ||
|  | 7b4cbbe816 | ||
|  | 8f8ee4662d | ||
|  | 86013c7db4 | ||
|  | 01aa3324f8 | ||
|  | b9cfeee965 | ||
|  | b3684a70b5 | ||
|  | 51fce9343b | ||
|  | 0c5c3448d0 | ||
|  | deaef3ab86 | ||
|  | a443491c0c | ||
|  | 276d893198 | ||
|  | 653d0e71e4 | ||
|  | faa7d948a7 | ||
|  | 771342989e | ||
|  | e9ce519e4b | ||
|  | c016b102eb | ||
|  | 0583c60837 | ||
|  | 1c1a85dcef | ||
|  | c71e76335b | ||
|  | c1a32c4eb9 | ||
|  | 9ad1f769d3 | ||
|  | 87f8fd34b8 | ||
|  | e206d2919e | ||
|  | 9d809aa2ba | ||
|  | 8f744794e4 | ||
|  | 78ab4217be | ||
|  | d090df94c5 | ||
|  | 937f26da41 | ||
|  | 98e3ff014e | ||
|  | 6f84526364 | ||
|  | 105d38c885 | ||
|  | d7bdcd69fc | ||
|  | 87e537da90 | ||
|  | 8f16695f06 | ||
|  | 8403f6291f | ||
|  | 5af6ac3e80 | ||
|  | 0d557094b2 | ||
|  | a2aa78afd4 | ||
|  | b0de8abb63 | ||
|  | 6ff540ed08 | ||
|  | 2b8ed9850b | ||
|  | dcd579b5e3 | ||
|  | c9d2d301aa | ||
|  | 1aaef598a5 | ||
|  | 73d1f3d0e8 | ||
|  | e369ded6c5 | ||
|  | 269846c587 | ||
|  | 8dc98420db | ||
|  | 1014abe92f | ||
|  | 6927f10f8f | ||
|  | 49d3a7190a | ||
|  | a2e65b0018 | ||
|  | f48ee01a03 | ||
|  | 0e926c566b | ||
|  | d2c4c2c34c | ||
|  | c2253d1e25 | ||
|  | eae16b6e8c | ||
|  | 868ae5b5dd | ||
|  | 1406503e10 | ||
|  | 9ca9d88546 | ||
|  | 203d3f672c | ||
|  | 698b2688f6 | ||
|  | be1620dd07 | ||
|  | e1f0969957 | ||
|  | e1dd8cf2ab | ||
|  | 8ee90777ee | ||
|  | 2fe9c1e55f | ||
|  | 9dd7e2e43d | ||
|  | 5efbdf5d04 | ||
|  | 5be3472413 | ||
|  | a9a0953653 | ||
|  | d4ac4c44d0 | ||
|  | f459ff8ad0 | ||
|  | b96ea36b70 | ||
|  | e543cc0fed | ||
|  | 0b1b4df210 | ||
|  | e59ffb0b19 | 
							
								
								
									
										24
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,11 +1,19 @@ | ||||
| node_modules | ||||
| credentials.json | ||||
| flows*.json | ||||
| .DS_store | ||||
| .config.json | ||||
| .dist | ||||
| .jshintignore | ||||
| .npm | ||||
| .project | ||||
| .sessions.json | ||||
| .settings | ||||
| .tern-project | ||||
| *.backup | ||||
| *_cred* | ||||
| coverage | ||||
| credentials.json | ||||
| flows*.json | ||||
| nodes/node-red-nodes/ | ||||
| .npm | ||||
| /coverage | ||||
| .config.json | ||||
| .sessions.json | ||||
|  | ||||
| node_modules | ||||
| public | ||||
| locales/zz-ZZ | ||||
| nodes/core/locales/zz-ZZ | ||||
|   | ||||
							
								
								
									
										13
									
								
								.jshintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| { | ||||
|     "asi": true,        // allow missing semicolons | ||||
|     "curly": true,      // require braces | ||||
|     "eqnull": true,     // ignore ==null | ||||
|     "forin": true,      // require property filtering in "for in" loops | ||||
|     "immed": true,      // require immediate functions to be wrapped in ( ) | ||||
|     "nonbsp": true,     // warn on unexpected whitespace breaking chars | ||||
|     //"strict": true,   // commented out for now as it causes 100s of warnings, but want to get there eventually | ||||
|     //"unused": true,   // Check for unused functions and variables | ||||
|     "loopfunc": true,   // allow functions to be defined in loops | ||||
|     //"expr": true,       // allow ternery operator syntax... | ||||
|     "sub": true         // don't warn that foo['bar'] should be written as foo.bar | ||||
| } | ||||
| @@ -1,4 +1,5 @@ | ||||
| .git/* | ||||
| *.json | ||||
| lib/* | ||||
| /Gruntfile.js | ||||
| /.git/* | ||||
| /lib/* | ||||
| *.backup | ||||
| /public/* | ||||
|   | ||||
							
								
								
									
										7
									
								
								.npmignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| .settings | ||||
| .jshintignore | ||||
| .jshintrc | ||||
| .project | ||||
| .tern-project | ||||
| .travis.yml | ||||
| .git | ||||
							
								
								
									
										19
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						| @@ -1,9 +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: "5" | ||||
| before_install: | ||||
|   - npm install -g npm@~1.4.18 | ||||
|   - npm install -g npm@latest-2 | ||||
| node_js: | ||||
|   - "5" | ||||
|   - "4" | ||||
|   - "0.12" | ||||
|   - "0.10" | ||||
|   - "0.8" | ||||
| 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: | ||||
|   | ||||
| @@ -6,11 +6,10 @@ We welcome contributions, but request you follow these guidelines. | ||||
|  - [Feature requests](#feature-requests) | ||||
|  - [Pull-Requests](#pull-requests) | ||||
|    - [Contributor License Agreement](#contributor-license-agreement) | ||||
|   | ||||
|  | ||||
| ## Raising issues | ||||
|  | ||||
| Please raise any bug reports on the project's  | ||||
| [issue tracker](https://github.com/node-red/node-red/issues?state=open). Be sure to | ||||
| Please raise any bug reports on the relevant project's issue tracker. Be sure to | ||||
| search the list to see if your issue has already been raised. | ||||
|  | ||||
| A good bug report is one that make it easy for us to understand what you were | ||||
| @@ -25,7 +24,6 @@ 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? | ||||
|  | ||||
|  | ||||
| ## Feature requests | ||||
|  | ||||
| For feature requests, please raise them on the [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||
| @@ -33,15 +31,15 @@ For feature requests, please raise them on the [mailing list](https://groups.goo | ||||
| ## 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  | ||||
| of existing code, it may well get rejected if you haven't discussed it on | ||||
| the [mailing list](https://groups.google.com/forum/#!forum/node-red) first. | ||||
|  | ||||
| ### 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  | ||||
| a Contributor License Agreement (CLA). This clarifies the intellectual | ||||
| property license granted with any contribution. It is for your protection as a | ||||
| Contributor as well as the protection of IBM and its customers; it does not | ||||
| change your rights to use your own Contributions for any other purpose. | ||||
|  | ||||
| You can download the CLAs here: | ||||
| @@ -59,13 +57,5 @@ code base. Some basic rules include: | ||||
|  | ||||
|  - all files must have the Apache license in the header. | ||||
|  - indent with 4-spaces, no tabs. No arguments. | ||||
|  - opening brace on same line as `if`/`for`/`function`/etc, closing brace on its | ||||
|    own line. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  - opening brace on same line as `if`/`for`/`function` and so on, closing brace | ||||
|  on its own line. | ||||
|   | ||||
							
								
								
									
										470
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2014 IBM Corp. | ||||
|  * 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. | ||||
| @@ -14,82 +14,410 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| var path = require("path"); | ||||
|  | ||||
| module.exports = function(grunt) { | ||||
|      | ||||
|     // Project configuration. | ||||
|  | ||||
|     var nodemonArgs = ["-v"]; | ||||
|     var flowFile = grunt.option('flowFile'); | ||||
|     if (flowFile) { | ||||
|         nodemonArgs.push(flowFile); | ||||
|     } | ||||
|  | ||||
|     grunt.initConfig({ | ||||
|             pkg: grunt.file.readJSON('package.json'), | ||||
|             simplemocha: { | ||||
|                 options: { | ||||
|                     globals: ['expect'], | ||||
|                     timeout: 3000, | ||||
|                     ignoreLeaks: false, | ||||
|                     ui: 'bdd', | ||||
|                     reporter: 'spec' | ||||
|                 }, | ||||
|                 all: { src: ['test/**/*_spec.js'] }, | ||||
|                 core: { src: ["test/_spec.js","test/red/**/*_spec.js"]}, | ||||
|                 nodes: { src: ["test/nodes/**/*_spec.js"]} | ||||
|         pkg: grunt.file.readJSON('package.json'), | ||||
|         paths: { | ||||
|             dist: ".dist" | ||||
|         }, | ||||
|         simplemocha: { | ||||
|             options: { | ||||
|                 globals: ['expect'], | ||||
|                 timeout: 3000, | ||||
|                 ignoreLeaks: false, | ||||
|                 ui: 'bdd', | ||||
|                 reporter: 'spec' | ||||
|             }, | ||||
|             jshint: { | ||||
|                 options: { | ||||
|                     // http://www.jshint.com/docs/options/ | ||||
|                     "asi": true,      // allow missing semicolons | ||||
|                     "curly": true,    // require braces | ||||
|                     "eqnull": true,   // ignore ==null | ||||
|                     "forin": true,    // require property filtering in "for in" loops | ||||
|                     "immed": true,    // require immediate functions to be wrapped in ( ) | ||||
|                     "nonbsp": true,   // warn on unexpected whitespace breaking chars | ||||
|                     //"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually | ||||
|                     "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', | ||||
|                     'red/**/*.js', | ||||
|                     'nodes/**/*.js', | ||||
|                     'public/red/**/*.js' | ||||
|                 ], | ||||
|                  | ||||
|                 core: { | ||||
|                     files: { | ||||
|                         src: [ | ||||
|                             'Gruntfile.js', | ||||
|                             'red.js', | ||||
|                             'red/**/*.js' | ||||
|                         ] | ||||
|                     } | ||||
|                 }, | ||||
|                 nodes: { | ||||
|                     files: { | ||||
|                         src: [ 'nodes/**/*.js' ] | ||||
|                     } | ||||
|                 }, | ||||
|                 editor: { | ||||
|                     files: { | ||||
|                         src: [ 'public/red/**/*.js' ] | ||||
|                     } | ||||
|                 }, | ||||
|                 tests: { | ||||
|                     files: { | ||||
|                         src: ['test/**/*.js'] | ||||
|                     }, | ||||
|                     options: { | ||||
|                         "expr": true | ||||
|                     } | ||||
|             all: { src: ['test/**/*_spec.js'] }, | ||||
|             core: { src: ["test/_spec.js","test/red/**/*_spec.js"]}, | ||||
|             nodes: { src: ["test/nodes/**/*_spec.js"]} | ||||
|         }, | ||||
|         jshint: { | ||||
|             options: { | ||||
|                 jshintrc:true | ||||
|                 // http://www.jshint.com/docs/options/ | ||||
|                 //"asi": true,      // allow missing semicolons | ||||
|                 //"curly": true,    // require braces | ||||
|                 //"eqnull": true,   // ignore ==null | ||||
|                 //"forin": true,    // require property filtering in "for in" loops | ||||
|                 //"immed": true,    // require immediate functions to be wrapped in ( ) | ||||
|                 //"nonbsp": true,   // warn on unexpected whitespace breaking chars | ||||
|                 ////"strict": true, // commented out for now as it causes 100s of warnings, but want to get there eventually | ||||
|                 //"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', | ||||
|                 'red/**/*.js', | ||||
|                 'nodes/core/*/*.js', | ||||
|                 'editor/js/**/*.js' | ||||
|             ], | ||||
|             core: { | ||||
|                 files: { | ||||
|                     src: [ | ||||
|                         'Gruntfile.js', | ||||
|                         'red.js', | ||||
|                         'red/**/*.js' | ||||
|                     ] | ||||
|                 } | ||||
|             }, | ||||
|             nodes: { | ||||
|                 files: { | ||||
|                     src: [ 'nodes/core/*/*.js' ] | ||||
|                 } | ||||
|             }, | ||||
|             editor: { | ||||
|                 files: { | ||||
|                     src: [ 'editor/js/**/*.js' ] | ||||
|                 } | ||||
|             }, | ||||
|             tests: { | ||||
|                 files: { | ||||
|                     src: ['test/**/*.js'] | ||||
|                 }, | ||||
|                 options: { | ||||
| 					"expr": true | ||||
|                 } | ||||
|                  | ||||
|             } | ||||
|         }, | ||||
|         concat: { | ||||
|             options: { | ||||
|                 separator: ";", | ||||
|             }, | ||||
|             build: { | ||||
|               src: [ | ||||
|                   // Ensure editor source files are concatenated in | ||||
|                   // the right order | ||||
|                   "editor/js/main.js", | ||||
|                   "editor/js/events.js", | ||||
|                   "editor/js/i18n.js", | ||||
|                   "editor/js/settings.js", | ||||
|                   "editor/js/user.js", | ||||
|                   "editor/js/comms.js", | ||||
|                   "editor/js/ui/state.js", | ||||
|                   "editor/js/nodes.js", | ||||
|                   "editor/js/history.js", | ||||
|                   "editor/js/validators.js", | ||||
|                   "editor/js/ui/deploy.js", | ||||
|                   "editor/js/ui/menu.js", | ||||
|                   "editor/js/ui/keyboard.js", | ||||
|                   "editor/js/ui/tabs.js", | ||||
|                   "editor/js/ui/popover.js", | ||||
|                   "editor/js/ui/workspaces.js", | ||||
|                   "editor/js/ui/view.js", | ||||
|                   "editor/js/ui/sidebar.js", | ||||
|                   "editor/js/ui/palette.js", | ||||
|                   "editor/js/ui/tab-info.js", | ||||
|                   "editor/js/ui/tab-config.js", | ||||
|                   "editor/js/ui/editor.js", | ||||
|                   "editor/js/ui/clipboard.js", | ||||
|                   "editor/js/ui/library.js", | ||||
|                   "editor/js/ui/notifications.js", | ||||
|                   "editor/js/ui/subflow.js", | ||||
|                   "editor/js/ui/touch/radialMenu.js" | ||||
|               ], | ||||
|               dest: "public/red/red.js" | ||||
|             }, | ||||
|             vendor: { | ||||
|                 files: { | ||||
|                     "public/vendor/vendor.js": [ | ||||
|                         "editor/vendor/jquery/js/jquery-1.11.1.min.js", | ||||
|                         "editor/vendor/bootstrap/js/bootstrap.min.js", | ||||
|                         "editor/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js", | ||||
|                         "editor/vendor/jquery/js/jquery.ui.touch-punch.min.js", | ||||
|                         "editor/vendor/marked/marked.min.js", | ||||
|                         "editor/vendor/orion/built-editor.min.js", | ||||
|                         "editor/vendor/d3/d3.v3.min.js", | ||||
|                         "editor/vendor/i18next/i18next.min.js" | ||||
|                     ], | ||||
|                     "public/vendor/vendor.css": [ | ||||
|                         "editor/vendor/orion/built-editor.css" | ||||
|                         // TODO: resolve relative resource paths in | ||||
|                         //       bootstrap/FA/jquery | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         uglify: { | ||||
|             build: { | ||||
|                 files: { | ||||
|                     'public/red/red.min.js': 'public/red/red.js' | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         sass: { | ||||
|             build: { | ||||
|                 options: { | ||||
|                     outputStyle: 'compressed' | ||||
|                 }, | ||||
|                 files: [{ | ||||
|                     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: [ | ||||
|                     'nodes/core/locales/en-US/messages.json', | ||||
|                     'locales/en-US/editor.json', | ||||
|                     'locales/en-US/runtime.json' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         attachCopyright: { | ||||
|             js: { | ||||
|                 src: [ | ||||
|                     'public/red/red.min.js' | ||||
|                 ] | ||||
|             }, | ||||
|             css: { | ||||
|                 src: [ | ||||
|                     'public/red/style.min.css' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         clean: { | ||||
|             build: { | ||||
|                 src: [ | ||||
|                     "public/red", | ||||
|                     "public/index.html", | ||||
|                     "public/favicon.ico", | ||||
|                     "public/icons", | ||||
|                     "public/vendor" | ||||
|                 ] | ||||
|             }, | ||||
|             release: { | ||||
|                 src: [ | ||||
|                     '<%= paths.dist %>' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         watch: { | ||||
|             js: { | ||||
|                 files: [ | ||||
|                     'editor/js/**/*.js' | ||||
|                 ], | ||||
|                 tasks: ['concat','uglify','attachCopyright:js'] | ||||
|             }, | ||||
|             sass: { | ||||
|                 files: [ | ||||
|                     'editor/sass/**/*.scss' | ||||
|                 ], | ||||
|                 tasks: ['sass','attachCopyright:css'] | ||||
|             }, | ||||
|             json: { | ||||
|                 files: [ | ||||
|                     'nodes/core/locales/en-US/messages.json', | ||||
|                     'locales/en-US/editor.json', | ||||
|                     'locales/en-US/runtime.json' | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:messages'] | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         nodemon: { | ||||
|             /* uses .nodemonignore */ | ||||
|             dev: { | ||||
|                 script: 'red.js', | ||||
|                 options: { | ||||
|                     args: nodemonArgs, | ||||
|                     ext: 'js,html,json', | ||||
|                     watch: [ | ||||
|                         'red','nodes','locales' | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         concurrent: { | ||||
|             dev: { | ||||
|                 tasks: ['nodemon', 'watch'], | ||||
|                 options: { | ||||
|                     logConcurrentOutput: true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         copy: { | ||||
|             build: { | ||||
|                 files:[{ | ||||
|                     cwd: 'editor/images', | ||||
|                     src: '**', | ||||
|                     expand: true, | ||||
|                     dest: 'public/red/images/' | ||||
|                 }, | ||||
|                 { | ||||
|                     cwd: 'editor/vendor', | ||||
|                     src: [ | ||||
|                         'ace/**', | ||||
|                         //'bootstrap/css/**', | ||||
|                         'bootstrap/img/**', | ||||
|                         'jquery/css/**', | ||||
|                         'font-awesome/**' | ||||
|                     ], | ||||
|                     expand: true, | ||||
|                     dest: 'public/vendor/' | ||||
|                 }, | ||||
|                 { | ||||
|                     cwd: 'editor/icons', | ||||
|                     src: '**', | ||||
|                     expand: true, | ||||
|                     dest: 'public/icons/' | ||||
|                 }, | ||||
|                 { | ||||
|                     expand: true, | ||||
|                     src: ['editor/index.html','editor/favicon.ico'], | ||||
|                     dest: 'public/', | ||||
|                     flatten: true | ||||
|                 }] | ||||
|             }, | ||||
|             release: { | ||||
|                 files: [{ | ||||
|                     mode: true, | ||||
|                     expand: true, | ||||
|                     src: [ | ||||
|                         '*.md', | ||||
|                         'LICENSE', | ||||
|                         'package.json', | ||||
|                         'settings.js', | ||||
|                         'red.js', | ||||
|                         'lib/.gitignore', | ||||
|                         'nodes/*.demo', | ||||
|                         'nodes/core/**', | ||||
|                         'red/**', | ||||
|                         'public/**', | ||||
|                         'editor/templates/**', | ||||
|                         'bin/**', | ||||
|                         'locales/**' | ||||
|                     ], | ||||
|                     dest: path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>') | ||||
|                 }] | ||||
|             } | ||||
|         }, | ||||
|         chmod: { | ||||
|             options: { | ||||
|                 mode: '755' | ||||
|             }, | ||||
|             release: { | ||||
|                 // Target-specific file/dir lists and/or options go here. | ||||
|                 src: [ | ||||
|                     path.resolve('<%= paths.dist %>/node-red-<%= pkg.version %>/nodes/core/hardware/nrgpio*') | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         compress: { | ||||
|             release: { | ||||
|                 options: { | ||||
|                     archive: '<%= paths.dist %>/node-red-<%= pkg.version %>.zip' | ||||
|                 }, | ||||
|                 expand: true, | ||||
|                 cwd: '<%= paths.dist %>/', | ||||
|                 src: ['node-red-<%= pkg.version %>/**'] | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|      | ||||
|  | ||||
|     grunt.loadNpmTasks('grunt-simple-mocha'); | ||||
|     grunt.loadNpmTasks('grunt-contrib-jshint'); | ||||
|      | ||||
|     grunt.registerTask('default', ['test-core','test-editor','test-nodes']); | ||||
|      | ||||
|     grunt.registerTask('test-core', ['jshint:core','simplemocha:core']); | ||||
|     grunt.registerTask('test-editor', ['jshint:editor']); | ||||
|     grunt.registerTask('test-nodes', ['simplemocha:nodes']); | ||||
|      | ||||
|     grunt.loadNpmTasks('grunt-contrib-concat'); | ||||
|     grunt.loadNpmTasks('grunt-contrib-uglify'); | ||||
|     grunt.loadNpmTasks('grunt-contrib-clean'); | ||||
|     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'); | ||||
|  | ||||
|     grunt.registerMultiTask('attachCopyright', function() { | ||||
|         var files = this.data.src; | ||||
|         var copyright = "/**\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"+ | ||||
|             " * You may obtain a copy of the License at\n"+ | ||||
|             " *\n"+ | ||||
|             " * http://www.apache.org/licenses/LICENSE-2.0\n"+ | ||||
|             " *\n"+ | ||||
|             " * Unless required by applicable law or agreed to in writing, software\n"+ | ||||
|             " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"+ | ||||
|             " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"+ | ||||
|             " * See the License for the specific language governing permissions and\n"+ | ||||
|             " * limitations under the License.\n"+ | ||||
|             " **/\n"; | ||||
|  | ||||
|         if (files) { | ||||
|             for (var i=0;i<files.length;i++) { | ||||
|                 var file = files[i]; | ||||
|                 if (!grunt.file.exists(file)) { | ||||
|                     grunt.log.warn('File '+ file + ' not found'); | ||||
|                     return false; | ||||
|                 } else { | ||||
|                     var content = grunt.file.read(file); | ||||
|                     if (content.indexOf(copyright) == -1) { | ||||
|                         content = copyright+content; | ||||
|                         if (!grunt.file.write(file, content)) { | ||||
|                             return false; | ||||
|                         } | ||||
|                         grunt.log.writeln("Attached copyright to "+file); | ||||
|                     } else { | ||||
|                         grunt.log.writeln("Copyright already on "+file); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     grunt.registerTask('setDevEnv', | ||||
|         'Sets NODE_ENV=development so non-minified assets are used', | ||||
|             function () { | ||||
|                 process.env.NODE_ENV = 'development'; | ||||
|             }); | ||||
|  | ||||
|     grunt.registerTask('default', | ||||
|         'Builds editor content then runs code style checks and unit tests on all components', | ||||
|         ['build','test-core','test-editor','test-nodes']); | ||||
|  | ||||
|     grunt.registerTask('test-core', | ||||
|         'Runs code style check and unit tests on core runtime code', | ||||
|         ['jshint:core','simplemocha:core']); | ||||
|  | ||||
|     grunt.registerTask('test-editor', | ||||
|         'Runs code style check on editor code', | ||||
|         ['jshint:editor']); | ||||
|  | ||||
|     grunt.registerTask('test-nodes', | ||||
|         'Runs unit tests on core nodes', | ||||
|         ['simplemocha:nodes']); | ||||
|  | ||||
|     grunt.registerTask('build', | ||||
|         'Builds editor content', | ||||
|         ['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', | ||||
|         ['build','setDevEnv','concurrent:dev']); | ||||
|  | ||||
|     grunt.registerTask('release', | ||||
|         'Create distribution zip file', | ||||
|         ['build','clean:release','copy:release','chmod:release','compress:release']); | ||||
|  | ||||
| }; | ||||
|   | ||||
							
								
								
									
										57
									
								
								INSTALL.md
									
									
									
									
									
								
							
							
						
						| @@ -1,57 +0,0 @@ | ||||
| Node-RED Install | ||||
| ================ | ||||
|  | ||||
| ## Install node.js | ||||
|  | ||||
| You can get the latest version from <http://nodejs.org/download/>. | ||||
|  | ||||
| Or, you may want to use a version from your operating system's package manager: | ||||
|  <https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager> | ||||
|  | ||||
| ## Get Node-RED | ||||
|  | ||||
| Clone the repository from GitHub: | ||||
|  | ||||
|     $ git clone git@github.com:node-red/node-red.git | ||||
|  | ||||
| ## Install the pre-requisite modules | ||||
|  | ||||
| From the top-level directory of Node-RED, run: | ||||
|  | ||||
|     $ npm install | ||||
|  | ||||
| This will install the core pre-requisite modules. | ||||
|  | ||||
| ## Run Node-RED | ||||
|  | ||||
| From the top-level directory, run: | ||||
|  | ||||
|     $ node red.js | ||||
|  | ||||
| You can then access Node-RED at <http://localhost:1880>. | ||||
|  | ||||
| Online documentation is available at <http://nodered.org/docs>. | ||||
|  | ||||
| ## Installing individual node dependencies | ||||
|  | ||||
| When Node-RED starts, it attempts to load the nodes from the `nodes/` directory. | ||||
| Each will have its own set of dependencies that will need to be installed before | ||||
| the node is available in the palette. | ||||
|  | ||||
| To help identify the dependencies, Node-RED logs any modules it fails to find | ||||
| for a particular node. You don't have to install these unless you want or need | ||||
| that node to appear. | ||||
|  | ||||
| Alternatively, a node's `.js` file can be examined to identify the modules it | ||||
| explicitly requires. For example, the Twitter node is defined in | ||||
| `nodes/social/27-twitter.js` and contains: | ||||
|  | ||||
| 	var RED = require("../../red/red"); | ||||
| 	var ntwitter = require('ntwitter'); | ||||
| 	var OAuth= require('oauth').OAuth; | ||||
|  | ||||
| Of these, `ntwitter` and `oauth` are neither built-in modules nor ones provided | ||||
| by Node-RED itself. They can subsequently be installed by running: | ||||
|  | ||||
|     $ npm install ntwitter oauth | ||||
|  | ||||
							
								
								
									
										49
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -2,39 +2,58 @@ | ||||
|  | ||||
| http://nodered.org | ||||
|  | ||||
| [](https://travis-ci.org/node-red/node-red) [](https://coveralls.io/r/node-red/node-red?branch=master) | ||||
|  | ||||
| [](https://travis-ci.org/node-red/node-red) | ||||
| [](https://coveralls.io/r/node-red/node-red?branch=master) | ||||
|  | ||||
| A visual tool for wiring the Internet of Things. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Quick Start | ||||
|  | ||||
| Check out [INSTALL](INSTALL.md) for full instructions on getting started. | ||||
| Check out http://nodered.org/docs/getting-started/ for full instructions on getting | ||||
| started. | ||||
|  | ||||
| 1. download the zip and unzip, or git clone | ||||
| 2. cd node-red | ||||
| 3. npm install | ||||
| 4. node red.js | ||||
| 5. Open <http://localhost:1880> | ||||
| 1. `sudo npm install -g node-red` | ||||
| 2. `node-red` | ||||
| 3. Open <http://localhost:1880> | ||||
|  | ||||
| ## Getting Help | ||||
|  | ||||
| More documentation can be found [here](http://nodered.org/docs). | ||||
|  | ||||
| For further help, or general discussion, please use the [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||
| For further help, or general discussion, please use the | ||||
| [mailing list](https://groups.google.com/forum/#!forum/node-red). | ||||
|  | ||||
| ## Browser Support | ||||
| ## Developers | ||||
|  | ||||
| The Node-RED editor runs in the browser. We routinely develop and test using | ||||
| Chrome and Firefox. We have anecdotal evidence that it works in recent versions of IE. | ||||
| If you want to run the latest code from git, here's how to get started: | ||||
|  | ||||
| We have basic support for using in mobile/tablet browsers. | ||||
| 1. Install grunt, the build tool | ||||
|  | ||||
|         npm install -g grunt-cli | ||||
|  | ||||
| 2. Clone the code: | ||||
|  | ||||
|         git clone https://github.com/node-red/node-red.git | ||||
|         cd node-red | ||||
|  | ||||
| 3. Install the node-red dependencies | ||||
|  | ||||
|         npm install | ||||
|  | ||||
| 4. Build the code | ||||
|  | ||||
|         grunt build | ||||
|  | ||||
| 5. Run | ||||
|  | ||||
|         node red.js | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| Before raising a pull-request, please read our [contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md). | ||||
| Before raising a pull-request, please read our | ||||
| [contributing guide](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md). | ||||
|  | ||||
| ## Authors | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								editor/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
| Before Width: | Height: | Size: 308 B 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 | 
| Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 609 B | 
| Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 575 B | 
| Before Width: | Height: | Size: 601 B After Width: | Height: | Size: 601 B | 
| Before Width: | Height: | Size: 459 B After Width: | Height: | Size: 459 B | 
| Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B | 
| Before Width: | Height: | Size: 324 B After Width: | Height: | Size: 324 B | 
| Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B | 
| Before Width: | Height: | Size: 255 B After Width: | Height: | Size: 255 B | 
| Before Width: | Height: | Size: 457 B After Width: | Height: | Size: 457 B | 
| Before Width: | Height: | Size: 502 B After Width: | Height: | Size: 502 B | 
| Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B | 
| Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 639 B After Width: | Height: | Size: 639 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 | 
| Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B | 
| Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B | 
| Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B | 
| Before Width: | Height: | Size: 736 B After Width: | Height: | Size: 736 B | 
| Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B | 
| Before Width: | Height: | Size: 273 B After Width: | Height: | Size: 273 B | 
| Before Width: | Height: | Size: 439 B After Width: | Height: | Size: 439 B | 
| Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B | 
| Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 509 B | 
| Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 488 B | 
| Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 628 B | 
| Before Width: | Height: | Size: 258 B After Width: | Height: | Size: 258 B | 
| Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 404 B | 
| Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B | 
| Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B | 
| Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B | 
| Before Width: | Height: | Size: 386 B After Width: | Height: | Size: 386 B | 
| Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B | 
| Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B | 
| Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 290 B | 
| Before Width: | Height: | Size: 392 B 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 | 
							
								
								
									
										33
									
								
								editor/images/node-red-icon.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,33 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
| <svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="480" width="480" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 480.00002 479.99999"> | ||||
|  <title>Node-RED Icon</title> | ||||
|  <metadata> | ||||
|   <rdf:RDF> | ||||
|    <cc:Work rdf:about=""> | ||||
|     <dc:format>image/svg+xml</dc:format> | ||||
|     <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> | ||||
|     <dc:title>Node-RED Icon</dc:title> | ||||
|     <cc:license rdf:resource="http://creativecommons.org/licenses/by/3.0/"/> | ||||
|     <dc:creator> | ||||
|      <cc:Agent> | ||||
|       <dc:title>Nick O'Leary</dc:title> | ||||
|      </cc:Agent> | ||||
|     </dc:creator> | ||||
|    </cc:Work> | ||||
|    <cc:License rdf:about="http://creativecommons.org/licenses/by/3.0/"> | ||||
|     <cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/> | ||||
|     <cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/> | ||||
|     <cc:requires rdf:resource="http://creativecommons.org/ns#Notice"/> | ||||
|     <cc:requires rdf:resource="http://creativecommons.org/ns#Attribution"/> | ||||
|     <cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/> | ||||
|    </cc:License> | ||||
|   </rdf:RDF> | ||||
|  </metadata> | ||||
|  <g transform="translate(0 -572.36)"> | ||||
|   <rect style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry="56" height="448" width="448" y="588.36" x="16" fill="#8f0000"/> | ||||
|   <g transform="matrix(8.545 0 0 8.545 -786.19 -1949.8)"> | ||||
|    <path style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m104.41 321.21c0.0138-2.3846-1.905-4.2806-4.2896-4.2806h-6.243v2.9257h6.243c0.80513 0 1.4808 0.58383 1.4808 1.389v4.2422c0 0.80513-0.67566 1.5075-1.4808 1.5075h-6.243v2.8086h6.243c2.3846 0 4.2895-1.9315 4.2895-4.3162l-0.00005-1.9812c9.8659 0.14125 12.737 2.7065 15.877 5.4519 3.0241 2.6446 6.4153 5.4869 15.252 5.557l0.00046 0.97238c0.001 2.3846 1.9543 4.3803 4.3389 4.3803h6.4273v-3.0427h-6.4273c-0.80514 0-1.4135-0.53255-1.4135-1.3377v-4.2422c0-0.80513 0.60835-1.4418 1.4135-1.4418h6.4273v-2.8086h-6.4273c-2.3846 0-4.3379 1.8658-4.3389 4.2504l-0.00045 1.005c-8.351-0.0276-10.723-2.3434-13.76-4.9992-2.5914-2.2662-5.6368-4.7578-12.346-5.6642 0.0583-0.0501 0.11211-0.0987 0.16838-0.15027 1.2918-1.1846 1.9884-2.6158 2.6699-3.8516 0.68148-1.2357 1.3227-2.267 2.373-2.9879 0.85207-0.58483 2.0639-1.0208 3.926-1.1017l0.00018 0.99192c0.00043 2.3846 1.9236 4.4325 4.3083 4.4325h17.242c2.3846 0 4.3127-2.0479 4.3127-4.4325v-4.2422c0-2.3846-1.9281-4.3153-4.3127-4.3153h-17.242c-2.3846 0-4.3095 1.9306-4.3083 4.3153l0.00051 0.98395c-2.2474 0.0903-3.9508 0.6357-5.2079 1.4985-1.5245 1.0464-2.3662 2.4764-3.0762 3.7637-0.70992 1.2873-1.3108 2.4408-2.2188 3.2734-0.79034 0.72475-1.8834 1.2844-3.658 1.493zm18.468-12.356h17.242c0.80514 0 1.387 0.58455 1.387 1.3897v4.2422c0 0.80514-0.5819 1.3898-1.387 1.3898h-17.242c-0.80514 0-1.4994-0.58462-1.4994-1.3898v-4.2422c0-0.80513 0.69431-1.3897 1.4994-1.3897z" fill="#fff"/> | ||||
|   </g> | ||||
|  </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 3.3 KiB | 
| Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 1019 B | 
| Before Width: | Height: | Size: 600 B After Width: | Height: | Size: 600 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/subflow_tab.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 410 B | 
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2014 IBM Corp. | ||||
|  * Copyright 2014, 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -13,23 +13,30 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|   | ||||
| 
 | ||||
| RED.comms = (function() { | ||||
|      | ||||
| 
 | ||||
|     var errornotification = null; | ||||
|     var clearErrorTimer = null; | ||||
|      | ||||
| 
 | ||||
|     var subscriptions = {}; | ||||
|     var ws; | ||||
|     var pendingAuth = false; | ||||
|      | ||||
|     var reconnectAttempts = 0; | ||||
| 
 | ||||
|     function connectWS() { | ||||
|         var path = location.hostname+":"+location.port+document.location.pathname; | ||||
|         var path = location.hostname; | ||||
|         var port = location.port; | ||||
|         if (port.length !== 0) { | ||||
|             path = path+":"+port; | ||||
|         } | ||||
|         path = path+document.location.pathname; | ||||
|         path = path+(path.slice(-1) == "/"?"":"/")+"comms"; | ||||
|         path = "ws"+(document.location.protocol=="https:"?"s":"")+"://"+path; | ||||
| 
 | ||||
|         var auth_tokens = RED.settings.get("auth-tokens"); | ||||
|         pendingAuth = (auth_tokens!=null); | ||||
|          | ||||
| 
 | ||||
|         function completeConnection() { | ||||
|             for (var t in subscriptions) { | ||||
|                 if (subscriptions.hasOwnProperty(t)) { | ||||
| @@ -37,9 +44,10 @@ RED.comms = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         ws = new WebSocket(path); | ||||
|         ws.onopen = function() { | ||||
|             reconnectAttempts = 0; | ||||
|             if (errornotification) { | ||||
|                 clearErrorTimer = setTimeout(function() { | ||||
|                     errornotification.close(); | ||||
| @@ -74,16 +82,17 @@ RED.comms = (function() { | ||||
|             } | ||||
|         }; | ||||
|         ws.onclose = function() { | ||||
|             if (errornotification == null) { | ||||
|                 errornotification = RED.notify("<b>Error</b>: Lost connection to server","error",true); | ||||
|             if (reconnectAttempts > 5 && errornotification == null) { | ||||
|                 errornotification = RED.notify(RED._("notification.error",{message:RED._("notification.errors.lostConnection")}),"error",true); | ||||
|             } else if (clearErrorTimer) { | ||||
|                 clearTimeout(clearErrorTimer); | ||||
|                 clearErrorTimer = null; | ||||
|             } | ||||
|             reconnectAttempts++; | ||||
|             setTimeout(connectWS,1000); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function subscribe(topic,callback) { | ||||
|         if (subscriptions[topic] == null) { | ||||
|             subscriptions[topic] = []; | ||||
| @@ -93,7 +102,7 @@ RED.comms = (function() { | ||||
|             ws.send(JSON.stringify({subscribe:topic})); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function unsubscribe(topic,callback) { | ||||
|         if (subscriptions[topic]) { | ||||
|             for (var i=0;i<subscriptions[topic].length;i++) { | ||||
| @@ -107,7 +116,7 @@ RED.comms = (function() { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     return { | ||||
|         connect: connectWS, | ||||
|         subscribe: subscribe, | ||||
							
								
								
									
										48
									
								
								editor/js/events.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,48 @@ | ||||
| /** | ||||
|  * 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.events = (function() { | ||||
|      var handlers = {}; | ||||
|  | ||||
|      function on(evt,func) { | ||||
|          handlers[evt] = handlers[evt]||[]; | ||||
|          handlers[evt].push(func); | ||||
|      } | ||||
|      function off(evt,func) { | ||||
|          var handler = handlers[evt]; | ||||
|          if (handler) { | ||||
|              for (var i=0;i<handler.length;i++) { | ||||
|                  if (handler[i] === func) { | ||||
|                      handler.splice(i,1); | ||||
|                      return; | ||||
|                  } | ||||
|              } | ||||
|          } | ||||
|      } | ||||
|      function emit(evt,arg) { | ||||
|          if (handlers[evt]) { | ||||
|              for (var i=0;i<handlers[evt].length;i++) { | ||||
|                  handlers[evt][i](arg); | ||||
|              } | ||||
|  | ||||
|          } | ||||
|      } | ||||
|      return { | ||||
|          on: on, | ||||
|          off: off, | ||||
|          emit: emit | ||||
|      } | ||||
|  })(); | ||||
| @@ -15,7 +15,7 @@ | ||||
|  **/ | ||||
| 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() { | ||||
| @@ -33,6 +33,7 @@ RED.history = (function() { | ||||
|             var ev = undo_history.pop(); | ||||
|             var i; | ||||
|             var node; | ||||
|             var subflow; | ||||
|             var modifiedTabs = {}; | ||||
|             if (ev) { | ||||
|                 if (ev.t == 'add') { | ||||
| @@ -62,6 +63,23 @@ RED.history = (function() { | ||||
|                             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; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else if (ev.t == "delete") { | ||||
|                     if (ev.workspaces) { | ||||
|                         for (i=0;i<ev.workspaces.length;i++) { | ||||
| @@ -69,10 +87,9 @@ RED.history = (function() { | ||||
|                             RED.workspaces.add(ev.workspaces[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow) { | ||||
|                         RED.nodes.addSubflow(ev.subflow); | ||||
|                     if (ev.subflow && ev.subflow.subflow) { | ||||
|                         RED.nodes.addSubflow(ev.subflow.subflow); | ||||
|                     } | ||||
|                     var subflow; | ||||
|                     if (ev.subflowInputs && ev.subflowInputs.length > 0) { | ||||
|                         subflow = RED.nodes.subflow(ev.subflowInputs[0].z); | ||||
|                         subflow.in.push(ev.subflowInputs[0]); | ||||
| @@ -97,9 +114,17 @@ RED.history = (function() { | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     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.changed = true; | ||||
|                             n.inputs = subflow.in.length; | ||||
|                             n.outputs = subflow.out.length; | ||||
|                             while (n.outputs > n.ports.length) { | ||||
| @@ -120,6 +145,22 @@ RED.history = (function() { | ||||
|                             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]; | ||||
| @@ -130,6 +171,17 @@ RED.history = (function() { | ||||
|                 } 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]; | ||||
|                         } | ||||
|                     } | ||||
| @@ -148,14 +200,24 @@ RED.history = (function() { | ||||
|                                 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.changed = ev.changed; | ||||
|                             n.inputs = ev.node.in.length; | ||||
|                             n.outputs = ev.node.out.length; | ||||
|                             RED.editor.updateNodeProperties(n); | ||||
|                         }); | ||||
|                          | ||||
|                         RED.palette.refresh(); | ||||
| 
 | ||||
|                         if (ev.node.type === 'subflow') { | ||||
|                             $("#menu-item-workspace-menu-"+ev.node.id.replace(".","-")).text(ev.node.name); | ||||
|                         } | ||||
|                     } else { | ||||
|                         RED.editor.updateNodeProperties(ev.node); | ||||
|                         RED.editor.validateNode(ev.node); | ||||
| @@ -169,7 +231,7 @@ RED.history = (function() { | ||||
|                     ev.node.changed = ev.changed; | ||||
|                 } else if (ev.t == "createSubflow") { | ||||
|                     if (ev.nodes) { | ||||
|                         RED.nodes.filterNodes({z:ev.subflow.id}).forEach(function(n) { | ||||
|                         RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) { | ||||
|                             n.z = ev.activeWorkspace; | ||||
|                             n.dirty = true; | ||||
|                         }); | ||||
| @@ -182,10 +244,10 @@ RED.history = (function() { | ||||
|                             RED.nodes.removeLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|                      | ||||
|                     RED.nodes.removeSubflow(ev.subflow); | ||||
|                     RED.workspaces.remove(ev.subflow); | ||||
|                      | ||||
| 
 | ||||
|                     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]); | ||||
| @@ -199,10 +261,10 @@ RED.history = (function() { | ||||
|                     } | ||||
|                 }); | ||||
| 
 | ||||
|                  | ||||
|                 RED.nodes.dirty(ev.dirty); | ||||
|                 RED.view.redraw(true); | ||||
|                 RED.palette.refresh(); | ||||
|                 RED.workspaces.refresh(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
							
								
								
									
										43
									
								
								editor/js/i18n.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,43 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| RED.i18n = (function() { | ||||
|  | ||||
|     return { | ||||
|         init: function(done) { | ||||
|             i18n.init({ | ||||
|                 resGetPath: 'locales/__ns__', | ||||
|                 dynamicLoad: false, | ||||
|                 load:'current', | ||||
|                 ns: { | ||||
|                     namespaces: ["editor","node-red"], | ||||
|                     defaultNs: "editor" | ||||
|                 }, | ||||
|                 fallbackLng: ['en-US'], | ||||
|                 useCookie: false | ||||
|             },function() { | ||||
|                 done(); | ||||
|             }); | ||||
|             RED["_"] = function() { | ||||
|                 return i18n.t.apply(null,arguments); | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|         loadCatalog: function(namespace,done) { | ||||
|             i18n.loadNamespace(namespace,done); | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
| @@ -16,10 +16,6 @@ | ||||
| var RED = (function() { | ||||
| 
 | ||||
| 
 | ||||
|     function loadSettings() { | ||||
|         RED.settings.init(loadNodeList); | ||||
|     } | ||||
| 
 | ||||
|     function loadNodeList() { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
| @@ -29,7 +25,23 @@ var RED = (function() { | ||||
|             url: 'nodes', | ||||
|             success: function(data) { | ||||
|                 RED.nodes.setNodeList(data); | ||||
|                 loadNodes(); | ||||
| 
 | ||||
|                 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(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| @@ -43,6 +55,9 @@ var RED = (function() { | ||||
|             url: 'nodes', | ||||
|             success: function(data) { | ||||
|                 $("body").append(data); | ||||
|                 $("body").i18n(); | ||||
| 
 | ||||
| 
 | ||||
|                 $(".palette-spinner").hide(); | ||||
|                 $(".palette-scroll").show(); | ||||
|                 $("#palette-search").show(); | ||||
| @@ -66,6 +81,9 @@ var RED = (function() { | ||||
|                     var parts = topic.split("/"); | ||||
|                     var node = RED.nodes.node(parts[1]); | ||||
|                     if (node) { | ||||
|                         if (msg.text) { | ||||
|                             msg.text = node._(msg.text.toString(),{defaultValue:msg.text.toString()}); | ||||
|                         } | ||||
|                         node.status = msg; | ||||
|                         if (statusEnabled) { | ||||
|                             node.dirty = true; | ||||
| @@ -91,7 +109,7 @@ var RED = (function() { | ||||
|                         } | ||||
|                         if (addedTypes.length) { | ||||
|                             typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>"; | ||||
|                             RED.notify("Node"+(addedTypes.length!=1 ? "s":"")+" added to palette:"+typeList,"success"); | ||||
|                             RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success"); | ||||
|                         } | ||||
|                     } else if (topic == "node/removed") { | ||||
|                         for (i=0;i<msg.length;i++) { | ||||
| @@ -99,7 +117,7 @@ var RED = (function() { | ||||
|                             info = RED.nodes.removeNodeSet(m.id); | ||||
|                             if (info.added) { | ||||
|                                 typeList = "<ul><li>"+m.types.join("</li><li>")+"</li></ul>"; | ||||
|                                 RED.notify("Node"+(m.types.length!=1 ? "s":"")+" removed from palette:"+typeList,"success"); | ||||
|                                 RED.notify(RED._("palette.event.nodeRemoved", {count:m.types.length})+typeList,"success"); | ||||
|                             } | ||||
|                         } | ||||
|                     } else if (topic == "node/enabled") { | ||||
| @@ -108,12 +126,12 @@ var RED = (function() { | ||||
|                             if (info.added) { | ||||
|                                 RED.nodes.enableNodeSet(msg.id); | ||||
|                                 typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||
|                                 RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" enabled:"+typeList,"success"); | ||||
|                                 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("Node"+(msg.types.length!=1 ? "s":"")+" added to palette:"+typeList,"success"); | ||||
|                                     RED.notify(RED._("palette.event.nodeAdded", {count:msg.types.length})+typeList,"success"); | ||||
|                                 }); | ||||
|                             } | ||||
|                         } | ||||
| @@ -121,7 +139,7 @@ var RED = (function() { | ||||
|                         if (msg.types) { | ||||
|                             RED.nodes.disableNodeSet(msg.id); | ||||
|                             typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; | ||||
|                             RED.notify("Node"+(msg.types.length!=1 ? "s":"")+" disabled:"+typeList,"success"); | ||||
|                             RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success"); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
| @@ -138,39 +156,43 @@ var RED = (function() { | ||||
|     function loadEditor() { | ||||
|         RED.menu.init({id:"btn-sidemenu", | ||||
|             options: [ | ||||
|                 {id:"btn-sidebar",label:"Sidebar",toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}, | ||||
|                 {id:"btn-node-status",label:"Display node status",toggle:true,onselect:toggleStatus, selected: true}, | ||||
|                 null, | ||||
|                 {id:"btn-import-menu",label:"Import",options:[ | ||||
|                     {id:"btn-import-clipboard",label:"Clipboard",onselect:RED.clipboard.import}, | ||||
|                     {id:"btn-import-library",label:"Library",options:[]} | ||||
|                 {id:"menu-item-sidebar-menu",label:RED._("menu.label.sidebar.sidebar"),options:[ | ||||
|                     {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true}, | ||||
|                     null | ||||
|                 ]}, | ||||
|                 {id:"btn-export-menu",label:"Export",disabled:true,options:[ | ||||
|                     {id:"btn-export-clipboard",label:"Clipboard",disabled:true,onselect:RED.clipboard.export}, | ||||
|                     {id:"btn-export-library",label:"Library",disabled:true,onselect:RED.library.export} | ||||
|                 {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true}, | ||||
|                 null, | ||||
|                 {id:"menu-item-import",label:RED._("menu.label.import"),options:[ | ||||
|                     {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import}, | ||||
|                     {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]} | ||||
|                 ]}, | ||||
|                 {id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[ | ||||
|                     {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export}, | ||||
|                     {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export} | ||||
|                 ]}, | ||||
|                 null, | ||||
|                 {id:"btn-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show}, | ||||
|                 null, | ||||
|                 {id:"btn-subflow-menu",label:"Subflows", options: [ | ||||
|                     {id:"btn-create-subflow",label:"Create subflow",onselect:RED.subflow.createSubflow}, | ||||
|                     {id:"btn-convert-subflow",label:"Selection to subflow",disabled:true,onselect:RED.subflow.convertToSubflow}, | ||||
|                 {id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [ | ||||
|                     {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow}, | ||||
|                     {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow}, | ||||
|                 ]}, | ||||
|                 null, | ||||
|                 {id:"btn-workspace-menu",label:"Workspaces",options:[ | ||||
|                     {id:"btn-workspace-add",label:"Add",onselect:RED.workspaces.add}, | ||||
|                     {id:"btn-workspace-edit",label:"Rename",onselect:RED.workspaces.edit}, | ||||
|                     {id:"btn-workspace-delete",label:"Delete",onselect:RED.workspaces.remove}, | ||||
|                 {id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[ | ||||
|                     {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add}, | ||||
|                     {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit}, | ||||
|                     {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove}, | ||||
|                     null | ||||
|                 ]}, | ||||
|                 null, | ||||
|                 {id:"btn-keyboard-shortcuts",label:"Keyboard Shortcuts",onselect:RED.keyboard.showHelp}, | ||||
|                 {id:"btn-help",label:"Node-RED Website", href:"http://nodered.org/docs"} | ||||
|                 {id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp}, | ||||
|                 {id:"menu-item-help", | ||||
|                     label: RED.settings.theme("menu.menu-item-help.label","Node-RED Website"), | ||||
|                     href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs") | ||||
|                 } | ||||
|             ] | ||||
|         }); | ||||
|          | ||||
| 
 | ||||
|         RED.user.init(); | ||||
|          | ||||
| 
 | ||||
|         RED.library.init(); | ||||
|         RED.palette.init(); | ||||
|         RED.sidebar.init(); | ||||
| @@ -178,8 +200,10 @@ var RED = (function() { | ||||
|         RED.workspaces.init(); | ||||
|         RED.clipboard.init(); | ||||
|         RED.view.init(); | ||||
|         RED.deploy.init(); | ||||
|          | ||||
|         RED.editor.init(); | ||||
| 
 | ||||
|         RED.deploy.init(RED.settings.theme("deployButton",null)); | ||||
| 
 | ||||
|         RED.keyboard.add(/* ? */ 191,{shift:true},function(){RED.keyboard.showHelp();d3.event.preventDefault();}); | ||||
|         RED.comms.connect(); | ||||
| 
 | ||||
| @@ -190,14 +214,16 @@ var RED = (function() { | ||||
|     } | ||||
| 
 | ||||
|     $(function() { | ||||
|              | ||||
| 
 | ||||
|         if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) { | ||||
|             document.title = "Node-RED : "+window.location.hostname; | ||||
|             document.title = document.title+" : "+window.location.hostname; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         ace.require("ace/ext/language_tools"); | ||||
| 
 | ||||
|         RED.settings.init(loadEditor); | ||||
|         RED.i18n.init(function() { | ||||
|             RED.settings.init(loadEditor); | ||||
|         }) | ||||
|     }); | ||||
| 
 | ||||
| 
 | ||||
| @@ -22,20 +22,20 @@ RED.nodes = (function() { | ||||
|     var defaultWorkspace; | ||||
|     var workspaces = {}; | ||||
|     var subflows = {}; | ||||
|      | ||||
| 
 | ||||
|     var dirty = false; | ||||
|      | ||||
| 
 | ||||
|     function setDirty(d) { | ||||
|         dirty = d; | ||||
|         eventHandler.emit("change",{dirty:dirty}); | ||||
|         RED.events.emit("nodes:change",{dirty:dirty}); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     var registry = (function() { | ||||
|         var nodeList = []; | ||||
|         var nodeSets = {}; | ||||
|         var typeToId = {}; | ||||
|         var nodeDefinitions = {}; | ||||
|          | ||||
| 
 | ||||
|         var exports = { | ||||
|             getNodeList: function() { | ||||
|                 return nodeList; | ||||
| @@ -107,7 +107,23 @@ RED.nodes = (function() { | ||||
|             registerNodeType: function(nt,def) { | ||||
|                 nodeDefinitions[nt] = def; | ||||
|                 if (def.category != "subflows") { | ||||
|                     def.set = nodeSets[typeToId[nt]]; | ||||
|                     nodeSets[typeToId[nt]].added = true; | ||||
| 
 | ||||
|                     var ns; | ||||
|                     if (def.set.module === "node-red") { | ||||
|                         ns = "node-red"; | ||||
|                     } else { | ||||
|                         ns = def.set.id; | ||||
|                     } | ||||
|                     def["_"] = function() { | ||||
|                         var args = Array.prototype.slice.call(arguments, 0); | ||||
|                         if (args[0].indexOf(":") === -1) { | ||||
|                             args[0] = ns+":"+args[0]; | ||||
|                         } | ||||
|                         return RED._.apply(null,args); | ||||
|                     } | ||||
| 
 | ||||
|                     // TODO: too tightly coupled into palette UI
 | ||||
|                 } | ||||
|                 RED.palette.add(nt,def); | ||||
| @@ -117,6 +133,7 @@ RED.nodes = (function() { | ||||
|             }, | ||||
|             removeNodeType: function(nt) { | ||||
|                 if (nt.substring(0,8) != "subflow:") { | ||||
|                     // NON-NLS - internal debug message
 | ||||
|                     throw new Error("this api is subflow only. called with:",nt); | ||||
|                 } | ||||
|                 delete nodeDefinitions[nt]; | ||||
| @@ -128,16 +145,24 @@ RED.nodes = (function() { | ||||
|         }; | ||||
|         return exports; | ||||
|     })(); | ||||
|      | ||||
| 
 | ||||
|     function getID() { | ||||
|         return (1+Math.random()*4294967295).toString(16); | ||||
|     } | ||||
| 
 | ||||
|     function addNode(n) { | ||||
|         if (n.type.indexOf("subflow") !== 0) { | ||||
|             n["_"] = n._def._; | ||||
|         } | ||||
|         if (n._def.category == "config") { | ||||
|             configNodes[n.id] = n; | ||||
|             RED.sidebar.config.refresh(); | ||||
|         } else { | ||||
|             n.ports = []; | ||||
|             if (n.outputs) { | ||||
|                 for (var i=0;i<n.outputs;i++) { | ||||
|                     n.ports.push(i); | ||||
|                 } | ||||
|             } | ||||
|             n.dirty = true; | ||||
|             var updatedConfigNode = false; | ||||
|             for (var d in n._def.defaults) { | ||||
| @@ -156,7 +181,7 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|             } | ||||
|             if (updatedConfigNode) { | ||||
|                 RED.sidebar.config.refresh(); | ||||
|                 // TODO: refresh config tab?
 | ||||
|             } | ||||
|             if (n._def.category == "subflows" && typeof n.i === "undefined") { | ||||
|                 var nextId = 0; | ||||
| @@ -190,11 +215,12 @@ RED.nodes = (function() { | ||||
| 
 | ||||
|     function removeNode(id) { | ||||
|         var removedLinks = []; | ||||
|         var removedNodes = []; | ||||
|         var node; | ||||
|         if (id in configNodes) { | ||||
|             node = configNodes[id]; | ||||
|             delete configNodes[id]; | ||||
|             RED.sidebar.config.refresh(); | ||||
|             RED.workspaces.refresh(); | ||||
|         } else { | ||||
|             node = getNode(id); | ||||
|             if (node) { | ||||
| @@ -211,22 +237,27 @@ RED.nodes = (function() { | ||||
|                                 var configNode = configNodes[node[d]]; | ||||
|                                 if (configNode) { | ||||
|                                     updatedConfigNode = true; | ||||
|                                     var users = configNode.users; | ||||
|                                     users.splice(users.indexOf(node),1); | ||||
|                                     if (configNode._def.exclusive) { | ||||
|                                         removeNode(node[d]); | ||||
|                                         removedNodes.push(configNode); | ||||
|                                     } else { | ||||
|                                         var users = configNode.users; | ||||
|                                         users.splice(users.indexOf(node),1); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (updatedConfigNode) { | ||||
|                     RED.sidebar.config.refresh(); | ||||
|                     RED.workspaces.refresh(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (node._def.onremove) { | ||||
|         if (node && node._def.onremove) { | ||||
|             node._def.onremove.call(n); | ||||
|         } | ||||
|         return removedLinks; | ||||
|         return {links:removedLinks,nodes:removedNodes}; | ||||
|     } | ||||
| 
 | ||||
|     function removeLink(l) { | ||||
| @@ -247,15 +278,24 @@ RED.nodes = (function() { | ||||
|         var removedNodes = []; | ||||
|         var removedLinks = []; | ||||
|         var n; | ||||
|         var node; | ||||
|         for (n=0;n<nodes.length;n++) { | ||||
|             var node = nodes[n]; | ||||
|             node = nodes[n]; | ||||
|             if (node.z == id) { | ||||
|                 removedNodes.push(node); | ||||
|             } | ||||
|         } | ||||
|         for(n in configNodes) { | ||||
|             if (configNodes.hasOwnProperty(n)) { | ||||
|                 node = configNodes[n]; | ||||
|                 if (node.z == id) { | ||||
|                     removedNodes.push(node); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         for (n=0;n<removedNodes.length;n++) { | ||||
|             var rmlinks = removeNode(removedNodes[n].id); | ||||
|             removedLinks = removedLinks.concat(rmlinks); | ||||
|             var result = removeNode(removedNodes[n].id); | ||||
|             removedLinks = removedLinks.concat(result.links); | ||||
|         } | ||||
|         return {nodes:removedNodes,links:removedLinks}; | ||||
|     } | ||||
| @@ -265,7 +305,7 @@ RED.nodes = (function() { | ||||
|             var subflowNames = Object.keys(subflows).map(function(sfid) { | ||||
|                 return subflows[sfid].name; | ||||
|             }); | ||||
|              | ||||
| 
 | ||||
|             subflowNames.sort(); | ||||
|             var copyNumber = 1; | ||||
|             var subflowName = sf.name; | ||||
| @@ -277,10 +317,11 @@ RED.nodes = (function() { | ||||
|             }); | ||||
|             sf.name = subflowName; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         subflows[sf.id] = sf; | ||||
|         RED.nodes.registerType("subflow:"+sf.id, { | ||||
|             defaults:{name:{value:""}}, | ||||
|             info: sf.info, | ||||
|             icon:"subflow.png", | ||||
|             category: "subflows", | ||||
|             inputs: sf.in.length, | ||||
| @@ -288,10 +329,13 @@ RED.nodes = (function() { | ||||
|             color: "#da9", | ||||
|             label: function() { return this.name||RED.nodes.subflow(sf.id).name }, | ||||
|             labelStyle: function() { return this.name?"node_label_italic":""; }, | ||||
|             paletteLabel: function() { return RED.nodes.subflow(sf.id).name } | ||||
|             paletteLabel: function() { return RED.nodes.subflow(sf.id).name }, | ||||
|             set:{ | ||||
|                 module: "node-red" | ||||
|             } | ||||
|         }); | ||||
|          | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
|     function getSubflow(id) { | ||||
|         return subflows[id]; | ||||
| @@ -300,7 +344,7 @@ RED.nodes = (function() { | ||||
|         delete subflows[sf.id]; | ||||
|         registry.removeNodeType("subflow:"+sf.id); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function subflowContains(sfid,nodeid) { | ||||
|         for (var i=0;i<nodes.length;i++) { | ||||
|             var node = nodes[i]; | ||||
| @@ -320,7 +364,7 @@ RED.nodes = (function() { | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function getAllFlowNodes(node) { | ||||
|         var visited = {}; | ||||
|         visited[node.id] = true; | ||||
| @@ -353,6 +397,7 @@ RED.nodes = (function() { | ||||
|         var node = {}; | ||||
|         node.id = n.id; | ||||
|         node.type = n.type; | ||||
|         node.z = n.z; | ||||
|         if (node.type == "unknown") { | ||||
|             for (var p in n._orig) { | ||||
|                 if (n._orig.hasOwnProperty(p)) { | ||||
| @@ -388,7 +433,6 @@ RED.nodes = (function() { | ||||
|         if (n._def.category != "config") { | ||||
|             node.x = n.x; | ||||
|             node.y = n.y; | ||||
|             node.z = n.z; | ||||
|             node.wires = []; | ||||
|             for(var i=0;i<n.outputs;i++) { | ||||
|                 node.wires.push([]); | ||||
| @@ -409,9 +453,10 @@ RED.nodes = (function() { | ||||
|         node.id = n.id; | ||||
|         node.type = n.type; | ||||
|         node.name = n.name; | ||||
|         node.info = n.info; | ||||
|         node.in = []; | ||||
|         node.out = []; | ||||
|          | ||||
| 
 | ||||
|         n.in.forEach(function(p) { | ||||
|             var nIn = {x:p.x,y:p.y,wires:[]}; | ||||
|             var wires = links.filter(function(d) { return d.source === p }); | ||||
| @@ -435,8 +480,8 @@ RED.nodes = (function() { | ||||
|             } | ||||
|             node.out.push(nOut); | ||||
|         }); | ||||
|              | ||||
|                  | ||||
| 
 | ||||
| 
 | ||||
|         return node; | ||||
|     } | ||||
|     /** | ||||
| @@ -516,6 +561,35 @@ RED.nodes = (function() { | ||||
|         return nns; | ||||
|     } | ||||
| 
 | ||||
|     function compareNodes(nodeA,nodeB,idMustMatch) { | ||||
|         if (idMustMatch && nodeA.id != nodeB.id) { | ||||
|             return false; | ||||
|         } | ||||
|         if (nodeA.type != nodeB.type) { | ||||
|             return false; | ||||
|         } | ||||
|         var def = nodeA._def; | ||||
|         for (var d in def.defaults) { | ||||
|             if (def.defaults.hasOwnProperty(d)) { | ||||
|                 var vA = nodeA[d]; | ||||
|                 var vB = nodeB[d]; | ||||
|                 if (typeof vA !== typeof vB) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if (vA === null || typeof vA === "string" || typeof vA === "number") { | ||||
|                     if (vA !== vB) { | ||||
|                         return false; | ||||
|                     } | ||||
|                 } else { | ||||
|                     if (JSON.stringify(vA) !== JSON.stringify(vB)) { | ||||
|                         return false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     function importNodes(newNodesObj,createNewIds) { | ||||
|         var i; | ||||
|         var n; | ||||
| @@ -527,7 +601,7 @@ RED.nodes = (function() { | ||||
|             try { | ||||
|                 newNodes = JSON.parse(newNodesObj); | ||||
|             } catch(err) { | ||||
|                 var e = new Error("Invalid flow: "+err.message); | ||||
|                 var e = new Error(RED._("clipboard.invalidFlow",{message:err.message})); | ||||
|                 e.code = "NODE_RED"; | ||||
|                 throw e; | ||||
|             } | ||||
| @@ -542,21 +616,19 @@ RED.nodes = (function() { | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
|             // TODO: remove workspace in next release+1
 | ||||
|             if (n.type != "workspace" &&  | ||||
|                 n.type != "tab" &&  | ||||
|             if (n.type != "workspace" && | ||||
|                 n.type != "tab" && | ||||
|                 n.type != "subflow" && | ||||
|                 !registry.getNodeType(n.type) && | ||||
|                 n.type.substring(0,8) != "subflow:" && | ||||
|                 unknownTypes.indexOf(n.type)==-1) { | ||||
|              | ||||
|                     unknownTypes.push(n.type); | ||||
|             } | ||||
|         } | ||||
|         if (unknownTypes.length > 0) { | ||||
|             var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>"; | ||||
|             var type = "type"+(unknownTypes.length > 1?"s":""); | ||||
|             RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000); | ||||
|             //"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
 | ||||
|             RED.notify("<strong>"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"</strong>"+typeList,"error",false,10000); | ||||
|         } | ||||
| 
 | ||||
|         var activeWorkspace = RED.workspaces.active(); | ||||
| @@ -568,25 +640,27 @@ RED.nodes = (function() { | ||||
|                     var subflowId = m[1]; | ||||
|                     var err; | ||||
|                     if (subflowId === activeSubflow.id) { | ||||
|                         err = new Error("Cannot add subflow to itself"); | ||||
|                         err = new Error(RED._("notification.errors.cannotAddSubflowToItself")); | ||||
|                     } | ||||
|                     if (subflowContains(m[1],activeSubflow.id)) { | ||||
|                         err = new Error("Cannot add subflow - circular reference detected"); | ||||
|                         err = new Error(RED._("notification.errors.cannotAddCircularReference")); | ||||
|                     } | ||||
|                     if (err) { | ||||
|                         // TODO: standardise error codes
 | ||||
|                         err.code = "NODE_RED"; | ||||
|                         throw err; | ||||
|                     } | ||||
|                      | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         var new_workspaces = []; | ||||
|         var workspace_map = {}; | ||||
|         var new_subflows = []; | ||||
|         var subflow_map = {}; | ||||
|         var node_map = {}; | ||||
|         var new_nodes = []; | ||||
|         var new_links = []; | ||||
|         var nid; | ||||
|         var def; | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
| @@ -630,34 +704,69 @@ RED.nodes = (function() { | ||||
|                 }); | ||||
|                 new_subflows.push(n); | ||||
|                 addSubflow(n,createNewIds); | ||||
|             } else { | ||||
|                 def = registry.getNodeType(n.type); | ||||
|                 if (def && def.category == "config") { | ||||
|                     if (!RED.nodes.node(n.id)) { | ||||
|                         var configNode = {id:n.id,type:n.type,users:[]}; | ||||
|                         for (var d in def.defaults) { | ||||
|                             if (def.defaults.hasOwnProperty(d)) { | ||||
|                                 configNode[d] = n[d]; | ||||
|                             } | ||||
|                         } | ||||
|                         configNode.label = def.label; | ||||
|                         configNode._def = def; | ||||
|                         RED.nodes.add(configNode); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (defaultWorkspace == null) { | ||||
|             defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" }; | ||||
|             defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})}; | ||||
|             addWorkspace(defaultWorkspace); | ||||
|             RED.workspaces.add(defaultWorkspace); | ||||
|             new_workspaces.push(defaultWorkspace); | ||||
|             activeWorkspace = RED.workspaces.active(); | ||||
|         } | ||||
| 
 | ||||
|         var node_map = {}; | ||||
|         var new_nodes = []; | ||||
|         var new_links = []; | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
|             def = registry.getNodeType(n.type); | ||||
|             if (def && def.category == "config") { | ||||
|                 var existingConfigNode = null; | ||||
|                 if (createNewIds) { | ||||
|                     if (n.z) { | ||||
|                         if (subflow_map[n.z]) { | ||||
|                             n.z = subflow_map[n.z].id; | ||||
|                         } else { | ||||
|                             n.z = workspace_map[n.z]; | ||||
|                             if (!workspaces[n.z]) { | ||||
|                                 n.z = activeWorkspace; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     existingConfigNode = RED.nodes.node(n.id); | ||||
|                     if (existingConfigNode) { | ||||
|                         if (n.z && existingConfigNode.z !== n.z) { | ||||
|                             existingConfigNode = null; | ||||
|                             // Check the config nodes on n.z
 | ||||
|                             for (var cn in configNodes) { | ||||
|                                 if (configNodes.hasOwnProperty(cn)) { | ||||
|                                     if (configNodes[cn].z === n.z && compareNodes(configNodes[cn],n,false)) { | ||||
|                                         existingConfigNode = configNodes[cn]; | ||||
|                                         node_map[n.id] = configNodes[cn]; | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|                 if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) {
 | ||||
|                     var configNode = {id:n.id, z:n.z, type:n.type, users:[]}; | ||||
|                     for (var d in def.defaults) { | ||||
|                         if (def.defaults.hasOwnProperty(d)) { | ||||
|                             configNode[d] = n[d]; | ||||
|                         } | ||||
|                     } | ||||
|                     configNode.label = def.label; | ||||
|                     configNode._def = def; | ||||
|                     if (createNewIds) { | ||||
|                         configNode.id = getID(); | ||||
|                     } | ||||
|                     node_map[n.id] = configNode; | ||||
|                     new_nodes.push(configNode); | ||||
|                     RED.nodes.add(configNode); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
| @@ -704,11 +813,13 @@ RED.nodes = (function() { | ||||
|                                     defaults: {}, | ||||
|                                     label: "unknown: "+n.type, | ||||
|                                     labelStyle: "node_label_italic", | ||||
|                                     outputs: n.outputs||n.wires.length | ||||
|                                     outputs: n.outputs||n.wires.length, | ||||
|                                     set: registry.getNodeSet("node-red/unknown") | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 node._def = { | ||||
|                                     category:"config" | ||||
|                                     category:"config", | ||||
|                                     set: registry.getNodeSet("node-red/unknown") | ||||
|                                 }; | ||||
|                                 node.users = []; | ||||
|                             } | ||||
| @@ -727,7 +838,15 @@ RED.nodes = (function() { | ||||
|                             node.outputs = n.outputs||node._def.outputs; | ||||
|                             for (var d2 in node._def.defaults) { | ||||
|                                 if (node._def.defaults.hasOwnProperty(d2)) { | ||||
|                                     node[d2] = n[d2]; | ||||
|                                     if (node._def.defaults[d2].type) { | ||||
|                                         if (node_map[n[d2]]) { | ||||
|                                             node[d2] = node_map[n[d2]].id; | ||||
|                                         } else { | ||||
|                                             node[d2] = n[d2]; | ||||
|                                         } | ||||
|                                     } else { | ||||
|                                         node[d2] = n[d2]; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| @@ -743,17 +862,19 @@ RED.nodes = (function() { | ||||
|         } | ||||
|         for (i=0;i<new_nodes.length;i++) { | ||||
|             n = new_nodes[i]; | ||||
|             for (var w1=0;w1<n.wires.length;w1++) { | ||||
|                 var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]]; | ||||
|                 for (var w2=0;w2<wires.length;w2++) { | ||||
|                     if (wires[w2] in node_map) { | ||||
|                         var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]}; | ||||
|                         addLink(link); | ||||
|                         new_links.push(link); | ||||
|             if (n.wires) { | ||||
|                 for (var w1=0;w1<n.wires.length;w1++) { | ||||
|                     var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]]; | ||||
|                     for (var w2=0;w2<wires.length;w2++) { | ||||
|                         if (wires[w2] in node_map) { | ||||
|                             var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]}; | ||||
|                             addLink(link); | ||||
|                             new_links.push(link); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 delete n.wires; | ||||
|             } | ||||
|             delete n.wires; | ||||
|         } | ||||
|         for (i=0;i<new_subflows.length;i++) { | ||||
|             n = new_subflows[i]; | ||||
| @@ -779,20 +900,21 @@ RED.nodes = (function() { | ||||
|                 delete output.wires; | ||||
|             }); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         RED.workspaces.refresh(); | ||||
|         return [new_nodes,new_links,new_workspaces,new_subflows]; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     // TODO: supports filter.z|type
 | ||||
|     function filterNodes(filter) { | ||||
|         var result = []; | ||||
|          | ||||
| 
 | ||||
|         for (var n=0;n<nodes.length;n++) { | ||||
|             var node = nodes[n]; | ||||
|             if (filter.z && node.z !== filter.z) { | ||||
|             if (filter.hasOwnProperty("z") && node.z !== filter.z) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (filter.type && node.type !== filter.type) { | ||||
|             if (filter.hasOwnProperty("type") && node.type !== filter.type) { | ||||
|                 continue; | ||||
|             } | ||||
|             result.push(node); | ||||
| @@ -801,84 +923,62 @@ RED.nodes = (function() { | ||||
|     } | ||||
|     function filterLinks(filter) { | ||||
|         var result = []; | ||||
|          | ||||
| 
 | ||||
|         for (var n=0;n<links.length;n++) { | ||||
|             var link = links[n]; | ||||
|             if (filter.source) { | ||||
|                 if (filter.source.id && link.source.id !== filter.source.id) { | ||||
|                 if (filter.source.hasOwnProperty("id") && link.source.id !== filter.source.id) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (filter.source.z && link.source.z !== filter.source.z) { | ||||
|                 if (filter.source.hasOwnProperty("z") && link.source.z !== filter.source.z) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             if (filter.target) { | ||||
|                 if (filter.target.id && link.target.id !== filter.target.id) { | ||||
|                 if (filter.target.hasOwnProperty("id") && link.target.id !== filter.target.id) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (filter.target.z && link.target.z !== filter.target.z) { | ||||
|                 if (filter.target.hasOwnProperty("z") && link.target.z !== filter.target.z) { | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             if (filter.sourcePort && link.sourcePort !== filter.sourcePort) { | ||||
|             if (filter.hasOwnProperty("sourcePort") && link.sourcePort !== filter.sourcePort) { | ||||
|                 continue; | ||||
|             } | ||||
|             result.push(link); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|      | ||||
|     // TODO: DRY
 | ||||
|     var eventHandler = (function() { | ||||
|         var handlers = {}; | ||||
|          | ||||
|         return { | ||||
|             on: function(evt,func) { | ||||
|                 handlers[evt] = handlers[evt]||[]; | ||||
|                 handlers[evt].push(func); | ||||
|             }, | ||||
|             emit: function(evt,arg) { | ||||
|                 if (handlers[evt]) { | ||||
|                     for (var i=0;i<handlers[evt].length;i++) { | ||||
|                         handlers[evt][i](arg); | ||||
|                     } | ||||
|                      | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     })(); | ||||
|      | ||||
| 
 | ||||
|     return { | ||||
|         on: eventHandler.on, | ||||
|          | ||||
|         registry:registry, | ||||
|         setNodeList: registry.setNodeList, | ||||
|          | ||||
| 
 | ||||
|         getNodeSet: registry.getNodeSet, | ||||
|         addNodeSet: registry.addNodeSet, | ||||
|         removeNodeSet: registry.removeNodeSet, | ||||
|         enableNodeSet: registry.enableNodeSet, | ||||
|         disableNodeSet: registry.disableNodeSet, | ||||
|          | ||||
| 
 | ||||
|         registerType: registry.registerNodeType, | ||||
|         getType: registry.getNodeType, | ||||
|         convertNode: convertNode, | ||||
|          | ||||
| 
 | ||||
|         add: addNode, | ||||
|         remove: removeNode, | ||||
|          | ||||
| 
 | ||||
|         addLink: addLink, | ||||
|         removeLink: removeLink, | ||||
|          | ||||
| 
 | ||||
|         addWorkspace: addWorkspace, | ||||
|         removeWorkspace: removeWorkspace, | ||||
|         workspace: getWorkspace, | ||||
|          | ||||
| 
 | ||||
|         addSubflow: addSubflow, | ||||
|         removeSubflow: removeSubflow, | ||||
|         subflow: getSubflow, | ||||
|         subflowContains: subflowContains, | ||||
|          | ||||
| 
 | ||||
|         eachNode: function(cb) { | ||||
|             for (var n=0;n<nodes.length;n++) { | ||||
|                 cb(nodes[n]); | ||||
| @@ -903,14 +1003,21 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|          | ||||
|         eachWorkspace: function(cb) { | ||||
|             for (var id in workspaces) { | ||||
|                 if (workspaces.hasOwnProperty(id)) { | ||||
|                     cb(workspaces[id]); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
| 
 | ||||
|         node: getNode, | ||||
|          | ||||
| 
 | ||||
|         filterNodes: filterNodes, | ||||
|         filterLinks: filterLinks, | ||||
|          | ||||
| 
 | ||||
|         import: importNodes, | ||||
|          | ||||
| 
 | ||||
|         getAllFlowNodes: getAllFlowNodes, | ||||
|         createExportableNodeSet: createExportableNodeSet, | ||||
|         createCompleteNodeSet: createCompleteNodeSet, | ||||
| @@ -119,13 +119,30 @@ RED.settings = (function () { | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     function theme(property,defaultValue) { | ||||
|         if (!RED.settings.editorTheme) { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         var parts = property.split("."); | ||||
|         var v = RED.settings.editorTheme; | ||||
|         try { | ||||
|             for (var i=0;i<parts.length;i++) { | ||||
|                 v = v[parts[i]]; | ||||
|             } | ||||
|             return v; | ||||
|         } catch(err) { | ||||
|             return defaultValue; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|         init: init, | ||||
|         load: load, | ||||
|         set: set, | ||||
|         get: get, | ||||
|         remove: remove | ||||
|         remove: remove, | ||||
|          | ||||
|         theme: theme | ||||
|     } | ||||
| }) | ||||
| (); | ||||
							
								
								
									
										190
									
								
								editor/js/ui/clipboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,190 @@ | ||||
| /** | ||||
|  * Copyright 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
|  | ||||
| RED.clipboard = (function() { | ||||
|  | ||||
|     var dialog; | ||||
|     var dialogContainer; | ||||
|     var exportNodesDialog; | ||||
|     var importNodesDialog; | ||||
|  | ||||
|     function setupDialogs(){ | ||||
|         dialog = $('<div id="clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>') | ||||
|             .appendTo("body") | ||||
|             .dialog({ | ||||
|                 modal: true, | ||||
|                 autoOpen: false, | ||||
|                 width: 500, | ||||
|                 resizable: false, | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "clipboard-dialog-ok", | ||||
|                         text: RED._("common.label.ok"), | ||||
|                         click: function() { | ||||
|                             RED.view.importNodes($("#clipboard-import").val()); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "clipboard-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "clipboard-dialog-close", | ||||
|                         text: RED._("common.label.close"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 open: function(e) { | ||||
|                     $(this).parent().find(".ui-dialog-titlebar-close").hide(); | ||||
|                     RED.keyboard.disable(); | ||||
|                 }, | ||||
|                 close: function(e) { | ||||
|                     RED.keyboard.enable(); | ||||
|                 } | ||||
|         }); | ||||
|  | ||||
|         dialogContainer = dialog.children(".dialog-form"); | ||||
|  | ||||
|         exportNodesDialog = '<div class="form-row">'+ | ||||
|             '<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> '+RED._("clipboard.nodes")+'</label>'+ | ||||
|             '<textarea readonly style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+ | ||||
|             '</div>'+ | ||||
|             '<div class="form-tips">'+ | ||||
|             RED._("clipboard.selectNodes")+ | ||||
|             '</div>'; | ||||
|  | ||||
|         importNodesDialog = '<div class="form-row">'+ | ||||
|             '<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="'+ | ||||
|             RED._("clipboard.pasteNodes")+ | ||||
|             '"></textarea>'+ | ||||
|             '</div>'; | ||||
|     } | ||||
|  | ||||
|     function validateImport() { | ||||
|         var importInput = $("#clipboard-import"); | ||||
|         var v = importInput.val(); | ||||
|         try { | ||||
|             JSON.parse(v); | ||||
|             importInput.removeClass("input-error"); | ||||
|             $("#clipboard-dialog-ok").button("enable"); | ||||
|         } catch(err) { | ||||
|             if (v !== "") { | ||||
|                 importInput.addClass("input-error"); | ||||
|             } | ||||
|             $("#clipboard-dialog-ok").button("disable"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function importNodes() { | ||||
|         dialogContainer.empty(); | ||||
|         dialogContainer.append($(importNodesDialog)); | ||||
|         $("#clipboard-dialog-ok").show(); | ||||
|         $("#clipboard-dialog-cancel").show(); | ||||
|         $("#clipboard-dialog-close").hide(); | ||||
|         $("#clipboard-dialog-ok").button("disable"); | ||||
|         $("#clipboard-import").keyup(validateImport); | ||||
|         $("#clipboard-import").on('paste',function() { setTimeout(validateImport,10)}); | ||||
|  | ||||
|         dialog.dialog("option","title",RED._("clipboard.importNodes")).dialog("open"); | ||||
|     } | ||||
|  | ||||
|     function exportNodes() { | ||||
|         dialogContainer.empty(); | ||||
|         dialogContainer.append($(exportNodesDialog)); | ||||
|         $("#clipboard-dialog-ok").hide(); | ||||
|         $("#clipboard-dialog-cancel").hide(); | ||||
|         $("#clipboard-dialog-close").show(); | ||||
|         var selection = RED.view.selection(); | ||||
|         if (selection.nodes) { | ||||
|             var nns = RED.nodes.createExportableNodeSet(selection.nodes); | ||||
|             $("#clipboard-export") | ||||
|                 .val(JSON.stringify(nns)) | ||||
|                 .focus(function() { | ||||
|                     var textarea = $(this); | ||||
|                     textarea.select(); | ||||
|                     textarea.mouseup(function() { | ||||
|                         textarea.unbind("mouseup"); | ||||
|                         return false; | ||||
|                     }) | ||||
|                 }); | ||||
|             dialog.dialog("option","title",RED._("clipboard.exportNodes")).dialog( "open" ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function hideDropTarget() { | ||||
|         $("#dropTarget").hide(); | ||||
|         RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             setupDialogs(); | ||||
|             RED.events.on("view:selection-changed",function(selection) { | ||||
|                 if (!selection.nodes) { | ||||
|                     RED.menu.setDisabled("menu-item-export",true); | ||||
|                     RED.menu.setDisabled("menu-item-export-clipboard",true); | ||||
|                     RED.menu.setDisabled("menu-item-export-library",true); | ||||
|                 } else { | ||||
|                     RED.menu.setDisabled("menu-item-export",false); | ||||
|                     RED.menu.setDisabled("menu-item-export-clipboard",false); | ||||
|                     RED.menu.setDisabled("menu-item-export-library",false); | ||||
|                 } | ||||
|             }); | ||||
|             RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();}); | ||||
|             RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();}); | ||||
|  | ||||
|  | ||||
|  | ||||
|             $('#chart').on("dragenter",function(event) { | ||||
|                 if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                     $("#dropTarget").css({display:'table'}); | ||||
|                     RED.keyboard.add(/* ESCAPE */ 27,hideDropTarget); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             $('#dropTarget').on("dragover",function(event) { | ||||
|                 if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                     event.preventDefault(); | ||||
|                 } | ||||
|             }) | ||||
|             .on("dragleave",function(event) { | ||||
|                 hideDropTarget(); | ||||
|             }) | ||||
|             .on("drop",function(event) { | ||||
|                 var data = event.originalEvent.dataTransfer.getData("text/plain"); | ||||
|                 hideDropTarget(); | ||||
|                 RED.view.importNodes(data); | ||||
|                 event.preventDefault(); | ||||
|             }); | ||||
|  | ||||
|  | ||||
|         }, | ||||
|         import: importNodes, | ||||
|         export: exportNodes | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| })(); | ||||
							
								
								
									
										280
									
								
								editor/js/ui/deploy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,280 @@ | ||||
| /** | ||||
|  * 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.deploy = (function() { | ||||
|  | ||||
|     var deploymentTypes = { | ||||
|         "full":{img:"red/images/deploy-full-o.png"}, | ||||
|         "nodes":{img:"red/images/deploy-nodes-o.png"}, | ||||
|         "flows":{img:"red/images/deploy-flows-o.png"} | ||||
|     } | ||||
|  | ||||
|     var ignoreDeployWarnings = { | ||||
|         unknown: false, | ||||
|         unusedConfig: false, | ||||
|         invalid: false | ||||
|     } | ||||
|  | ||||
|     var deploymentType = "full"; | ||||
|  | ||||
|     function changeDeploymentType(type) { | ||||
|         deploymentType = type; | ||||
|         $("#btn-deploy img").attr("src",deploymentTypes[type].img); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * options: | ||||
|      *   type: "default" - Button with drop-down options - no further customisation available | ||||
|      *   type: "simple"  - Button without dropdown. Customisations: | ||||
|      *      label: the text to display - default: "Deploy" | ||||
|      *      icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.png" | ||||
|      */ | ||||
|     function init(options) { | ||||
|         options = options || {}; | ||||
|         var type = options.type || "default"; | ||||
|  | ||||
|         if (type == "default") { | ||||
|             $('<li><span class="deploy-button-group button-group">'+ | ||||
|               '<a id="btn-deploy" class="deploy-button disabled" href="#"><img id="btn-deploy-icon" src="red/images/deploy-full-o.png"> <span>'+RED._("deploy.deploy")+'</span></a>'+ | ||||
|               '<a id="btn-deploy-options" data-toggle="dropdown" class="deploy-button" href="#"><i class="fa fa-caret-down"></i></a>'+ | ||||
|               '</span></li>').prependTo(".header-toolbar"); | ||||
|               RED.menu.init({id:"btn-deploy-options", | ||||
|                   options: [ | ||||
|                       {id:"deploymenu-item-full",toggle:"deploy-type",icon:"red/images/deploy-full.png",label:RED._("deploy.full"),sublabel:RED._("deploy.fullDesc"),selected: true, onselect:function(s) { if(s){changeDeploymentType("full")}}}, | ||||
|                       {id:"deploymenu-item-flow",toggle:"deploy-type",icon:"red/images/deploy-flows.png",label:RED._("deploy.modifiedFlows"),sublabel:RED._("deploy.modifiedFlowsDesc"), onselect:function(s) {if(s){changeDeploymentType("flows")}}}, | ||||
|                       {id:"deploymenu-item-node",toggle:"deploy-type",icon:"red/images/deploy-nodes.png",label:RED._("deploy.modifiedNodes"),sublabel:RED._("deploy.modifiedNodesDesc"),onselect:function(s) { if(s){changeDeploymentType("nodes")}}} | ||||
|                   ] | ||||
|               }); | ||||
|         } else if (type == "simple") { | ||||
|             var label = options.label || RED._("deploy.deploy"); | ||||
|             var icon = 'red/images/deploy-full-o.png'; | ||||
|             if (options.hasOwnProperty('icon')) { | ||||
|                 icon = options.icon; | ||||
|             } | ||||
|  | ||||
|             $('<li><span class="deploy-button-group button-group">'+ | ||||
|               '<a id="btn-deploy" class="deploy-button disabled" href="#">'+ | ||||
|               (icon?'<img id="btn-deploy-icon" src="'+icon+'"> ':'')+ | ||||
|               '<span>'+label+'</span></a>'+ | ||||
|               '</span></li>').prependTo(".header-toolbar"); | ||||
|         } | ||||
|  | ||||
|         $('#btn-deploy').click(function() { save(); }); | ||||
|  | ||||
|         $( "#node-dialog-confirm-deploy" ).dialog({ | ||||
|                 title: "Confirm deploy", | ||||
|                 modal: true, | ||||
|                 autoOpen: false, | ||||
|                 width: 550, | ||||
|                 height: "auto", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         text: RED._("deploy.confirm.button.confirm"), | ||||
|                         click: function() { | ||||
|  | ||||
|                             var ignoreChecked = $( "#node-dialog-confirm-deploy-hide" ).prop("checked"); | ||||
|                             if (ignoreChecked) { | ||||
|                                 ignoreDeployWarnings[$( "#node-dialog-confirm-deploy-type" ).val()] = true; | ||||
|                             } | ||||
|                             save(true); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         text: RED._("deploy.confirm.button.cancel"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 create: function() { | ||||
|                     $("#node-dialog-confirm-deploy").parent().find("div.ui-dialog-buttonpane") | ||||
|                         .prepend('<div style="height:0; vertical-align: middle; display:inline-block; margin-top: 13px; float:left;">'+ | ||||
|                                    '<input style="vertical-align:top;" type="checkbox" id="node-dialog-confirm-deploy-hide">'+ | ||||
|                                    '<label style="display:inline;" for="node-dialog-confirm-deploy-hide"> do not warn about this again</label>'+ | ||||
|                                    '<input type="hidden" id="node-dialog-confirm-deploy-type">'+ | ||||
|                                    '</div>'); | ||||
|                 } | ||||
|         }); | ||||
|  | ||||
|         RED.events.on('nodes:change',function(state) { | ||||
|             if (state.dirty) { | ||||
|                 window.onbeforeunload = function() { | ||||
|                     return RED._("deploy.confirm.undeployedChanges"); | ||||
|                 } | ||||
|                 $("#btn-deploy").removeClass("disabled"); | ||||
|             } else { | ||||
|                 window.onbeforeunload = null; | ||||
|                 $("#btn-deploy").addClass("disabled"); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function getNodeInfo(node) { | ||||
|         var tabLabel = ""; | ||||
|         if (node.z) { | ||||
|             var tab = RED.nodes.workspace(node.z); | ||||
|             if (!tab) { | ||||
|                 tab = RED.nodes.subflow(node.z); | ||||
|                 tabLabel = tab.name; | ||||
|             } else { | ||||
|                 tabLabel = tab.label; | ||||
|             } | ||||
|         } | ||||
|         var label = ""; | ||||
|         if (typeof node._def.label == "function") { | ||||
|             label = node._def.label.call(node); | ||||
|         } else { | ||||
|             label = node._def.label; | ||||
|         } | ||||
|         label = label || node.id; | ||||
|         return {tab:tabLabel,type:node.type,label:label}; | ||||
|     } | ||||
|     function sortNodeInfo(A,B) { | ||||
|         if (A.tab < B.tab) { return -1;} | ||||
|         if (A.tab > B.tab) { return 1;} | ||||
|         if (A.type < B.type) { return -1;} | ||||
|         if (A.type > B.type) { return 1;} | ||||
|         if (A.name < B.name) { return -1;} | ||||
|         if (A.name > B.name) { return 1;} | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     function save(force) { | ||||
|         if (RED.nodes.dirty()) { | ||||
|             //$("#debug-tab-clear").click();  // uncomment this to auto clear debug on deploy | ||||
|  | ||||
|             if (!force) { | ||||
|                 var hasUnknown = false; | ||||
|                 var hasInvalid = false; | ||||
|                 var hasUnusedConfig = false; | ||||
|  | ||||
|                 var unknownNodes = []; | ||||
|                 var invalidNodes = []; | ||||
|  | ||||
|                 RED.nodes.eachNode(function(node) { | ||||
|                     hasInvalid = hasInvalid || !node.valid; | ||||
|                     if (!node.valid) { | ||||
|                         invalidNodes.push(getNodeInfo(node)); | ||||
|                     } | ||||
|                     if (node.type === "unknown") { | ||||
|                         if (unknownNodes.indexOf(node.name) == -1) { | ||||
|                             unknownNodes.push(node.name); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|                 hasUnknown = unknownNodes.length > 0; | ||||
|  | ||||
|                 var unusedConfigNodes = []; | ||||
|                 RED.nodes.eachConfig(function(node) { | ||||
|                     if (node.users.length === 0) { | ||||
|                         unusedConfigNodes.push(getNodeInfo(node)); | ||||
|                         hasUnusedConfig = true; | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 $( "#node-dialog-confirm-deploy-config" ).hide(); | ||||
|                 $( "#node-dialog-confirm-deploy-unknown" ).hide(); | ||||
|                 $( "#node-dialog-confirm-deploy-unused" ).hide(); | ||||
|  | ||||
|                 var showWarning = false; | ||||
|  | ||||
|                 if (hasUnknown && !ignoreDeployWarnings.unknown) { | ||||
|                     showWarning = true; | ||||
|                     $( "#node-dialog-confirm-deploy-type" ).val("unknown"); | ||||
|                     $( "#node-dialog-confirm-deploy-unknown" ).show(); | ||||
|                     $( "#node-dialog-confirm-deploy-unknown-list" ) | ||||
|                         .html("<li>"+unknownNodes.join("</li><li>")+"</li>"); | ||||
|                 } else if (hasInvalid && !ignoreDeployWarnings.invalid) { | ||||
|                     showWarning = true; | ||||
|                     $( "#node-dialog-confirm-deploy-type" ).val("invalid"); | ||||
|                     $( "#node-dialog-confirm-deploy-config" ).show(); | ||||
|                     invalidNodes.sort(sortNodeInfo); | ||||
|                     $( "#node-dialog-confirm-deploy-invalid-list" ) | ||||
|                         .html("<li>"+invalidNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>"); | ||||
|  | ||||
|                 } else if (hasUnusedConfig && !ignoreDeployWarnings.unusedConfig) { | ||||
|                     showWarning = true; | ||||
|                     $( "#node-dialog-confirm-deploy-type" ).val("unusedConfig"); | ||||
|                     $( "#node-dialog-confirm-deploy-unused" ).show(); | ||||
|  | ||||
|                     unusedConfigNodes.sort(sortNodeInfo); | ||||
|                     $( "#node-dialog-confirm-deploy-unused-list" ) | ||||
|                         .html("<li>"+unusedConfigNodes.map(function(A) { return (A.tab?"["+A.tab+"] ":"")+A.label+" ("+A.type+")"}).join("</li><li>")+"</li>"); | ||||
|                 } | ||||
|                 if (showWarning) { | ||||
|                     $( "#node-dialog-confirm-deploy-hide" ).prop("checked",false); | ||||
|                     $( "#node-dialog-confirm-deploy" ).dialog( "open" ); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|             var nns = RED.nodes.createCompleteNodeSet(); | ||||
|  | ||||
|             $("#btn-deploy-icon").removeClass('fa-download'); | ||||
|             $("#btn-deploy-icon").addClass('spinner'); | ||||
|             RED.nodes.dirty(false); | ||||
|  | ||||
|             $.ajax({ | ||||
|                 url:"flows", | ||||
|                 type: "POST", | ||||
|                 data: JSON.stringify(nns), | ||||
|                 contentType: "application/json; charset=utf-8", | ||||
|                 headers: { | ||||
|                     "Node-RED-Deployment-Type":deploymentType | ||||
|                 } | ||||
|             }).done(function(data,textStatus,xhr) { | ||||
|                 RED.notify(RED._("deploy.successfulDeploy"),"success"); | ||||
|                 RED.nodes.eachNode(function(node) { | ||||
|                     if (node.changed) { | ||||
|                         node.dirty = true; | ||||
|                         node.changed = false; | ||||
|                     } | ||||
|                     if(node.credentials) { | ||||
|                         delete node.credentials; | ||||
|                     } | ||||
|                 }); | ||||
|                 RED.nodes.eachConfig(function (confNode) { | ||||
|                     if (confNode.credentials) { | ||||
|                         delete confNode.credentials; | ||||
|                     } | ||||
|                 }); | ||||
|                 // Once deployed, cannot undo back to a clean state | ||||
|                 RED.history.markAllDirty(); | ||||
|                 RED.view.redraw(); | ||||
|                 RED.events.emit("deploy"); | ||||
|             }).fail(function(xhr,textStatus,err) { | ||||
|                 RED.nodes.dirty(true); | ||||
|                 if (xhr.responseText) { | ||||
|                     RED.notify(RED._("notification.error",{message:xhr.responseText}),"error"); | ||||
|                 } else { | ||||
|                     RED.notify(RED._("notification.error",{message:RED._("deploy.errors.noResponse")}),"error"); | ||||
|                 } | ||||
|             }).always(function() { | ||||
|                 $("#btn-deploy-icon").removeClass('spinner'); | ||||
|                 $("#btn-deploy-icon").addClass('fa-download'); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										1196
									
								
								editor/js/ui/editor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										121
									
								
								editor/js/ui/keyboard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,121 @@ | ||||
| /** | ||||
|  * Copyright 2013 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| RED.keyboard = (function() { | ||||
|  | ||||
|     var active = true; | ||||
|     var handlers = {}; | ||||
|  | ||||
|     d3.select(window).on("keydown",function() { | ||||
|         if (!active) { return; } | ||||
|         var handler = handlers[d3.event.keyCode]; | ||||
|         if (handler && handler.ondown) { | ||||
|             if (!handler.modifiers || | ||||
|                 ((!handler.modifiers.shift || d3.event.shiftKey) && | ||||
|                  (!handler.modifiers.ctrl  || d3.event.ctrlKey || d3.event.metaKey) && | ||||
|                  (!handler.modifiers.alt   || d3.event.altKey) )) { | ||||
|                 handler.ondown(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     d3.select(window).on("keyup",function() { | ||||
|         if (!active) { return; } | ||||
|         var handler = handlers[d3.event.keyCode]; | ||||
|         if (handler && handler.onup) { | ||||
|             if (!handler.modifiers || | ||||
|                 ((!handler.modifiers.shift || d3.event.shiftKey) && | ||||
|                  (!handler.modifiers.ctrl  || d3.event.ctrlKey || d3.event.metaKey) && | ||||
|                  (!handler.modifiers.alt   || d3.event.altKey) )) { | ||||
|                 handler.onup(); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|     function addHandler(key,modifiers,ondown,onup) { | ||||
|         var mod = modifiers; | ||||
|         var cbdown = ondown; | ||||
|         var cbup = onup; | ||||
|         if (typeof modifiers == "function") { | ||||
|             mod = {}; | ||||
|             cbdown = modifiers; | ||||
|             cbup = ondown; | ||||
|         } | ||||
|         handlers[key] = {modifiers:mod, ondown:cbdown, onup:cbup}; | ||||
|     } | ||||
|     function removeHandler(key) { | ||||
|         delete handlers[key]; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     var dialog = null; | ||||
|      | ||||
|     function showKeyboardHelp() { | ||||
|         if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) { | ||||
|             return; | ||||
|         } | ||||
|         if (!dialog) { | ||||
|             dialog = $('<div id="keyboard-help-dialog" class="hide">'+ | ||||
|                 '<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+ | ||||
|                     '<table class="keyboard-shortcuts">'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">a</span></td><td>'+RED._("keyboard.selectAll")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.selectAllConnected")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Click</span></td><td>'+RED._("keyboard.addRemoveNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteSelected")+'</td></tr>'+ | ||||
|                         '<tr><td> </td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">i</span></td><td>'+RED._("keyboard.importNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">e</span></td><td>'+RED._("keyboard.exportNode")+'</td></tr>'+ | ||||
|                     '</table>'+ | ||||
|                 '</div>'+ | ||||
|                 '<div style="vertical-align: top;display:inline-block; box-sizing: border-box; width:50%; padding: 10px;">'+ | ||||
|                     '<table class="keyboard-shortcuts">'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Space</span></td><td>'+RED._("keyboard.toggleSidebar")+'</td></tr>'+ | ||||
|                         '<tr><td></td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Delete</span></td><td>'+RED._("keyboard.deleteNode")+'</td></tr>'+ | ||||
|                         '<tr><td></td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">c</span></td><td>'+RED._("keyboard.copyNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">x</span></td><td>'+RED._("keyboard.cutNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">v</span></td><td>'+RED._("keyboard.pasteNode")+'</td></tr>'+ | ||||
|                     '</table>'+ | ||||
|                 '</div>'+ | ||||
|                 '</div>') | ||||
|             .appendTo("body") | ||||
|             .dialog({ | ||||
|                 modal: true, | ||||
|                 autoOpen: false, | ||||
|                 width: "800", | ||||
|                 title:"Keyboard shortcuts", | ||||
|                 resizable: false, | ||||
|                 open: function() { | ||||
|                     RED.keyboard.disable(); | ||||
|                 }, | ||||
|                 close: function() { | ||||
|                     RED.keyboard.enable(); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|          | ||||
|         dialog.dialog("open"); | ||||
|     } | ||||
|      | ||||
|     return { | ||||
|         add: addHandler, | ||||
|         remove: removeHandler, | ||||
|         disable: function(){ active = false;}, | ||||
|         enable: function(){ active = true; }, | ||||
|          | ||||
|         showHelp: showKeyboardHelp | ||||
|     } | ||||
|  | ||||
| })(); | ||||
| @@ -14,8 +14,10 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| RED.library = (function() { | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
|     var exportToLibraryDialog; | ||||
| 
 | ||||
|     function loadFlowLibrary() { | ||||
|         $.getJSON("library/flows",function(data) { | ||||
|             //console.log(data);
 | ||||
| @@ -25,7 +27,7 @@ RED.library = (function() { | ||||
|                 var li; | ||||
|                 var a; | ||||
|                 var ul = document.createElement("ul"); | ||||
|                 ul.id = "btn-import-library-submenu"; | ||||
|                 ul.id = "menu-item-import-library-submenu"; | ||||
|                 ul.className = "dropdown-menu"; | ||||
|                 if (data.d) { | ||||
|                     for (i in data.d) { | ||||
| @@ -63,15 +65,15 @@ RED.library = (function() { | ||||
|             }; | ||||
|             var menu = buildMenu(data,""); | ||||
|             //TODO: need an api in RED.menu for this
 | ||||
|             $("#btn-import-library-submenu").replaceWith(menu); | ||||
|             $("#menu-item-import-library-submenu").replaceWith(menu); | ||||
|         }); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function createUI(options) { | ||||
|         var libraryData = {}; | ||||
|         var selectedLibraryItem = null; | ||||
|         var libraryEditor = null; | ||||
|          | ||||
| 
 | ||||
|         // Orion editor has set/getText
 | ||||
|         // ACE editor has set/getValue
 | ||||
|         // normalise to set/getValue
 | ||||
| @@ -84,14 +86,14 @@ RED.library = (function() { | ||||
|         if (options.editor.getText) { | ||||
|             options.editor.getValue = options.editor.getText; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         function buildFileListItem(item) { | ||||
|             var li = document.createElement("li"); | ||||
|             li.onmouseover = function(e) { $(this).addClass("list-hover"); }; | ||||
|             li.onmouseout = function(e) { $(this).removeClass("list-hover"); }; | ||||
|             return li; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         function buildFileList(root,data) { | ||||
|             var ul = document.createElement("ul"); | ||||
|             var li; | ||||
| @@ -104,7 +106,7 @@ RED.library = (function() { | ||||
|                         var dirName = v; | ||||
|                         return function(e) { | ||||
|                             var bcli = $('<li class="active"><span class="divider">/</span> <a href="#">'+dirName+'</a></li>'); | ||||
|                             $("a",bcli).click(function(e) {  | ||||
|                             $("a",bcli).click(function(e) { | ||||
|                                 $(this).parent().nextAll().remove(); | ||||
|                                 $.getJSON("library/"+options.url+root+dirName,function(data) { | ||||
|                                     $("#node-select-library").children().first().replaceWith(buildFileList(root+dirName+"/",data)); | ||||
| @@ -141,24 +143,24 @@ RED.library = (function() { | ||||
|             } | ||||
|             return ul; | ||||
|         } | ||||
|      | ||||
|         $('#node-input-name').addClass('input-append-left').css("width","65%").after( | ||||
|             '<div class="btn-group" style="margin-left: 0px;">'+ | ||||
|             '<button id="node-input-'+options.type+'-lookup" class="btn input-append-right" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></button>'+ | ||||
| 
 | ||||
|         $('#node-input-name').css("width","60%").after( | ||||
|             '<div class="btn-group" style="margin-left: 5px;">'+ | ||||
|             '<a id="node-input-'+options.type+'-lookup" class="editor-button" data-toggle="dropdown"><i class="fa fa-book"></i> <i class="fa fa-caret-down"></i></a>'+ | ||||
|             '<ul class="dropdown-menu pull-right" role="menu">'+ | ||||
|             '<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">Open Library...</a></li>'+ | ||||
|             '<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">Save to Library...</a></li>'+ | ||||
|             '<li><a id="node-input-'+options.type+'-menu-open-library" tabindex="-1" href="#">'+RED._("library.openLibrary")+'</a></li>'+ | ||||
|             '<li><a id="node-input-'+options.type+'-menu-save-library" tabindex="-1" href="#">'+RED._("library.saveToLibrary")+'</a></li>'+ | ||||
|             '</ul></div>' | ||||
|         ); | ||||
|      | ||||
|          | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         $('#node-input-'+options.type+'-menu-open-library').click(function(e) { | ||||
|             $("#node-select-library").children().remove(); | ||||
|             var bc = $("#node-dialog-library-breadcrumbs"); | ||||
|             bc.children().first().nextAll().remove(); | ||||
|             libraryEditor.setValue('',-1); | ||||
|              | ||||
| 
 | ||||
|             $.getJSON("library/"+options.url,function(data) { | ||||
|                 $("#node-select-library").append(buildFileList("/",data)); | ||||
|                 $("#node-dialog-library-breadcrumbs a").click(function(e) { | ||||
| @@ -168,10 +170,10 @@ RED.library = (function() { | ||||
|                 }); | ||||
|                 $( "#node-dialog-library-lookup" ).dialog( "open" ); | ||||
|             }); | ||||
|              | ||||
| 
 | ||||
|             e.preventDefault(); | ||||
|         }); | ||||
|      | ||||
| 
 | ||||
|         $('#node-input-'+options.type+'-menu-save-library').click(function(e) { | ||||
|             //var found = false;
 | ||||
|             var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,""); | ||||
| @@ -217,7 +219,7 @@ RED.library = (function() { | ||||
|             $( "#node-dialog-library-save" ).dialog( "open" ); | ||||
|             e.preventDefault(); | ||||
|         }); | ||||
|      | ||||
| 
 | ||||
|         libraryEditor = ace.edit('node-select-library-text'); | ||||
|         libraryEditor.setTheme("ace/theme/tomorrow"); | ||||
|         if (options.mode) { | ||||
| @@ -230,16 +232,16 @@ RED.library = (function() { | ||||
|         }); | ||||
|         libraryEditor.renderer.$cursorLayer.element.style.opacity=0; | ||||
|         libraryEditor.$blockScrolling = Infinity; | ||||
|          | ||||
| 
 | ||||
|         $( "#node-dialog-library-lookup" ).dialog({ | ||||
|             title: options.type+" library", | ||||
|             title: RED._("library.typeLibrary", {type:options.type}), | ||||
|             modal: true, | ||||
|             autoOpen: false, | ||||
|             width: 800, | ||||
|             height: 450, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: "Ok", | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     click: function() { | ||||
|                         if (selectedLibraryItem) { | ||||
|                             for (var i=0;i<options.fields.length;i++) { | ||||
| @@ -252,7 +254,7 @@ RED.library = (function() { | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: "Cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
| @@ -270,16 +272,16 @@ RED.library = (function() { | ||||
|                 $(".form-row:last-child",form).children().height(form.height()-60); | ||||
|             } | ||||
|         }); | ||||
|          | ||||
| 
 | ||||
|         function saveToLibrary(overwrite) { | ||||
|             var name = $("#node-input-name").val().replace(/(^\s*)|(\s*$)/g,""); | ||||
|             if (name === "") { | ||||
|                 name = "Unnamed "+options.type; | ||||
|                 name = RED._("library.unnamedType",{type:options.type}); | ||||
|             } | ||||
|             var filename = $("#node-dialog-library-save-filename").val().replace(/(^\s*)|(\s*$)/g,""); | ||||
|             var pathname = $("#node-dialog-library-save-folder").val().replace(/(^\s*)|(\s*$)/g,""); | ||||
|             if (filename === "" || !/.+\.js$/.test(filename)) { | ||||
|                 RED.notify("Invalid filename","warning"); | ||||
|                 RED.notify(RED._("library.invalidFilename"),"warning"); | ||||
|                 return; | ||||
|             } | ||||
|             var fullpath = pathname+(pathname===""?"":"/")+filename; | ||||
| @@ -304,8 +306,7 @@ RED.library = (function() { | ||||
|                 //    }
 | ||||
|                 //}
 | ||||
|                 //if (exists) {
 | ||||
|                 //    $("#node-dialog-library-save-type").html(options.type);
 | ||||
|                 //    $("#node-dialog-library-save-name").html(fullpath);
 | ||||
|                 //    $("#node-dialog-library-save-content").html(RED._("library.dialogSaveOverwrite",{libraryType:options.type,libraryName:fullpath}));
 | ||||
|                 //    $("#node-dialog-library-save-confirm").dialog( "open" );
 | ||||
|                 //    return;
 | ||||
|                 //}
 | ||||
| @@ -320,7 +321,7 @@ RED.library = (function() { | ||||
|                     data[field] = $("#node-input-"+field).val(); | ||||
|                 } | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|             data.text = options.editor.getValue(); | ||||
|             $.ajax({ | ||||
|                 url:"library/"+options.url+'/'+fullpath, | ||||
| @@ -328,27 +329,27 @@ RED.library = (function() { | ||||
|                 data: JSON.stringify(data), | ||||
|                 contentType: "application/json; charset=utf-8" | ||||
|             }).done(function(data,textStatus,xhr) { | ||||
|                 RED.notify("Saved "+options.type,"success"); | ||||
|                 RED.notify(RED._("library.savedType", {type:options.type}),"success"); | ||||
|             }).fail(function(xhr,textStatus,err) { | ||||
|                 RED.notify("Saved failed: "+xhr.responseJSON.message,"error"); | ||||
|                 RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error"); | ||||
|             }); | ||||
|         } | ||||
|         $( "#node-dialog-library-save-confirm" ).dialog({ | ||||
|             title: "Save to library", | ||||
|             title: RED._("library.saveToLibrary"), | ||||
|             modal: true, | ||||
|             autoOpen: false, | ||||
|             width: 530, | ||||
|             height: 230, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: "Ok", | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     click: function() { | ||||
|                         saveToLibrary(true); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: "Cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
| @@ -356,21 +357,21 @@ RED.library = (function() { | ||||
|             ] | ||||
|         }); | ||||
|         $( "#node-dialog-library-save" ).dialog({ | ||||
|             title: "Save to library", | ||||
|             title: RED._("library.saveToLibrary"), | ||||
|             modal: true, | ||||
|             autoOpen: false, | ||||
|             width: 530, | ||||
|             height: 230, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: "Ok", | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     click: function() { | ||||
|                         saveToLibrary(false); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: "Cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
| @@ -379,36 +380,90 @@ RED.library = (function() { | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function exportFlow() { | ||||
|         //TODO: don't rely on the main dialog
 | ||||
|         var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes); | ||||
|         $("#dialog-form").html($("script[data-template-name='export-library-dialog']").html()); | ||||
|         $("#node-input-filename").attr('nodes',JSON.stringify(nns)); | ||||
|         $( "#dialog" ).dialog("option","title","Export nodes to library").dialog( "open" ); | ||||
|         $("#node-input-library-filename").attr('nodes',JSON.stringify(nns)); | ||||
|         exportToLibraryDialog.dialog( "open" ); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     return { | ||||
|         init: function() { | ||||
|             RED.view.on("selection-changed",function(selection) { | ||||
|             RED.events.on("view:selection-changed",function(selection) { | ||||
|                 if (!selection.nodes) { | ||||
|                     RED.menu.setDisabled("btn-export-menu",true); | ||||
|                     RED.menu.setDisabled("btn-export-clipboard",true); | ||||
|                     RED.menu.setDisabled("btn-export-library",true); | ||||
|                     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("btn-export-menu",false); | ||||
|                     RED.menu.setDisabled("btn-export-clipboard",false); | ||||
|                     RED.menu.setDisabled("btn-export-library",false); | ||||
|                     RED.menu.setDisabled("menu-item-export",false); | ||||
|                     RED.menu.setDisabled("menu-item-export-clipboard",false); | ||||
|                     RED.menu.setDisabled("menu-item-export-library",false); | ||||
|                 } | ||||
|             }); | ||||
|              | ||||
|             loadFlowLibrary(); | ||||
| 
 | ||||
|             if (RED.settings.theme("menu.menu-item-import-library") !== false) { | ||||
|                 loadFlowLibrary(); | ||||
|             } | ||||
| 
 | ||||
|             exportToLibraryDialog = $('<div id="library-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>') | ||||
|                 .appendTo("body") | ||||
|                 .dialog({ | ||||
|                     modal: true, | ||||
|                     autoOpen: false, | ||||
|                     width: 500, | ||||
|                     resizable: false, | ||||
|                     title: RED._("library.exportToLibrary"), | ||||
|                     buttons: [ | ||||
|                         { | ||||
|                             id: "library-dialog-ok", | ||||
|                             text: RED._("common.label.ok"), | ||||
|                             click: function() { | ||||
|                                 //TODO: move this to RED.library
 | ||||
|                                 var flowName = $("#node-input-library-filename").val(); | ||||
|                                 if (!/^\s*$/.test(flowName)) { | ||||
|                                     $.ajax({ | ||||
|                                         url:'library/flows/'+flowName, | ||||
|                                         type: "POST", | ||||
|                                         data: $("#node-input-library-filename").attr('nodes'), | ||||
|                                         contentType: "application/json; charset=utf-8" | ||||
|                                     }).done(function() { | ||||
|                                             RED.library.loadFlowLibrary(); | ||||
|                                             RED.notify(RED._("library.savedNodes"),"success"); | ||||
|                                     }).fail(function(xhr,textStatus,err) { | ||||
|                                         RED.notify(RED._("library.saveFailed",{message:xhr.responseText}),"error"); | ||||
|                                     }); | ||||
|                                 } | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             id: "library-dialog-cancel", | ||||
|                             text: RED._("common.label.cancel"), | ||||
|                             click: function() { | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         } | ||||
|                     ], | ||||
|                     open: function(e) { | ||||
|                         $(this).parent().find(".ui-dialog-titlebar-close").hide(); | ||||
|                         RED.keyboard.disable(); | ||||
|                     }, | ||||
|                     close: function(e) { | ||||
|                         RED.keyboard.enable(); | ||||
|                     } | ||||
|             }); | ||||
|             exportToLibraryDialog.children(".dialog-form").append($( | ||||
|                 '<div class="form-row">'+ | ||||
|                 '<label for="node-input-library-filename" data-i18n="[append]editor:library.filename"><i class="fa fa-file"></i> </label>'+ | ||||
|                 '<input type="text" id="node-input-library-filename" data-i18n="[placeholder]editor:library.fullFilenamePlaceholder">'+ | ||||
|                 '<input type="text" style="display: none;" />'+ // Second hidden input to prevent submit on Enter
 | ||||
|                 '</div>' | ||||
|             )); | ||||
|         }, | ||||
|         create: createUI, | ||||
|         loadFlowLibrary: loadFlowLibrary, | ||||
|          | ||||
| 
 | ||||
|         export: exportFlow | ||||
|     } | ||||
| })(); | ||||
| 
 | ||||
| 
 | ||||
| @@ -23,7 +23,14 @@ RED.menu = (function() { | ||||
|     function createMenuItem(opt) { | ||||
|         var item; | ||||
| 
 | ||||
|         function setState() { | ||||
|         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"); | ||||
| @@ -45,12 +52,16 @@ RED.menu = (function() { | ||||
|             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)) { | ||||
| @@ -59,16 +70,16 @@ RED.menu = (function() { | ||||
|                     linkContent += '<i class="'+(opt.icon?opt.icon:'" style="display: inline-block;"')+'"></i> '; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|             if (opt.sublabel) { | ||||
|                 linkContent += '<span class="menu-label-container"><span class="menu-label">'+opt.label+'</span>'+ | ||||
|                                '<span class="menu-sublabel">'+opt.sublabel+'</span></span>' | ||||
|             } else { | ||||
|                 linkContent += '<span class="menu-label">'+opt.label+'</span>' | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|             linkContent += '</a>'; | ||||
|                  | ||||
| 
 | ||||
|             var link = $(linkContent).appendTo(item); | ||||
| 
 | ||||
|             menuItems[opt.id] = opt; | ||||
| @@ -99,7 +110,7 @@ RED.menu = (function() { | ||||
|                         opt.onselect.call(opt); | ||||
|                     } | ||||
|                 }); | ||||
|                 setState(); | ||||
|                 setInitialState(); | ||||
|             } else if (opt.href) { | ||||
|                 link.attr("target","_blank").attr("href",opt.href); | ||||
|             } else if (!opt.options) { | ||||
| @@ -113,23 +124,15 @@ RED.menu = (function() { | ||||
|                 var submenu = $('<ul id="'+opt.id+'-submenu" class="dropdown-menu"></ul>').appendTo(item); | ||||
| 
 | ||||
|                 for (var i=0;i<opt.options.length;i++) { | ||||
|                     createMenuItem(opt.options[i]).appendTo(submenu); | ||||
|                     var li = createMenuItem(opt.options[i]); | ||||
|                     if (li) { | ||||
|                         li.appendTo(submenu); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (opt.disabled) { | ||||
|                 item.addClass("disabled"); | ||||
|             } | ||||
|             if (opt.tip) { | ||||
|                 item.popover({ | ||||
|                     placement:"left", | ||||
|                     trigger: "hover", | ||||
|                     delay: { show: 350, hide: 20 }, | ||||
|                     html: true, | ||||
|                     container:'body', | ||||
|                     content: opt.tip | ||||
|                 }); | ||||
|             } | ||||
|              | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| @@ -144,13 +147,20 @@ RED.menu = (function() { | ||||
|         //    $("#"+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]; | ||||
|             createMenuItem(opt).appendTo(topMenu); | ||||
|             if (opt !== null || !lastAddedSeparator) { | ||||
|                 var li = createMenuItem(opt); | ||||
|                 if (li) { | ||||
|                     li.appendTo(topMenu); | ||||
|                     lastAddedSeparator = (opt === null); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -176,7 +186,7 @@ RED.menu = (function() { | ||||
|         } else { | ||||
|             $("#"+id).removeClass("active"); | ||||
|         } | ||||
|         if (opt.onselect) { | ||||
|         if (opt && opt.onselect) { | ||||
|             opt.onselect.call(opt,state); | ||||
|         } | ||||
|         setSavedState(id, state); | ||||
| @@ -191,24 +201,47 @@ RED.menu = (function() { | ||||
|     } | ||||
| 
 | ||||
|     function addItem(id,opt) { | ||||
|         createMenuItem(opt).appendTo("#"+id+"-submenu"); | ||||
|         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) { | ||||
|         menuItems[id].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]); | ||||
|             } | ||||
|         }); | ||||
|         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 { | ||||
| @@ -30,10 +30,10 @@ RED.notify = (function() { | ||||
|         } | ||||
|         var n = document.createElement("div"); | ||||
|         n.id="red-notification-"+c; | ||||
|         n.className = "alert"; | ||||
|         n.className = "notification"; | ||||
|         n.fixed = fixed; | ||||
|         if (type) { | ||||
|             n.className = "alert alert-"+type; | ||||
|             n.className = "notification notification-"+type; | ||||
|         } | ||||
|         n.style.display = "none"; | ||||
|         n.innerHTML = msg; | ||||
| @@ -44,7 +44,7 @@ RED.notify = (function() { | ||||
|             return function() { | ||||
|                 currentNotifications.splice(currentNotifications.indexOf(nn),1); | ||||
|                 $(nn).slideUp(300, function() { | ||||
|                         nn.parentNode.removeChild(nn); | ||||
|                     nn.parentNode.removeChild(nn); | ||||
|                 }); | ||||
|             }; | ||||
|         })(); | ||||
| @@ -56,4 +56,3 @@ RED.notify = (function() { | ||||
|         return n; | ||||
|     } | ||||
| })(); | ||||
| 
 | ||||
| @@ -17,27 +17,51 @@ | ||||
| RED.palette = (function() { | ||||
| 
 | ||||
|     var exclusion = ['config','unknown','deprecated']; | ||||
|     var core = ['subflows', 'input', 'output', 'function', 'social', 'storage', 'analysis', 'advanced']; | ||||
|     var core = ['subflows', 'input', 'output', 'function', 'social', 'mobile', 'storage', 'analysis', 'advanced']; | ||||
| 
 | ||||
|     function createCategoryContainer(category){ | ||||
|         var escapedCategory = category.replace(" ","_"); | ||||
|         var catDiv = $("#palette-container").append('<div id="palette-container-'+category+'" class="palette-category hide">'+ | ||||
|             '<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-caret-down"></i><span>'+category.replace("_"," ")+'</span></div>'+ | ||||
|     var categoryContainers = {}; | ||||
| 
 | ||||
|     function createCategoryContainer(category, label){ | ||||
|         label = label || category.replace("_", " "); | ||||
|         var catDiv = $('<div id="palette-container-'+category+'" class="palette-category palette-close hide">'+ | ||||
|             '<div id="palette-header-'+category+'" class="palette-header"><i class="expanded fa fa-angle-down"></i><span>'+label+'</span></div>'+ | ||||
|             '<div class="palette-content" id="palette-base-category-'+category+'">'+ | ||||
|             '<div id="palette-'+category+'-input"></div>'+ | ||||
|             '<div id="palette-'+category+'-output"></div>'+ | ||||
|             '<div id="palette-'+category+'-function"></div>'+ | ||||
|             '</div>'+ | ||||
|             '</div>'); | ||||
|             '</div>').appendTo("#palette-container"); | ||||
| 
 | ||||
|         categoryContainers[category] = { | ||||
|             container: catDiv, | ||||
|             close: function() { | ||||
|                 catDiv.removeClass("palette-open"); | ||||
|                 catDiv.addClass("palette-closed"); | ||||
|                 $("#palette-base-category-"+category).slideUp(); | ||||
|                 $("#palette-header-"+category+" i").removeClass("expanded"); | ||||
|             }, | ||||
|             open: function() { | ||||
|                 catDiv.addClass("palette-open"); | ||||
|                 catDiv.removeClass("palette-closed"); | ||||
|                 $("#palette-base-category-"+category).slideDown(); | ||||
|                 $("#palette-header-"+category+" i").addClass("expanded"); | ||||
|             }, | ||||
|             toggle: function() { | ||||
|                 if (catDiv.hasClass("palette-open")) { | ||||
|                     categoryContainers[category].close(); | ||||
|                 } else { | ||||
|                     categoryContainers[category].open(); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         $("#palette-header-"+category).on('click', function(e) { | ||||
|             $(this).next().slideToggle(); | ||||
|             $(this).children("i").toggleClass("expanded"); | ||||
|             categoryContainers[category].toggle(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function setLabel(type, el,label) { | ||||
|         var nodeWidth = 80; | ||||
|     function setLabel(type, el,label, info) { | ||||
|         var nodeWidth = 82; | ||||
|         var nodeHeight = 25; | ||||
|         var lineHeight = 20; | ||||
|         var portHeight = 10; | ||||
| @@ -77,20 +101,19 @@ RED.palette = (function() { | ||||
|             if (label != type) { | ||||
|                 l = "<p><b>"+label+"</b><br/><i>"+type+"</i></p>"; | ||||
|             } | ||||
|              | ||||
|             popOverContent = $(l+($("script[data-help-name|='"+type+"']").html()||"<p>no information available</p>").trim()) | ||||
|             popOverContent = $(l+(info?info:$("script[data-help-name|='"+type+"']").html()||"<p>"+RED._("palette.noInfo")+"</p>").trim()) | ||||
|                                 .filter(function(n) { | ||||
|                                     return this.nodeType == 1 || (this.nodeType == 3 && this.textContent.trim().length > 0) | ||||
|                                     return (this.nodeType == 1 && this.nodeName == "P") || (this.nodeType == 3 && this.textContent.trim().length > 0) | ||||
|                                 }).slice(0,2); | ||||
|         } catch(err) { | ||||
|             // Malformed HTML may cause errors. TODO: need to understand what can break
 | ||||
|             console.log("Error generating pop-over label for '"+type+"'."); | ||||
|             // NON-NLS: internal debug
 | ||||
|             console.log("Error generating pop-over label for ",type); | ||||
|             console.log(err.toString()); | ||||
|             popOverContent = "<p><b>"+label+"</b></p><p>no information available</p>"; | ||||
|             popOverContent = "<p><b>"+label+"</b></p><p>"+RED._("palette.noInfo")+"</p>"; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         el.data('popover').options.content = popOverContent; | ||||
|         el.data('popover').setContent(popOverContent); | ||||
|     } | ||||
| 
 | ||||
|     function escapeNodeType(nt) { | ||||
| @@ -102,7 +125,6 @@ RED.palette = (function() { | ||||
|         if ($("#palette_node_"+nodeTypeId).length) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (exclusion.indexOf(def.category)===-1) { | ||||
| 
 | ||||
|             var category = def.category.replace(" ","_"); | ||||
| @@ -120,15 +142,16 @@ RED.palette = (function() { | ||||
|                 label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||""; | ||||
|             } | ||||
| 
 | ||||
|              | ||||
| 
 | ||||
|             $('<div/>',{class:"palette_label"+(def.align=="right"?" palette_label_right":"")}).appendTo(d); | ||||
| 
 | ||||
|             d.className="palette_node"; | ||||
|              | ||||
|              | ||||
| 
 | ||||
| 
 | ||||
|             if (def.icon) { | ||||
|                 var icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon); | ||||
|                 var iconContainer = $('<div/>',{class:"palette_icon_container"+(def.align=="right"?" palette_icon_container_right":"")}).appendTo(d); | ||||
|                 $('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+def.icon+")"}).appendTo(iconContainer); | ||||
|                 $('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer); | ||||
|             } | ||||
| 
 | ||||
|             d.style.backgroundColor = def.color; | ||||
| @@ -146,7 +169,12 @@ RED.palette = (function() { | ||||
|             } | ||||
| 
 | ||||
|             if ($("#palette-base-category-"+rootCategory).length === 0) { | ||||
|                 createCategoryContainer(rootCategory); | ||||
|                 if(core.indexOf(rootCategory) !== -1){ | ||||
|                     createCategoryContainer(rootCategory, RED._("node-red:palette.label."+rootCategory, {defaultValue:rootCategory})); | ||||
|                 } else { | ||||
|                     var ns = def.set.id; | ||||
|                     createCategoryContainer(rootCategory, RED._(ns+":palette.label."+rootCategory, {defaultValue:rootCategory})); | ||||
|                 } | ||||
|             } | ||||
|             $("#palette-container-"+rootCategory).show(); | ||||
| 
 | ||||
| @@ -157,18 +185,30 @@ RED.palette = (function() { | ||||
|             $("#palette-"+category).append(d); | ||||
|             d.onmousedown = function(e) { e.preventDefault(); }; | ||||
| 
 | ||||
|             $(d).popover({ | ||||
|                 title:d.type, | ||||
|                 placement:"right", | ||||
|                 trigger: "hover", | ||||
|                 delay: { show: 750, hide: 50 }, | ||||
|                 html: true, | ||||
|                 container:'body' | ||||
|             RED.popover.create({ | ||||
|                 target:$(d), | ||||
|                 content: "hi", | ||||
|                 delay: { show: 750, hide: 50 } | ||||
|             }); | ||||
| 
 | ||||
|             // $(d).popover({
 | ||||
|             //     title:d.type,
 | ||||
|             //     placement:"right",
 | ||||
|             //     trigger: "hover",
 | ||||
|             //     delay: { show: 750, hide: 50 },
 | ||||
|             //     html: true,
 | ||||
|             //     container:'body'
 | ||||
|             // });
 | ||||
|             $(d).click(function() { | ||||
|                 RED.view.focus(); | ||||
|                 var help = '<div class="node-help">'+($("script[data-help-name|='"+d.type+"']").html()||"")+"</div>"; | ||||
|                 $("#tab-info").html(help); | ||||
|                 var helpText; | ||||
|                 if (nt.indexOf("subflow:") === 0) { | ||||
|                     helpText = marked(RED.nodes.subflow(nt.substring(8)).info||""); | ||||
|                 } else { | ||||
|                     helpText = $("script[data-help-name|='"+d.type+"']").html()||""; | ||||
|                 } | ||||
|                 var help = '<div class="node-help">'+helpText+"</div>"; | ||||
|                 RED.sidebar.info.set(help); | ||||
|             }); | ||||
|             $(d).draggable({ | ||||
|                 helper: 'clone', | ||||
| @@ -177,24 +217,22 @@ RED.palette = (function() { | ||||
|                 revertDuration: 50, | ||||
|                 start: function() {RED.view.focus();} | ||||
|             }); | ||||
|              | ||||
| 
 | ||||
|             var nodeInfo = null; | ||||
|             if (def.category == "subflows") { | ||||
|                 $(d).dblclick(function(e) { | ||||
|                     RED.workspaces.show(nt.substring(8)); | ||||
|                     e.preventDefault(); | ||||
|                 }); | ||||
|                 nodeInfo = marked(def.info||""); | ||||
|             } | ||||
|             setLabel(nt,$(d),label,nodeInfo); | ||||
| 
 | ||||
|             setLabel(nt,$(d),label); | ||||
|              | ||||
|             var categoryNode = $("#palette-container-"+category); | ||||
|             if (categoryNode.find(".palette_node").length === 1) { | ||||
|                 if (!categoryNode.find("i").hasClass("expanded")) { | ||||
|                     categoryNode.find(".palette-content").slideToggle(); | ||||
|                     categoryNode.find("i").toggleClass("expanded"); | ||||
|                 } | ||||
|                 categoryContainers[category].open(); | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -241,7 +279,7 @@ RED.palette = (function() { | ||||
|             } else if (portOutput.length !== 0 && sf.out.length === 0) { | ||||
|                 portOutput.remove(); | ||||
|             } | ||||
|             setLabel(sf.type+":"+sf.id,paletteNode,sf.name); | ||||
|             setLabel(sf.type+":"+sf.id,paletteNode,sf.name,marked(sf.info||"")); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @@ -262,40 +300,73 @@ RED.palette = (function() { | ||||
|                 $(this).hide(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         for (var category in categoryContainers) { | ||||
|             if (categoryContainers.hasOwnProperty(category)) { | ||||
|                 if (categoryContainers[category].container | ||||
|                         .find(".palette_node") | ||||
|                         .filter(function() { return $(this).css('display') !== 'none'}).length === 0) { | ||||
|                     categoryContainers[category].close(); | ||||
|                 } else { | ||||
|                     categoryContainers[category].open(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function init() { | ||||
|         $(".palette-spinner").show(); | ||||
|         if (RED.settings.paletteCategories) { | ||||
|             RED.settings.paletteCategories.forEach(createCategoryContainer); | ||||
|             RED.settings.paletteCategories.forEach(function(category){ | ||||
|                 createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category})); | ||||
|             }); | ||||
|         } else { | ||||
|             core.forEach(createCategoryContainer); | ||||
|             core.forEach(function(category){ | ||||
|                 createCategoryContainer(category, RED._("palette.label."+category,{defaultValue:category})); | ||||
|             }); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         $("#palette-search-input").focus(function(e) { | ||||
|             RED.keyboard.disable(); | ||||
|         }); | ||||
|         $("#palette-search-input").blur(function(e) { | ||||
|             RED.keyboard.enable(); | ||||
|         }); | ||||
|      | ||||
| 
 | ||||
|         $("#palette-search-clear").on("click",function(e) { | ||||
|             e.preventDefault(); | ||||
|             $("#palette-search-input").val(""); | ||||
|             filterChange(); | ||||
|             $("#palette-search-input").focus(); | ||||
|         }); | ||||
|      | ||||
| 
 | ||||
|         $("#palette-search-input").val(""); | ||||
|         $("#palette-search-input").on("keyup",function() { | ||||
|             filterChange(); | ||||
|         }); | ||||
|      | ||||
| 
 | ||||
|         $("#palette-search-input").on("focus",function() { | ||||
|             $("body").one("mousedown",function() { | ||||
|                 $("#palette-search-input").blur(); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         $("#palette-collapse-all").on("click", function(e) { | ||||
|             e.preventDefault(); | ||||
|             for (var cat in categoryContainers) { | ||||
|                 if (categoryContainers.hasOwnProperty(cat)) { | ||||
|                     categoryContainers[cat].close(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|         $("#palette-expand-all").on("click", function(e) { | ||||
|             e.preventDefault(); | ||||
|             for (var cat in categoryContainers) { | ||||
|                 if (categoryContainers.hasOwnProperty(cat)) { | ||||
|                     categoryContainers[cat].open(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
							
								
								
									
										79
									
								
								editor/js/ui/popover.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,79 @@ | ||||
| /** | ||||
|  * Copyright 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| RED.popover = (function() { | ||||
|  | ||||
|  | ||||
|     function createPopover(options) { | ||||
|         var target = options.target; | ||||
|  | ||||
|         var content = options.content; | ||||
|         var delay = options.delay; | ||||
|         var timer = null; | ||||
|         var active; | ||||
|         var div; | ||||
|  | ||||
|         var openPopup = function() { | ||||
|             if (active) { | ||||
|                 div = $('<div class="red-ui-popover"></div>').html(content).appendTo("body"); | ||||
|                 var targetPos = target.offset(); | ||||
|                 var targetWidth = target.width(); | ||||
|                 var targetHeight = target.height(); | ||||
|  | ||||
|                 var divHeight = div.height(); | ||||
|                 div.css({top: targetPos.top+targetHeight/2-divHeight/2-10,left:targetPos.left+targetWidth+17}); | ||||
|  | ||||
|                 div.fadeIn("fast"); | ||||
|             } | ||||
|         } | ||||
|         var closePopup = function() { | ||||
|             if (!active) { | ||||
|                 if (div) { | ||||
|                     div.fadeOut("fast",function() { | ||||
|                         $(this).remove(); | ||||
|                     }); | ||||
|                     div = null; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         target.on('mouseenter',function(e) { | ||||
|             clearTimeout(timer); | ||||
|             active = true; | ||||
|             timer = setTimeout(openPopup,delay.show); | ||||
|         }); | ||||
|         target.on('mouseleave', function(e) { | ||||
|             if (timer) { | ||||
|                 clearTimeout(timer); | ||||
|             } | ||||
|             active = false; | ||||
|             setTimeout(closePopup,delay.hide); | ||||
|         }); | ||||
|         var res = { | ||||
|             setContent: function(_content) { | ||||
|                 content = _content; | ||||
|             } | ||||
|         } | ||||
|         target.data('popover',res); | ||||
|         return res; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         create: createPopover | ||||
|     } | ||||
|  | ||||
| })(); | ||||
| @@ -20,25 +20,69 @@ RED.sidebar = (function() { | ||||
|         id:"sidebar-tabs", | ||||
|         onchange:function(tab) { | ||||
|             $("#sidebar-content").children().hide(); | ||||
|             $("#"+tab.id).show(); | ||||
|             if (tab.onchange) { | ||||
|                 tab.onchange.call(tab); | ||||
|             } | ||||
|             $(tab.content).show(); | ||||
|         }, | ||||
|         onremove: function(tab) { | ||||
|             $("#"+tab.id).remove(); | ||||
|         } | ||||
|             $(tab.content).hide(); | ||||
|             if (tab.onremove) { | ||||
|                 tab.onremove.call(tab); | ||||
|             } | ||||
|         }, | ||||
|         minimumActiveTabWidth: 110 | ||||
|     }); | ||||
|      | ||||
|     function addTab(title,content,closeable) { | ||||
|         $("#sidebar-content").append(content); | ||||
|         $(content).hide(); | ||||
|         sidebar_tabs.addTab({id:"tab-"+title,label:title,closeable:closeable}); | ||||
|         //content.style.position = "absolute";
 | ||||
|         //$('#sidebar').tabs("refresh");
 | ||||
| 
 | ||||
|     var knownTabs = { | ||||
| 
 | ||||
|     }; | ||||
| 
 | ||||
|     function addTab(title,content,closeable,visible) { | ||||
|         var options; | ||||
|         if (typeof title === "string") { | ||||
|             // TODO: legacy support in case anyone uses this...
 | ||||
|             options = { | ||||
|                 id: content.id, | ||||
|                 label: title, | ||||
|                 name: title, | ||||
|                 content: content, | ||||
|                 closeable: closeable, | ||||
|                 visible: visible | ||||
|             } | ||||
|         } else if (typeof title === "object") { | ||||
|             options = title; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         $("#sidebar-content").append(options.content); | ||||
|         $(options.content).hide(); | ||||
|         var id = options.id; | ||||
| 
 | ||||
|         RED.menu.addItem("menu-item-sidebar-menu",{ | ||||
|             id:"menu-item-sidebar-menu-"+options.id, | ||||
|             label:options.name, | ||||
|             onselect:function() { | ||||
|                 showSidebar(options.id); | ||||
|             }, | ||||
|             group: "sidebar-tabs" | ||||
|         }); | ||||
| 
 | ||||
|         knownTabs[options.id] = options; | ||||
| 
 | ||||
|         if (options.visible !== false) { | ||||
|             sidebar_tabs.addTab(knownTabs[options.id]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function removeTab(title) { | ||||
|         sidebar_tabs.removeTab("tab-"+title); | ||||
|     function removeTab(id) { | ||||
|         sidebar_tabs.removeTab(id); | ||||
|         $(knownTabs[id].content).remove(); | ||||
|         delete knownTabs[id]; | ||||
|         RED.menu.removeItem("menu-item-sidebar-menu-"+id); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     var sidebarSeparator =  {}; | ||||
|     $("#sidebar-separator").draggable({ | ||||
|             axis: "x", | ||||
| @@ -51,15 +95,14 @@ RED.sidebar = (function() { | ||||
|                 sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2; | ||||
| 
 | ||||
| 
 | ||||
|                 if (!RED.menu.isSelected("btn-sidebar")) { | ||||
|                 if (!RED.menu.isSelected("menu-item-sidebar")) { | ||||
|                     sidebarSeparator.opening = true; | ||||
|                     var newChartRight = 15; | ||||
|                     var newChartRight = 7; | ||||
|                     $("#sidebar").addClass("closing"); | ||||
|                     $("#workspace").css("right",newChartRight); | ||||
|                     $("#chart-zoom-controls").css("right",newChartRight+20); | ||||
|                     $("#sidebar").width(0); | ||||
|                     RED.menu.setSelected("btn-sidebar",true); | ||||
|                     eventHandler.emit("resize"); | ||||
|                     RED.menu.setSelected("menu-item-sidebar",true); | ||||
|                     RED.events.emit("sidebar:resize"); | ||||
|                 } | ||||
|                 sidebarSeparator.width = $("#sidebar").width(); | ||||
|             }, | ||||
| @@ -67,9 +110,9 @@ RED.sidebar = (function() { | ||||
|                 var d = ui.position.left-sidebarSeparator.start; | ||||
|                 var newSidebarWidth = sidebarSeparator.width-d; | ||||
|                 if (sidebarSeparator.opening) { | ||||
|                     newSidebarWidth -= 13; | ||||
|                     newSidebarWidth -= 3; | ||||
|                 } | ||||
|                  | ||||
| 
 | ||||
|                 if (newSidebarWidth > 150) { | ||||
|                     if (sidebarSeparator.chartWidth+d < 200) { | ||||
|                         ui.position.left = 200+sidebarSeparator.start-sidebarSeparator.chartWidth; | ||||
| @@ -77,7 +120,7 @@ RED.sidebar = (function() { | ||||
|                         newSidebarWidth = sidebarSeparator.width-d; | ||||
|                     } | ||||
|                 } | ||||
|                      | ||||
| 
 | ||||
|                 if (newSidebarWidth < 150) { | ||||
|                     if (!sidebarSeparator.closing) { | ||||
|                         $("#sidebar").addClass("closing"); | ||||
| @@ -95,28 +138,26 @@ RED.sidebar = (function() { | ||||
| 
 | ||||
|                 var newChartRight = sidebarSeparator.chartRight-d; | ||||
|                 $("#workspace").css("right",newChartRight); | ||||
|                 $("#chart-zoom-controls").css("right",newChartRight+20); | ||||
|                 $("#sidebar").width(newSidebarWidth); | ||||
| 
 | ||||
|                 sidebar_tabs.resize(); | ||||
|                 eventHandler.emit("resize"); | ||||
|                 RED.events.emit("sidebar:resize"); | ||||
|             }, | ||||
|             stop:function(event,ui) { | ||||
|                 if (sidebarSeparator.closing) { | ||||
|                     $("#sidebar").removeClass("closing"); | ||||
|                     RED.menu.setSelected("btn-sidebar",false); | ||||
|                     RED.menu.setSelected("menu-item-sidebar",false); | ||||
|                     if ($("#sidebar").width() < 180) { | ||||
|                         $("#sidebar").width(180); | ||||
|                         $("#workspace").css("right",208); | ||||
|                         $("#chart-zoom-controls").css("right",228); | ||||
|                         $("#workspace").css("right",187); | ||||
|                     } | ||||
|                 } | ||||
|                 $("#sidebar-separator").css("left","auto"); | ||||
|                 $("#sidebar-separator").css("right",($("#sidebar").width()+13)+"px"); | ||||
|                 eventHandler.emit("resize"); | ||||
|                 $("#sidebar-separator").css("right",($("#sidebar").width()+2)+"px"); | ||||
|                 RED.events.emit("sidebar:resize"); | ||||
|             } | ||||
|     }); | ||||
|      | ||||
| 
 | ||||
|     function toggleSidebar(state) { | ||||
|         if (!state) { | ||||
|             $("#main-container").addClass("sidebar-closed"); | ||||
| @@ -124,44 +165,34 @@ RED.sidebar = (function() { | ||||
|             $("#main-container").removeClass("sidebar-closed"); | ||||
|             sidebar_tabs.resize(); | ||||
|         } | ||||
|         eventHandler.emit("resize"); | ||||
|         RED.events.emit("sidebar:resize"); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function showSidebar(id) { | ||||
|         if (id) { | ||||
|             sidebar_tabs.activateTab("tab-"+id); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     function containsTab(id) { | ||||
|         return sidebar_tabs.contains("tab-"+id); | ||||
|     } | ||||
|      | ||||
|     function init () { | ||||
|         RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("btn-sidebar",!RED.menu.isSelected("btn-sidebar"));d3.event.preventDefault();}); | ||||
|         showSidebar(); | ||||
|         RED.sidebar.info.show(); | ||||
|     } | ||||
|      | ||||
|     var eventHandler = (function() { | ||||
|         var handlers = {}; | ||||
|          | ||||
|         return { | ||||
|             on: function(evt,func) { | ||||
|                 handlers[evt] = handlers[evt]||[]; | ||||
|                 handlers[evt].push(func); | ||||
|             }, | ||||
|             emit: function(evt,arg) { | ||||
|                 if (handlers[evt]) { | ||||
|                     for (var i=0;i<handlers[evt].length;i++) { | ||||
|                         handlers[evt][i](arg); | ||||
|                     } | ||||
|                      | ||||
|                 } | ||||
|             if (!containsTab(id)) { | ||||
|                 sidebar_tabs.addTab(knownTabs[id]); | ||||
|             } | ||||
|             sidebar_tabs.activateTab(id); | ||||
|             if (!RED.menu.isSelected("menu-item-sidebar")) { | ||||
|                 RED.menu.setSelected("menu-item-sidebar",true); | ||||
|             } | ||||
|         } | ||||
|     })(); | ||||
|      | ||||
|     } | ||||
| 
 | ||||
|     function containsTab(id) { | ||||
|         return sidebar_tabs.contains(id); | ||||
|     } | ||||
| 
 | ||||
|     function init () { | ||||
|         RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){RED.menu.setSelected("menu-item-sidebar",!RED.menu.isSelected("menu-item-sidebar"));d3.event.preventDefault();}); | ||||
|         showSidebar(); | ||||
|         RED.sidebar.info.init(); | ||||
|         RED.sidebar.config.init(); | ||||
|         // hide info bar at start if screen rather narrow...
 | ||||
|         if ($(window).width() < 600) { toggleSidebar(); } | ||||
|     } | ||||
| 
 | ||||
|     return { | ||||
|         init: init, | ||||
|         addTab: addTab, | ||||
| @@ -169,7 +200,6 @@ RED.sidebar = (function() { | ||||
|         show: showSidebar, | ||||
|         containsTab: containsTab, | ||||
|         toggleSidebar: toggleSidebar, | ||||
|         on: eventHandler.on | ||||
|     } | ||||
|      | ||||
| 
 | ||||
| })(); | ||||
							
								
								
									
										608
									
								
								editor/js/ui/subflow.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,608 @@ | ||||
| /** | ||||
|  * Copyright 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| RED.subflow = (function() { | ||||
|  | ||||
|  | ||||
|     function getSubflow() { | ||||
|         return RED.nodes.subflow(RED.workspaces.active()); | ||||
|     } | ||||
|  | ||||
|     function findAvailableSubflowIOPosition(subflow,isInput) { | ||||
|         var pos = {x:50,y:30}; | ||||
|         if (!isInput) { | ||||
|             pos.x += 110; | ||||
|         } | ||||
|         for (var i=0;i<subflow.out.length+subflow.in.length;i++) { | ||||
|             var port; | ||||
|             if (i < subflow.out.length) { | ||||
|                 port = subflow.out[i]; | ||||
|             } else { | ||||
|                 port = subflow.in[i-subflow.out.length]; | ||||
|             } | ||||
|             if (port.x == pos.x && port.y == pos.y) { | ||||
|                 pos.x += 55; | ||||
|                 i=0; | ||||
|             } | ||||
|         } | ||||
|         return pos; | ||||
|     } | ||||
|  | ||||
|     function addSubflowInput() { | ||||
|         var subflow = RED.nodes.subflow(RED.workspaces.active()); | ||||
|         if (subflow.in.length === 1) { | ||||
|             return; | ||||
|         } | ||||
|         var position = findAvailableSubflowIOPosition(subflow,true); | ||||
|         var newInput = { | ||||
|             type:"subflow", | ||||
|             direction:"in", | ||||
|             z:subflow.id, | ||||
|             i:subflow.in.length, | ||||
|             x:position.x, | ||||
|             y:position.y, | ||||
|             id:RED.nodes.id() | ||||
|         }; | ||||
|         var oldInCount = subflow.in.length; | ||||
|         subflow.in.push(newInput); | ||||
|         subflow.dirty = true; | ||||
|         var wasDirty = RED.nodes.dirty(); | ||||
|         var wasChanged = subflow.changed; | ||||
|         subflow.changed = true; | ||||
|         var result = refresh(true); | ||||
|         var historyEvent = { | ||||
|             t:'edit', | ||||
|             node:subflow, | ||||
|             dirty:wasDirty, | ||||
|             changed:wasChanged, | ||||
|             subflow: { | ||||
|                 inputCount: oldInCount, | ||||
|                 instances: result.instances | ||||
|             } | ||||
|         }; | ||||
|         RED.history.push(historyEvent); | ||||
|         RED.view.select(); | ||||
|         RED.nodes.dirty(true); | ||||
|         RED.view.redraw(); | ||||
|         $("#workspace-subflow-input-add").addClass("active"); | ||||
|         $("#workspace-subflow-input-remove").removeClass("active"); | ||||
|     } | ||||
|  | ||||
|     function removeSubflowInput() { | ||||
|         var activeSubflow = RED.nodes.subflow(RED.workspaces.active()); | ||||
|         if (activeSubflow.in.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|         var removedInput = activeSubflow.in[0]; | ||||
|         var removedInputLinks = []; | ||||
|         RED.nodes.eachLink(function(l) { | ||||
|             if (l.source.type == "subflow" && l.source.z == activeSubflow.id && l.source.i == removedInput.i) { | ||||
|                 removedInputLinks.push(l); | ||||
|             } else if (l.target.type == "subflow:"+activeSubflow.id) { | ||||
|                 removedInputLinks.push(l); | ||||
|             } | ||||
|         }); | ||||
|         removedInputLinks.forEach(function(l) { RED.nodes.removeLink(l)}); | ||||
|         activeSubflow.in = []; | ||||
|         $("#workspace-subflow-input-add").removeClass("active"); | ||||
|         $("#workspace-subflow-input-remove").addClass("active"); | ||||
|         activeSubflow.changed = true; | ||||
|         return {subflowInputs: [ removedInput ], links:removedInputLinks}; | ||||
|     } | ||||
|  | ||||
|     function addSubflowOutput(id) { | ||||
|         var subflow = RED.nodes.subflow(RED.workspaces.active()); | ||||
|         var position = findAvailableSubflowIOPosition(subflow,false); | ||||
|  | ||||
|         var newOutput = { | ||||
|             type:"subflow", | ||||
|             direction:"out", | ||||
|             z:subflow.id, | ||||
|             i:subflow.out.length, | ||||
|             x:position.x, | ||||
|             y:position.y, | ||||
|             id:RED.nodes.id() | ||||
|         }; | ||||
|         var oldOutCount = subflow.out.length; | ||||
|         subflow.out.push(newOutput); | ||||
|         subflow.dirty = true; | ||||
|         var wasDirty = RED.nodes.dirty(); | ||||
|         var wasChanged = subflow.changed; | ||||
|         subflow.changed = true; | ||||
|  | ||||
|         var result = refresh(true); | ||||
|  | ||||
|         var historyEvent = { | ||||
|             t:'edit', | ||||
|             node:subflow, | ||||
|             dirty:wasDirty, | ||||
|             changed:wasChanged, | ||||
|             subflow: { | ||||
|                 outputCount: oldOutCount, | ||||
|                 instances: result.instances | ||||
|             } | ||||
|         }; | ||||
|         RED.history.push(historyEvent); | ||||
|         RED.view.select(); | ||||
|         RED.nodes.dirty(true); | ||||
|         RED.view.redraw(); | ||||
|         $("#workspace-subflow-output .spinner-value").html(subflow.out.length); | ||||
|     } | ||||
|  | ||||
|     function removeSubflowOutput(removedSubflowOutputs) { | ||||
|         var activeSubflow = RED.nodes.subflow(RED.workspaces.active()); | ||||
|         if (activeSubflow.out.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|         if (typeof removedSubflowOutputs === "undefined") { | ||||
|             removedSubflowOutputs = [activeSubflow.out[activeSubflow.out.length-1]]; | ||||
|         } | ||||
|         var removedLinks = []; | ||||
|         removedSubflowOutputs.sort(function(a,b) { return b.i-a.i}); | ||||
|         for (i=0;i<removedSubflowOutputs.length;i++) { | ||||
|             var output = removedSubflowOutputs[i]; | ||||
|             activeSubflow.out.splice(output.i,1); | ||||
|             var subflowRemovedLinks = []; | ||||
|             var subflowMovedLinks = []; | ||||
|             RED.nodes.eachLink(function(l) { | ||||
|                 if (l.target.type == "subflow" && l.target.z == activeSubflow.id && l.target.i == output.i) { | ||||
|                     subflowRemovedLinks.push(l); | ||||
|                 } | ||||
|                 if (l.source.type == "subflow:"+activeSubflow.id) { | ||||
|                     if (l.sourcePort == output.i) { | ||||
|                         subflowRemovedLinks.push(l); | ||||
|                     } else if (l.sourcePort > output.i) { | ||||
|                         subflowMovedLinks.push(l); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             subflowRemovedLinks.forEach(function(l) { RED.nodes.removeLink(l)}); | ||||
|             subflowMovedLinks.forEach(function(l) { l.sourcePort--; }); | ||||
|  | ||||
|             removedLinks = removedLinks.concat(subflowRemovedLinks); | ||||
|             for (var j=output.i;j<activeSubflow.out.length;j++) { | ||||
|                 activeSubflow.out[j].i--; | ||||
|                 activeSubflow.out[j].dirty = true; | ||||
|             } | ||||
|         } | ||||
|         activeSubflow.changed = true; | ||||
|  | ||||
|         return {subflowOutputs: removedSubflowOutputs, links: removedLinks} | ||||
|     } | ||||
|  | ||||
|     function refresh(markChange) { | ||||
|         var activeSubflow = RED.nodes.subflow(RED.workspaces.active()); | ||||
|         refreshToolbar(activeSubflow); | ||||
|         var subflowInstances = []; | ||||
|         if (activeSubflow) { | ||||
|             RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) { | ||||
|                 subflowInstances.push({ | ||||
|                     id: n.id, | ||||
|                     changed: n.changed | ||||
|                 }); | ||||
|                 if (markChange) { | ||||
|                     n.changed = true; | ||||
|                 } | ||||
|                 n.inputs = activeSubflow.in.length; | ||||
|                 n.outputs = activeSubflow.out.length; | ||||
|                 while (n.outputs < n.ports.length) { | ||||
|                     n.ports.pop(); | ||||
|                 } | ||||
|                 n.resize = true; | ||||
|                 n.dirty = true; | ||||
|                 RED.editor.updateNodeProperties(n); | ||||
|             }); | ||||
|             RED.editor.validateNode(activeSubflow); | ||||
|             return { | ||||
|                 instances: subflowInstances | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function refreshToolbar(activeSubflow) { | ||||
|         if (activeSubflow) { | ||||
|             $("#workspace-subflow-input-add").toggleClass("active", activeSubflow.in.length !== 0); | ||||
|             $("#workspace-subflow-input-remove").toggleClass("active",activeSubflow.in.length === 0); | ||||
|  | ||||
|             $("#workspace-subflow-output .spinner-value").html(activeSubflow.out.length); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function showWorkspaceToolbar(activeSubflow) { | ||||
|         var toolbar = $("#workspace-toolbar"); | ||||
|         toolbar.empty(); | ||||
|  | ||||
|         $('<a class="button" id="workspace-subflow-edit" href="#" data-i18n="[append]subflow.editSubflowProperties"><i class="fa fa-pencil"></i> </a>').appendTo(toolbar); | ||||
|         $('<span style="margin-left: 5px;" data-i18n="subflow.input"></span> '+ | ||||
|             '<div style="display: inline-block;" class="button-group">'+ | ||||
|             '<a id="workspace-subflow-input-remove" class="button active" href="#">0</a>'+ | ||||
|             '<a id="workspace-subflow-input-add" class="button" href="#">1</a>'+ | ||||
|             '</div>').appendTo(toolbar); | ||||
|  | ||||
|         $('<span style="margin-left: 5px;" data-i18n="subflow.output"></span> <div id="workspace-subflow-output" style="display: inline-block;" class="button-group spinner-group">'+ | ||||
|             '<a id="workspace-subflow-output-remove" class="button" href="#"><i class="fa fa-minus"></i></a>'+ | ||||
|             '<div class="spinner-value">3</div>'+ | ||||
|             '<a id="workspace-subflow-output-add" class="button" href="#"><i class="fa fa-plus"></i></a>'+ | ||||
|             '</div>').appendTo(toolbar); | ||||
|  | ||||
|         // $('<a class="button disabled" id="workspace-subflow-add-input" href="#" data-i18n="[append]subflow.input"><i class="fa fa-plus"></i> </a>').appendTo(toolbar); | ||||
|         // $('<a class="button" id="workspace-subflow-add-output" href="#" data-i18n="[append]subflow.output"><i class="fa fa-plus"></i> </a>').appendTo(toolbar); | ||||
|         $('<a class="button" id="workspace-subflow-delete" href="#" data-i18n="[append]subflow.deleteSubflow"><i class="fa fa-trash"></i> </a>').appendTo(toolbar); | ||||
|         toolbar.i18n(); | ||||
|  | ||||
|  | ||||
|         $("#workspace-subflow-output-remove").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             var wasDirty = RED.nodes.dirty(); | ||||
|             var wasChanged = activeSubflow.changed; | ||||
|             var result = removeSubflowOutput(); | ||||
|             if (result) { | ||||
|                 var inst = refresh(true); | ||||
|                 RED.history.push({ | ||||
|                     t:'delete', | ||||
|                     links:result.links, | ||||
|                     subflowOutputs: result.subflowOutputs, | ||||
|                     changed: wasChanged, | ||||
|                     dirty:wasDirty, | ||||
|                     subflow: { | ||||
|                         instances: inst.instances | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 RED.view.select(); | ||||
|                 RED.nodes.dirty(true); | ||||
|                 RED.view.redraw(true); | ||||
|             } | ||||
|         }); | ||||
|         $("#workspace-subflow-output-add").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             addSubflowOutput(); | ||||
|         }); | ||||
|  | ||||
|         $("#workspace-subflow-input-add").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             addSubflowInput(); | ||||
|         }); | ||||
|         $("#workspace-subflow-input-remove").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             var wasDirty = RED.nodes.dirty(); | ||||
|             var wasChanged = activeSubflow.changed; | ||||
|             activeSubflow.changed = true; | ||||
|             var result = removeSubflowInput(); | ||||
|             if (result) { | ||||
|                 var inst = refresh(true); | ||||
|                 RED.history.push({ | ||||
|                     t:'delete', | ||||
|                     links:result.links, | ||||
|                     changed: wasChanged, | ||||
|                     subflowInputs: result.subflowInputs, | ||||
|                     dirty:wasDirty, | ||||
|                     subflow: { | ||||
|                         instances: inst.instances | ||||
|                     } | ||||
|                 }); | ||||
|                 RED.view.select(); | ||||
|                 RED.nodes.dirty(true); | ||||
|                 RED.view.redraw(true); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         $("#workspace-subflow-edit").click(function(event) { | ||||
|             RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active())); | ||||
|             event.preventDefault(); | ||||
|         }); | ||||
|  | ||||
|         $("#workspace-subflow-delete").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             var removedNodes = []; | ||||
|             var removedLinks = []; | ||||
|             var startDirty = RED.nodes.dirty(); | ||||
|  | ||||
|             var activeSubflow = getSubflow(); | ||||
|  | ||||
|             RED.nodes.eachNode(function(n) { | ||||
|                 if (n.type == "subflow:"+activeSubflow.id) { | ||||
|                     removedNodes.push(n); | ||||
|                 } | ||||
|                 if (n.z == activeSubflow.id) { | ||||
|                     removedNodes.push(n); | ||||
|                 } | ||||
|             }); | ||||
|             RED.nodes.eachConfig(function(n) { | ||||
|                 if (n.z == activeSubflow.id) { | ||||
|                     removedNodes.push(n); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             var removedConfigNodes = []; | ||||
|             for (var i=0;i<removedNodes.length;i++) { | ||||
|                 var removedEntities = RED.nodes.remove(removedNodes[i].id); | ||||
|                 removedLinks = removedLinks.concat(removedEntities.links); | ||||
|                 removedConfigNodes = removedConfigNodes.concat(removedEntities.nodes); | ||||
|             } | ||||
|             // TODO: this whole delete logic should be in RED.nodes.removeSubflow.. | ||||
|             removedNodes = removedNodes.concat(removedConfigNodes); | ||||
|  | ||||
|             RED.nodes.removeSubflow(activeSubflow); | ||||
|  | ||||
|             RED.history.push({ | ||||
|                     t:'delete', | ||||
|                     nodes:removedNodes, | ||||
|                     links:removedLinks, | ||||
|                     subflow: { | ||||
|                         subflow: activeSubflow | ||||
|                     }, | ||||
|                     dirty:startDirty | ||||
|             }); | ||||
|  | ||||
|             RED.workspaces.remove(activeSubflow); | ||||
|             RED.nodes.dirty(true); | ||||
|             RED.view.redraw(); | ||||
|         }); | ||||
|  | ||||
|         refreshToolbar(activeSubflow); | ||||
|  | ||||
|         $("#chart").css({"margin-top": "40px"}); | ||||
|         $("#workspace-toolbar").show(); | ||||
|     } | ||||
|     function hideWorkspaceToolbar() { | ||||
|         $("#workspace-toolbar").hide().empty(); | ||||
|         $("#chart").css({"margin-top": "0"}); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function init() { | ||||
|         RED.events.on("workspace:change",function(event) { | ||||
|             var activeSubflow = RED.nodes.subflow(event.workspace); | ||||
|             if (activeSubflow) { | ||||
|                 showWorkspaceToolbar(activeSubflow); | ||||
|             } else { | ||||
|                 hideWorkspaceToolbar(); | ||||
|             } | ||||
|         }); | ||||
|         RED.events.on("view:selection-changed",function(selection) { | ||||
|             if (!selection.nodes) { | ||||
|                 RED.menu.setDisabled("menu-item-subflow-convert",true); | ||||
|             } else { | ||||
|                 RED.menu.setDisabled("menu-item-subflow-convert",false); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function createSubflow() { | ||||
|         var lastIndex = 0; | ||||
|         RED.nodes.eachSubflow(function(sf) { | ||||
|            var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name); | ||||
|            if (m) { | ||||
|                lastIndex = Math.max(lastIndex,m[1]); | ||||
|            } | ||||
|         }); | ||||
|  | ||||
|         var name = "Subflow "+(lastIndex+1); | ||||
|  | ||||
|         var subflowId = RED.nodes.id(); | ||||
|         var subflow = { | ||||
|             type:"subflow", | ||||
|             id:subflowId, | ||||
|             name:name, | ||||
|             info:"", | ||||
|             in: [], | ||||
|             out: [] | ||||
|         }; | ||||
|         RED.nodes.addSubflow(subflow); | ||||
|         RED.history.push({ | ||||
|             t:'createSubflow', | ||||
|             subflow: { | ||||
|                 subflow:subflow | ||||
|             }, | ||||
|             dirty:RED.nodes.dirty() | ||||
|         }); | ||||
|         RED.workspaces.show(subflowId); | ||||
|         RED.nodes.dirty(true); | ||||
|     } | ||||
|  | ||||
|     function convertToSubflow() { | ||||
|         var selection = RED.view.selection(); | ||||
|         if (!selection.nodes) { | ||||
|             RED.notify(RED._("subflow.errors.noNodesSelected"),"error"); | ||||
|             return; | ||||
|         } | ||||
|         var i; | ||||
|         var nodes = {}; | ||||
|         var new_links = []; | ||||
|         var removedLinks = []; | ||||
|  | ||||
|         var candidateInputs = []; | ||||
|         var candidateOutputs = []; | ||||
|         var candidateInputNodes = {}; | ||||
|  | ||||
|  | ||||
|         var boundingBox = [selection.nodes[0].x, | ||||
|             selection.nodes[0].y, | ||||
|             selection.nodes[0].x, | ||||
|             selection.nodes[0].y]; | ||||
|  | ||||
|         for (i=0;i<selection.nodes.length;i++) { | ||||
|             var n = selection.nodes[i]; | ||||
|             nodes[n.id] = {n:n,outputs:{}}; | ||||
|             boundingBox = [ | ||||
|                 Math.min(boundingBox[0],n.x), | ||||
|                 Math.min(boundingBox[1],n.y), | ||||
|                 Math.max(boundingBox[2],n.x), | ||||
|                 Math.max(boundingBox[3],n.y) | ||||
|             ] | ||||
|         } | ||||
|  | ||||
|         var center = [(boundingBox[2]+boundingBox[0]) / 2,(boundingBox[3]+boundingBox[1]) / 2]; | ||||
|  | ||||
|         RED.nodes.eachLink(function(link) { | ||||
|             if (nodes[link.source.id] && nodes[link.target.id]) { | ||||
|                 // A link wholely within the selection | ||||
|             } | ||||
|  | ||||
|             if (nodes[link.source.id] && !nodes[link.target.id]) { | ||||
|                 // An outbound link from the selection | ||||
|                 candidateOutputs.push(link); | ||||
|                 removedLinks.push(link); | ||||
|             } | ||||
|             if (!nodes[link.source.id] && nodes[link.target.id]) { | ||||
|                 // An inbound link | ||||
|                 candidateInputs.push(link); | ||||
|                 candidateInputNodes[link.target.id] = link.target; | ||||
|                 removedLinks.push(link); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         var outputs = {}; | ||||
|         candidateOutputs = candidateOutputs.filter(function(v) { | ||||
|              if (outputs[v.source.id+":"+v.sourcePort]) { | ||||
|                  outputs[v.source.id+":"+v.sourcePort].targets.push(v.target); | ||||
|                  return false; | ||||
|              } | ||||
|              v.targets = []; | ||||
|              v.targets.push(v.target); | ||||
|              outputs[v.source.id+":"+v.sourcePort] = v; | ||||
|              return true; | ||||
|         }); | ||||
|         candidateOutputs.sort(function(a,b) { return a.source.y-b.source.y}); | ||||
|  | ||||
|         if (Object.keys(candidateInputNodes).length > 1) { | ||||
|              RED.notify(RED._("subflow.errors.multipleInputsToSelection"),"error"); | ||||
|              return; | ||||
|         } | ||||
|  | ||||
|         var lastIndex = 0; | ||||
|         RED.nodes.eachSubflow(function(sf) { | ||||
|            var m = (new RegExp("^Subflow (\\d+)$")).exec(sf.name); | ||||
|            if (m) { | ||||
|                lastIndex = Math.max(lastIndex,m[1]); | ||||
|            } | ||||
|         }); | ||||
|  | ||||
|         var name = "Subflow "+(lastIndex+1); | ||||
|  | ||||
|         var subflowId = RED.nodes.id(); | ||||
|         var subflow = { | ||||
|             type:"subflow", | ||||
|             id:subflowId, | ||||
|             name:name, | ||||
|             info:"", | ||||
|             in: Object.keys(candidateInputNodes).map(function(v,i) { var index = i; return { | ||||
|                 type:"subflow", | ||||
|                 direction:"in", | ||||
|                 x:candidateInputNodes[v].x-(candidateInputNodes[v].w/2)-80, | ||||
|                 y:candidateInputNodes[v].y, | ||||
|                 z:subflowId, | ||||
|                 i:index, | ||||
|                 id:RED.nodes.id(), | ||||
|                 wires:[{id:candidateInputNodes[v].id}] | ||||
|             }}), | ||||
|             out: candidateOutputs.map(function(v,i) { var index = i; return { | ||||
|                 type:"subflow", | ||||
|                 direction:"in", | ||||
|                 x:v.source.x+(v.source.w/2)+80, | ||||
|                 y:v.source.y, | ||||
|                 z:subflowId, | ||||
|                 i:index, | ||||
|                 id:RED.nodes.id(), | ||||
|                 wires:[{id:v.source.id,port:v.sourcePort}] | ||||
|             }}) | ||||
|         }; | ||||
|  | ||||
|         RED.nodes.addSubflow(subflow); | ||||
|  | ||||
|         var subflowInstance = { | ||||
|             id:RED.nodes.id(), | ||||
|             type:"subflow:"+subflow.id, | ||||
|             x: center[0], | ||||
|             y: center[1], | ||||
|             z: RED.workspaces.active(), | ||||
|             inputs: subflow.in.length, | ||||
|             outputs: subflow.out.length, | ||||
|             h: Math.max(30/*node_height*/,(subflow.out.length||0) * 15), | ||||
|             changed:true | ||||
|         } | ||||
|         subflowInstance._def = RED.nodes.getType(subflowInstance.type); | ||||
|         RED.editor.validateNode(subflowInstance); | ||||
|         RED.nodes.add(subflowInstance); | ||||
|  | ||||
|         candidateInputs.forEach(function(l) { | ||||
|             var link = {source:l.source, sourcePort:l.sourcePort, target: subflowInstance}; | ||||
|             new_links.push(link); | ||||
|             RED.nodes.addLink(link); | ||||
|         }); | ||||
|  | ||||
|         candidateOutputs.forEach(function(output,i) { | ||||
|             output.targets.forEach(function(target) { | ||||
|                 var link = {source:subflowInstance, sourcePort:i, target: target}; | ||||
|                 new_links.push(link); | ||||
|                 RED.nodes.addLink(link); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         subflow.in.forEach(function(input) { | ||||
|             input.wires.forEach(function(wire) { | ||||
|                 var link = {source: input, sourcePort: 0, target: RED.nodes.node(wire.id) } | ||||
|                 new_links.push(link); | ||||
|                 RED.nodes.addLink(link); | ||||
|             }); | ||||
|         }); | ||||
|         subflow.out.forEach(function(output,i) { | ||||
|             output.wires.forEach(function(wire) { | ||||
|                 var link = {source: RED.nodes.node(wire.id), sourcePort: wire.port , target: output } | ||||
|                 new_links.push(link); | ||||
|                 RED.nodes.addLink(link); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         for (i=0;i<removedLinks.length;i++) { | ||||
|             RED.nodes.removeLink(removedLinks[i]); | ||||
|         } | ||||
|  | ||||
|         for (i=0;i<selection.nodes.length;i++) { | ||||
|             selection.nodes[i].z = subflow.id; | ||||
|         } | ||||
|  | ||||
|         RED.history.push({ | ||||
|             t:'createSubflow', | ||||
|             nodes:[subflowInstance.id], | ||||
|             links:new_links, | ||||
|             subflow: { | ||||
|                 subflow: subflow | ||||
|             }, | ||||
|  | ||||
|             activeWorkspace: RED.workspaces.active(), | ||||
|             removedLinks: removedLinks, | ||||
|  | ||||
|             dirty:RED.nodes.dirty() | ||||
|         }); | ||||
|  | ||||
|         RED.editor.validateNode(subflow); | ||||
|         RED.nodes.dirty(true); | ||||
|         RED.view.redraw(true); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         createSubflow: createSubflow, | ||||
|         convertToSubflow: convertToSubflow, | ||||
|         refresh: refresh, | ||||
|         removeInput: removeSubflowInput, | ||||
|         removeOutput: removeSubflowOutput | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										141
									
								
								editor/js/ui/tab-config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,141 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2015 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| RED.sidebar.config = (function() { | ||||
|  | ||||
|  | ||||
|     var content = document.createElement("div"); | ||||
|     content.className = "sidebar-node-config" | ||||
|  | ||||
|     $('<div class="palette-category">'+ | ||||
|       '<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i><span data-i18n="sidebar.config.local"></span></div>'+ | ||||
|     '<ul id="workspace-config-node-tray-locals" class="palette-content config-node-list"></ul>'+ | ||||
|     '</div>'+ | ||||
|     '<div class="palette-category">'+ | ||||
|         '<div class="workspace-config-node-tray-header palette-header"><i class="fa fa-angle-down expanded"></i><span data-i18n="sidebar.config.global"></span></div>'+ | ||||
|         '<ul id="workspace-config-node-tray-globals" class="palette-content config-node-list"></ul>'+ | ||||
|     '</div>').appendTo(content); | ||||
|  | ||||
|     function createConfigNodeList(nodes,list) { | ||||
|         nodes.sort(function(A,B) { | ||||
|             if (A.type < B.type) { return -1;} | ||||
|             if (A.type > B.type) { return 1;} | ||||
|             return 0; | ||||
|         }); | ||||
|         list.empty(); | ||||
|         if (nodes.length === 0) { | ||||
|             $('<li class="config_node_none" data-i18n="sidebar.config.none">NONE</li>').i18n().appendTo(list); | ||||
|         } else { | ||||
|             var currentType = ""; | ||||
|             nodes.forEach(function(node) { | ||||
|                 var label = ""; | ||||
|                 if (typeof node._def.label == "function") { | ||||
|                     label = node._def.label.call(node); | ||||
|                 } else { | ||||
|                     label = node._def.label; | ||||
|                 } | ||||
|                 label = label || node.id; | ||||
|                 if (node.type != currentType) { | ||||
|                     $('<li class="config_node_type">'+node.type+'</li>').appendTo(list); | ||||
|                     currentType = node.type; | ||||
|                 } | ||||
|  | ||||
|                 var entry = $('<li class="palette_node config_node"></li>').appendTo(list); | ||||
|                 $('<div class="palette_label"></div>').text(label).appendTo(entry); | ||||
|  | ||||
|                 var iconContainer = $('<div/>',{class:"palette_icon_container  palette_icon_container_right"}).text(node.users.length).appendTo(entry); | ||||
|                 if (node.users.length === 0) { | ||||
|                     entry.addClass("config_node_unused"); | ||||
|                 } | ||||
|                 entry.on('click',function(e) { | ||||
|                     RED.sidebar.info.refresh(node); | ||||
|                 }); | ||||
|                 entry.on('dblclick',function(e) { | ||||
|                     RED.editor.editConfig("", node.type, node.id); | ||||
|                 }); | ||||
|                 var userArray = node.users.map(function(n) { return n.id }); | ||||
|                 entry.on('mouseover',function(e) { | ||||
|                     RED.nodes.eachNode(function(node) { | ||||
|                         if( userArray.indexOf(node.id) != -1) { | ||||
|                             node.highlighted = true; | ||||
|                             node.dirty = true; | ||||
|                         } | ||||
|                     }); | ||||
|                     RED.view.redraw(); | ||||
|                 }); | ||||
|  | ||||
|                 entry.on('mouseout',function(e) { | ||||
|                     RED.nodes.eachNode(function(node) { | ||||
|                         if(node.highlighted) { | ||||
|                             node.highlighted = false; | ||||
|                             node.dirty = true; | ||||
|                         } | ||||
|                     }); | ||||
|                     RED.view.redraw(); | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function refreshConfigNodeList() { | ||||
|  | ||||
|         var localConfigNodes = []; | ||||
|         var globalConfigNodes = []; | ||||
|  | ||||
|         RED.nodes.eachConfig(function(cn) { | ||||
|             if (cn.z == RED.workspaces.active()) { | ||||
|                 localConfigNodes.push(cn); | ||||
|             } else if (!cn.z) { | ||||
|                 globalConfigNodes.push(cn); | ||||
|             } | ||||
|         }); | ||||
|         createConfigNodeList(localConfigNodes,$("#workspace-config-node-tray-locals")); | ||||
|         createConfigNodeList(globalConfigNodes,$("#workspace-config-node-tray-globals")); | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         RED.sidebar.addTab({ | ||||
|             id: "config", | ||||
|             label: RED._("sidebar.config.label"), | ||||
|             name: RED._("sidebar.config.name"), | ||||
|             content: content, | ||||
|             closeable: true, | ||||
|             visible: false, | ||||
|             onchange: function() { refreshConfigNodeList(); } | ||||
|         }); | ||||
|  | ||||
|         $(".workspace-config-node-tray-header").on('click', function(e) { | ||||
|             var icon = $(this).find("i"); | ||||
|             if (icon.hasClass("expanded")) { | ||||
|                 icon.removeClass("expanded"); | ||||
|                 $(this).next().slideUp(); | ||||
|             } else { | ||||
|                 icon.addClass("expanded"); | ||||
|                 $(this).next().slideDown(); | ||||
|             } | ||||
|  | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|     function show() { | ||||
|         refreshConfigNodeList(); | ||||
|         RED.sidebar.show("config"); | ||||
|     } | ||||
|     return { | ||||
|         init:init, | ||||
|         show:show, | ||||
|         refresh:refreshConfigNodeList | ||||
|     } | ||||
| })(); | ||||
| @@ -27,17 +27,24 @@ RED.sidebar.info = (function() { | ||||
|     }); | ||||
| 
 | ||||
|     var content = document.createElement("div"); | ||||
|     content.id = "tab-info"; | ||||
|     content.style.paddingTop = "4px"; | ||||
|     content.style.paddingLeft = "4px"; | ||||
|     content.style.paddingRight = "4px"; | ||||
|     content.className = "sidebar-node-info" | ||||
| 
 | ||||
|     var propertiesExpanded = false; | ||||
|      | ||||
| 
 | ||||
|     function init() { | ||||
|         RED.sidebar.addTab({ | ||||
|             id: "info", | ||||
|             label: RED._("sidebar.info.label"), | ||||
|             name: RED._("sidebar.info.name"), | ||||
|             content: content | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     function show() { | ||||
|         if (!RED.sidebar.containsTab("info")) { | ||||
|             RED.sidebar.addTab("info",content,false); | ||||
|         } | ||||
|         RED.sidebar.show("info"); | ||||
|     } | ||||
| 
 | ||||
| @@ -60,24 +67,24 @@ RED.sidebar.info = (function() { | ||||
| 
 | ||||
|     function refresh(node) { | ||||
|         var table = '<table class="node-info"><tbody>'; | ||||
|         table += '<tr class="blank"><td colspan="2">Node</td></tr>'; | ||||
|         table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>'; | ||||
|         if (node.type != "subflow" && node.name) { | ||||
|             table += "<tr><td>Name</td><td> "+node.name+"</td></tr>"; | ||||
|             table += "<tr><td>"+RED._("common.label.name")+"</td><td> "+node.name+"</td></tr>"; | ||||
|         } | ||||
|         table += "<tr><td>Type</td><td> "+node.type+"</td></tr>"; | ||||
|         table += "<tr><td>ID</td><td> "+node.id+"</td></tr>"; | ||||
|          | ||||
|         table += "<tr><td>"+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>"; | ||||
|         table += "<tr><td>"+RED._("sidebar.info.id")+"</td><td> "+node.id+"</td></tr>"; | ||||
| 
 | ||||
|         var m = /^subflow(:(.+))?$/.exec(node.type); | ||||
|         var subflowNode; | ||||
|         if (m) { | ||||
|             var subflowNode; | ||||
|             if (m[2]) { | ||||
|                 subflowNode = RED.nodes.subflow(m[2]); | ||||
|             } else { | ||||
|                 subflowNode = node; | ||||
|             } | ||||
|              | ||||
|             table += '<tr class="blank"><td colspan="2">Subflow</td></tr>'; | ||||
|              | ||||
| 
 | ||||
|             table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.subflow")+'</td></tr>'; | ||||
| 
 | ||||
|             var userCount = 0; | ||||
|             var subflowType = "subflow:"+subflowNode.id; | ||||
|             RED.nodes.eachNode(function(n) { | ||||
| @@ -85,12 +92,12 @@ RED.sidebar.info = (function() { | ||||
|                     userCount++; | ||||
|                 } | ||||
|             }); | ||||
|             table += "<tr><td>name</td><td>"+subflowNode.name+"</td></tr>"; | ||||
|             table += "<tr><td>instances</td><td>"+userCount+"</td></tr>"; | ||||
|             table += "<tr><td>"+RED._("common.label.name")+"</td><td>"+subflowNode.name+"</td></tr>"; | ||||
|             table += "<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>"; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         if (!m && node.type != "subflow" && node.type != "comment") { | ||||
|             table += '<tr class="blank"><td colspan="2"><a href="#" class="node-info-property-header"><i style="width: 10px; text-align: center;" class="fa fa-caret-'+(propertiesExpanded?"down":"right")+'"></i> Properties</a></td></tr>'; | ||||
|             table += '<tr class="blank"><td colspan="2"><a href="#" class="node-info-property-header"><i style="width: 10px; text-align: center;" class="fa fa-caret-'+(propertiesExpanded?"down":"right")+'"></i> '+RED._("sidebar.info.properties")+'</a></td></tr>'; | ||||
|             if (node._def) { | ||||
|                 for (var n in node._def.defaults) { | ||||
|                     if (n != "name" && node._def.defaults.hasOwnProperty(n)) { | ||||
| @@ -98,7 +105,7 @@ RED.sidebar.info = (function() { | ||||
|                         var type = typeof val; | ||||
|                         if (type === "string") { | ||||
|                             if (val.length === 0) { | ||||
|                                 val += '<span style="font-style: italic; color: #ccc;">blank</span>'; | ||||
|                                 val += '<span style="font-style: italic; color: #ccc;">'+RED._("sidebar.info.blank")+'</span>'; | ||||
|                             } else { | ||||
|                                 if (val.length > 30) { | ||||
|                                     val = val.substring(0,30)+" ..."; | ||||
| @@ -114,33 +121,34 @@ RED.sidebar.info = (function() { | ||||
|                                 val += " "+i+": "+vv+"<br/>"; | ||||
|                             } | ||||
|                             if (node[n].length > 10) { | ||||
|                                 val += " ... "+node[n].length+" items<br/>"; | ||||
|                                 val += " ... "+RED._("sidebar.info.arrayItems",{count:node[n].length})+"<br/>"; | ||||
|                             } | ||||
|                             val += "]"; | ||||
|                         } else { | ||||
|                             val = JSON.stringify(val,jsonFilter," "); | ||||
|                             val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|                         } | ||||
|      | ||||
| 
 | ||||
|                         table += '<tr class="node-info-property-row'+(propertiesExpanded?"":" hide")+'"><td>'+n+"</td><td>"+val+"</td></tr>"; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         table += "</tbody></table><hr/>"; | ||||
|         if (node.type != "comment") { | ||||
|         if (!subflowNode && node.type != "comment") { | ||||
|             var helpText = $("script[data-help-name|='"+node.type+"']").html()||""; | ||||
|             table  += '<div class="node-help">'+helpText+"</div>"; | ||||
|         } | ||||
| 
 | ||||
|         if (node._def && node._def.info) { | ||||
|         if (subflowNode) { | ||||
|             table += '<div class="node-help">'+marked(subflowNode.info||"")+'</div>'; | ||||
|         } else if (node._def && node._def.info) { | ||||
|             var info = node._def.info; | ||||
|             table += '<div class="node-help">'+marked(typeof info === "function" ? info.call(node) : info)+'</div>'; | ||||
|             //table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>';
 | ||||
|         } | ||||
| 
 | ||||
|         $("#tab-info").html(table); | ||||
|          | ||||
|         $(content).html(table); | ||||
| 
 | ||||
|         $(".node-info-property-header").click(function(e) { | ||||
|             var icon = $(this).find("i"); | ||||
|             if (icon.hasClass("fa-caret-right")) { | ||||
| @@ -154,16 +162,20 @@ RED.sidebar.info = (function() { | ||||
|                 $(".node-info-property-row").hide(); | ||||
|                 propertiesExpanded = false; | ||||
|             } | ||||
|              | ||||
| 
 | ||||
|             e.preventDefault(); | ||||
|         }); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function clear() { | ||||
|         $("#tab-info").html(""); | ||||
|         $(content).html(""); | ||||
|     } | ||||
|      | ||||
|     RED.view.on("selection-changed",function(selection) { | ||||
| 
 | ||||
|     function set(html) { | ||||
|         $(content).html(html); | ||||
|     } | ||||
| 
 | ||||
|     RED.events.on("view:selection-changed",function(selection) { | ||||
|         if (selection.nodes) { | ||||
|             if (selection.nodes.length == 1) { | ||||
|                 var node = selection.nodes[0]; | ||||
| @@ -184,8 +196,10 @@ RED.sidebar.info = (function() { | ||||
|     }); | ||||
| 
 | ||||
|     return { | ||||
|         init: init, | ||||
|         show: show, | ||||
|         refresh:refresh, | ||||
|         clear: clear | ||||
|         clear: clear, | ||||
|         set: set | ||||
|     } | ||||
| })(); | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2013 IBM Corp. | ||||
|  * 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. | ||||
| @@ -13,58 +13,90 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|   | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| RED.tabs = (function() { | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
|     function createTabs(options) { | ||||
|         var tabs = {}; | ||||
|          | ||||
|         var currentTabWidth; | ||||
|         var currentActiveTabWidth = 0; | ||||
| 
 | ||||
|         var ul = $("#"+options.id) | ||||
|         ul.addClass("red-ui-tabs"); | ||||
|         ul.children().first().addClass("active"); | ||||
|         ul.children().addClass("red-ui-tab"); | ||||
|          | ||||
| 
 | ||||
|         function onTabClick() { | ||||
|             activateTab($(this)); | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         function onTabDblClick() { | ||||
|             if (options.ondblclick) { | ||||
|                 options.ondblclick(tabs[$(this).attr('href').slice(1)]); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         function activateTab(link) { | ||||
|             if (typeof link === "string") { | ||||
|                 link = ul.find("a[href='#"+link+"']"); | ||||
|             } | ||||
|             if (!link.parent().hasClass("active")) { | ||||
|                 ul.children().removeClass("active"); | ||||
|                 ul.children().css({"transition": "width 100ms"}); | ||||
|                 link.parent().addClass("active"); | ||||
|                 if (options.onchange) { | ||||
|                     options.onchange(tabs[link.attr('href').slice(1)]); | ||||
|                 } | ||||
|                 updateTabWidths(); | ||||
|                 setTimeout(function() { | ||||
|                     ul.children().css({"transition": ""}); | ||||
|                 },100); | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         function updateTabWidths() { | ||||
|             var tabs = ul.find("li.red-ui-tab"); | ||||
|             var width = ul.width(); | ||||
|             var tabCount = tabs.size(); | ||||
|             var tabWidth = (width-6-(tabCount*7))/tabCount; | ||||
|             var pct = 100*tabWidth/width; | ||||
|             tabs.css({width:pct+"%"}); | ||||
|             var tabWidth = (width-12-(tabCount*6))/tabCount; | ||||
|             currentTabWidth = 100*tabWidth/width; | ||||
|             currentActiveTabWidth = currentTabWidth+"%"; | ||||
| 
 | ||||
|             if (options.hasOwnProperty("minimumActiveTabWidth")) { | ||||
|                 if (tabWidth < options.minimumActiveTabWidth) { | ||||
|                     tabCount -= 1; | ||||
|                     tabWidth = (width-12-options.minimumActiveTabWidth-(tabCount*6))/tabCount; | ||||
|                     currentTabWidth = 100*tabWidth/width; | ||||
|                     currentActiveTabWidth = options.minimumActiveTabWidth+"px"; | ||||
|                 } else { | ||||
|                     currentActiveTabWidth = 0; | ||||
|                 } | ||||
|             } | ||||
|             tabs.css({width:currentTabWidth+"%"}); | ||||
|             if (tabWidth < 50) { | ||||
|                 ul.find(".red-ui-tab-close").hide(); | ||||
|                 ul.find(".red-ui-tab-icon").hide(); | ||||
|             } else { | ||||
|                 ul.find(".red-ui-tab-close").show(); | ||||
|                 ul.find(".red-ui-tab-icon").show(); | ||||
|             } | ||||
|             if (currentActiveTabWidth !== 0) { | ||||
|                 ul.find("li.red-ui-tab.active").css({"width":options.minimumActiveTabWidth}); | ||||
|                 ul.find("li.red-ui-tab.active .red-ui-tab-close").show(); | ||||
|                 ul.find("li.red-ui-tab.active .red-ui-tab-icon").show(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick); | ||||
|         updateTabWidths(); | ||||
|          | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
|         function removeTab(id) { | ||||
|             var li = ul.find("a[href='#"+id+"']").parent(); | ||||
|             if (li.hasClass("active")) { | ||||
| @@ -81,20 +113,23 @@ RED.tabs = (function() { | ||||
|             delete tabs[id]; | ||||
|             updateTabWidths(); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         return { | ||||
|             addTab: function(tab) { | ||||
|                 tabs[tab.id] = tab; | ||||
|                 var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul); | ||||
|                 var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li); | ||||
|                 link.html(tab.label); | ||||
|                  | ||||
|                 if (tab.icon) { | ||||
|                     $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link); | ||||
|                 } | ||||
|                 $('<span/>').text(tab.label).appendTo(link); | ||||
| 
 | ||||
|                 link.on("click",onTabClick); | ||||
|                 link.on("dblclick",onTabDblClick); | ||||
|                 if (tab.closeable) { | ||||
|                     var closeLink = $("<a/>",{href:"#",class:"red-ui-tab-close"}).appendTo(li); | ||||
|                     closeLink.html('<i class="fa fa-times" />'); | ||||
|                      | ||||
|                     closeLink.append('<i class="fa fa-times" />'); | ||||
| 
 | ||||
|                     closeLink.on("click",function(event) { | ||||
|                         removeTab(tab.id); | ||||
|                     }); | ||||
| @@ -121,13 +156,13 @@ RED.tabs = (function() { | ||||
|                 tabs[id].label = label; | ||||
|                 var tab = ul.find("a[href='#"+id+"']"); | ||||
|                 tab.attr("title",label); | ||||
|                 tab.text(label); | ||||
|                 tab.find("span").text(label); | ||||
|                 updateTabWidths(); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     return { | ||||
|         create: createTabs | ||||
|     } | ||||
							
								
								
									
										257
									
								
								editor/js/ui/workspaces.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,257 @@ | ||||
| /** | ||||
|  * 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.workspaces = (function() { | ||||
|  | ||||
|     var activeWorkspace = 0; | ||||
|     var workspaceIndex = 0; | ||||
|  | ||||
|     function addWorkspace(ws) { | ||||
|         if (ws) { | ||||
|             workspace_tabs.addTab(ws); | ||||
|             workspace_tabs.resize(); | ||||
|         } else { | ||||
|             var tabId = RED.nodes.id(); | ||||
|             do { | ||||
|                 workspaceIndex += 1; | ||||
|                 //TODO: nls of Sheet | ||||
|             } while($("#workspace-tabs a[title='"+RED._('workspace.defaultName',{number:workspaceIndex})+"']").size() !== 0); | ||||
|  | ||||
|             ws = {type:"tab",id:tabId,label:RED._('workspace.defaultName',{number:workspaceIndex})}; | ||||
|             RED.nodes.addWorkspace(ws); | ||||
|             workspace_tabs.addTab(ws); | ||||
|             workspace_tabs.activateTab(tabId); | ||||
|             RED.history.push({t:'add',workspaces:[ws],dirty:RED.nodes.dirty()}); | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
|     } | ||||
|     function deleteWorkspace(ws,force) { | ||||
|         if (workspace_tabs.count() == 1) { | ||||
|             return; | ||||
|         } | ||||
|         var nodes = []; | ||||
|         if (!force) { | ||||
|             nodes = RED.nodes.filterNodes({z:ws.id}); | ||||
|         } | ||||
|         if (force || nodes.length === 0) { | ||||
|             removeWorkspace(ws); | ||||
|             var historyEvent = RED.nodes.removeWorkspace(ws.id); | ||||
|             historyEvent.t = 'delete'; | ||||
|             historyEvent.dirty = RED.nodes.dirty(); | ||||
|             historyEvent.workspaces = [ws]; | ||||
|             RED.history.push(historyEvent); | ||||
|             RED.nodes.dirty(true); | ||||
|         } else { | ||||
|             $( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws); | ||||
|             $( "#node-dialog-delete-workspace-content" ).text(RED._("workspace.delete",{label:ws.label})); | ||||
|             $( "#node-dialog-delete-workspace" ).dialog('open'); | ||||
|         } | ||||
|     } | ||||
|     function showRenameWorkspaceDialog(id) { | ||||
|         var ws = RED.nodes.workspace(id); | ||||
|         $( "#node-dialog-rename-workspace" ).dialog("option","workspace",ws); | ||||
|  | ||||
|         if (workspace_tabs.count() == 1) { | ||||
|             $( "#node-dialog-rename-workspace").next().find(".leftButton") | ||||
|                 .prop('disabled',true) | ||||
|                 .addClass("ui-state-disabled"); | ||||
|         } else { | ||||
|             $( "#node-dialog-rename-workspace").next().find(".leftButton") | ||||
|                 .prop('disabled',false) | ||||
|                 .removeClass("ui-state-disabled"); | ||||
|         } | ||||
|  | ||||
|         $( "#node-input-workspace-name" ).val(ws.label); | ||||
|         $( "#node-dialog-rename-workspace" ).dialog("open"); | ||||
|     } | ||||
|  | ||||
|     var workspace_tabs; | ||||
|     function createWorkspaceTabs(){ | ||||
|         workspace_tabs = RED.tabs.create({ | ||||
|             id: "workspace-tabs", | ||||
|             onchange: function(tab) { | ||||
|                 var event = { | ||||
|                     old: activeWorkspace | ||||
|                 } | ||||
|                 activeWorkspace = tab.id; | ||||
|                 event.workspace = activeWorkspace; | ||||
|                 RED.events.emit("workspace:change",event); | ||||
|                 RED.sidebar.config.refresh(); | ||||
|             }, | ||||
|             ondblclick: function(tab) { | ||||
|                 if (tab.type != "subflow") { | ||||
|                     showRenameWorkspaceDialog(tab.id); | ||||
|                 } else { | ||||
|                     RED.editor.editSubflow(RED.nodes.subflow(tab.id)); | ||||
|                 } | ||||
|             }, | ||||
|             onadd: function(tab) { | ||||
|                 RED.menu.addItem("menu-item-workspace",{ | ||||
|                     id:"menu-item-workspace-menu-"+tab.id.replace(".","-"), | ||||
|                     label:tab.label, | ||||
|                     onselect:function() { | ||||
|                         workspace_tabs.activateTab(tab.id); | ||||
|                     } | ||||
|                 }); | ||||
|                 RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1); | ||||
|             }, | ||||
|             onremove: function(tab) { | ||||
|                 RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1); | ||||
|                 RED.menu.removeItem("menu-item-workspace-menu-"+tab.id.replace(".","-")); | ||||
|             }, | ||||
|             minimumActiveTabWidth: 150 | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         $("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();}); | ||||
|         $( "#node-dialog-rename-workspace" ).dialog({ | ||||
|             modal: true, | ||||
|             autoOpen: false, | ||||
|             width: 500, | ||||
|             title: RED._("workspace.renameSheet"), | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     class: 'leftButton', | ||||
|                     text: RED._("common.label.delete"), | ||||
|                     click: function() { | ||||
|                         var workspace = $(this).dialog('option','workspace'); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                         deleteWorkspace(workspace); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     click: function() { | ||||
|                         var workspace = $(this).dialog('option','workspace'); | ||||
|                         var label = $( "#node-input-workspace-name" ).val(); | ||||
|                         if (workspace.label != label) { | ||||
|                             workspace_tabs.renameTab(workspace.id,label); | ||||
|                             RED.nodes.dirty(true); | ||||
|                             $("#menu-item-workspace-menu-"+workspace.id.replace(".","-")).text(label); | ||||
|                             // TODO: update entry in menu | ||||
|                         } | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             open: function(e) { | ||||
|                 RED.keyboard.disable(); | ||||
|             }, | ||||
|             close: function(e) { | ||||
|                 RED.keyboard.enable(); | ||||
|             } | ||||
|         }); | ||||
|         $( "#node-dialog-delete-workspace" ).dialog({ | ||||
|             modal: true, | ||||
|             autoOpen: false, | ||||
|             width: 500, | ||||
|             title: RED._("workspace.confirmDelete"), | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     click: function() { | ||||
|                         var workspace = $(this).dialog('option','workspace'); | ||||
|                         deleteWorkspace(workspace,true); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             open: function(e) { | ||||
|                 RED.keyboard.disable(); | ||||
|             }, | ||||
|             close: function(e) { | ||||
|                 RED.keyboard.enable(); | ||||
|             } | ||||
|  | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         createWorkspaceTabs(); | ||||
|         $('#btn-workspace-add-tab').on("click",function(e) {addWorkspace(); e.preventDefault()}); | ||||
|         RED.events.on("sidebar:resize",workspace_tabs.resize); | ||||
|  | ||||
|         RED.menu.setAction('menu-item-workspace-delete',function() { | ||||
|             deleteWorkspace(RED.nodes.workspace(activeWorkspace)); | ||||
|         }); | ||||
|  | ||||
|         $(window).resize(function() { | ||||
|             workspace_tabs.resize(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function removeWorkspace(ws) { | ||||
|         if (!ws) { | ||||
|             deleteWorkspace(RED.nodes.workspace(activeWorkspace)); | ||||
|         } else { | ||||
|             if (workspace_tabs.contains(ws.id)) { | ||||
|                 workspace_tabs.removeTab(ws.id); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         add: addWorkspace, | ||||
|         remove: removeWorkspace, | ||||
|  | ||||
|         edit: function(id) { | ||||
|             showRenameWorkspaceDialog(id||activeWorkspace); | ||||
|         }, | ||||
|         contains: function(id) { | ||||
|             return workspace_tabs.contains(id); | ||||
|         }, | ||||
|         count: function() { | ||||
|             return workspace_tabs.count(); | ||||
|         }, | ||||
|         active: function() { | ||||
|             return activeWorkspace | ||||
|         }, | ||||
|         show: function(id) { | ||||
|             if (!workspace_tabs.contains(id)) { | ||||
|                 var sf = RED.nodes.subflow(id); | ||||
|                 if (sf) { | ||||
|                     addWorkspace({type:"subflow",id:id,icon:"red/images/subflow_tab.png",label:sf.name, closeable: true}); | ||||
|                 } | ||||
|             } | ||||
|             workspace_tabs.activateTab(id); | ||||
|         }, | ||||
|         refresh: function() { | ||||
|             RED.nodes.eachSubflow(function(sf) { | ||||
|                 if (workspace_tabs.contains(sf.id)) { | ||||
|                     workspace_tabs.renameTab(sf.id,sf.name); | ||||
|                 } | ||||
|             }); | ||||
|             RED.sidebar.config.refresh(); | ||||
|         }, | ||||
|         resize: function() { | ||||
|             workspace_tabs.resize(); | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
| @@ -14,15 +14,15 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| RED.user = (function() { | ||||
|          | ||||
| 
 | ||||
|     function login(opts,done) { | ||||
|         if (typeof opts == 'function') { | ||||
|             done = opts; | ||||
|             opts = {}; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         var dialog = $('<div id="node-dialog-login" class="hide">'+ | ||||
|                        '<div style="display: inline-block;width: 250px; vertical-align: top; margin-right: 10px; margin-bottom: 20px;"><img src="node-red-256.png"/></div>'+ | ||||
|                        '<div style="display: inline-block;width: 250px; vertical-align: top; margin-right: 10px; margin-bottom: 20px;"><img id="node-dialog-login-image" src=""/></div>'+ | ||||
|                        '<div style="display: inline-block; width: 250px; vertical-align: bottom; margin-left: 10px; margin-bottom: 20px;">'+ | ||||
|                        '<form id="node-dialog-login-fields" class="form-horizontal" style="margin-bottom: 0px;"></form>'+ | ||||
|                        '</div>'+ | ||||
| @@ -37,7 +37,7 @@ RED.user = (function() { | ||||
|             resizable: false, | ||||
|             draggable: false | ||||
|         }); | ||||
|          | ||||
| 
 | ||||
|         $("#node-dialog-login-fields").empty(); | ||||
|         $.ajax({ | ||||
|             dataType: "json", | ||||
| @@ -45,25 +45,48 @@ RED.user = (function() { | ||||
|             success: function(data) { | ||||
|                 if (data.type == "credentials") { | ||||
|                     var i=0; | ||||
| 
 | ||||
|                     if (data.image) { | ||||
|                         $("#node-dialog-login-image").attr("src",data.image); | ||||
|                     } else { | ||||
|                         $("#node-dialog-login-image").attr("src","red/images/node-red-256.png"); | ||||
|                     } | ||||
|                     for (;i<data.prompts.length;i++) { | ||||
|                         var field = data.prompts[i]; | ||||
|                         var row = $("<div/>",{class:"form-row"}); | ||||
|                         var row = $("<div/>",{id:"rrr"+i,class:"form-row"}); | ||||
|                         $('<label for="node-dialog-login-'+field.id+'">'+field.label+':</label><br/>').appendTo(row); | ||||
|                         $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row); | ||||
|                         var input = $('<input style="width: 100%" id="node-dialog-login-'+field.id+'" type="'+field.type+'" tabIndex="'+(i+1)+'"/>').appendTo(row); | ||||
| 
 | ||||
|                         if (i<data.prompts.length-1) { | ||||
|                             input.keypress( | ||||
|                                 (function() { | ||||
|                                     var r = row; | ||||
|                                     return function(event) { | ||||
|                                         if (event.keyCode == 13) { | ||||
|                                             r.next("div").find("input").focus(); | ||||
|                                             event.preventDefault(); | ||||
|                                         } | ||||
|                                     } | ||||
|                                 })() | ||||
|                             ); | ||||
|                         } | ||||
|                         row.appendTo("#node-dialog-login-fields"); | ||||
|                     } | ||||
|                     $('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">Login failed</span><img src="spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+ | ||||
|                         (opts.cancelable?'<a href="#" id="node-dialog-login-cancel" style="margin-right: 20px;" tabIndex="'+(i+1)+'">Cancel</a>':'')+ | ||||
|                         '<a href="#" id="node-dialog-login-submit" tabIndex="'+(i+2)+'">Login</a></div>').appendTo("#node-dialog-login-fields"); | ||||
|                     $("#node-dialog-login-submit").button().click(function( event ) { | ||||
|                     $('<div class="form-row" style="text-align: right; margin-top: 10px;"><span id="node-dialog-login-failed" style="line-height: 2em;float:left;" class="hide">'+RED._("user.loginFailed")+'</span><img src="red/images/spin.svg" style="height: 30px; margin-right: 10px; " class="login-spinner hide"/>'+ | ||||
|                         (opts.cancelable?'<a href="#" id="node-dialog-login-cancel" style="margin-right: 20px;" tabIndex="'+(i+1)+'">'+RED._("common.label.cancel")+'</a>':'')+ | ||||
|                         '<input type="submit" id="node-dialog-login-submit" style="width: auto;" tabIndex="'+(i+2)+'" value="'+RED._("user.login")+'"></div>').appendTo("#node-dialog-login-fields"); | ||||
| 
 | ||||
| 
 | ||||
|                     $("#node-dialog-login-submit").button(); | ||||
|                     $("#node-dialog-login-fields").submit(function(event) { | ||||
|                         $("#node-dialog-login-submit").button("option","disabled",true); | ||||
|                         $("#node-dialog-login-failed").hide(); | ||||
|                         $(".login-spinner").show(); | ||||
|                          | ||||
| 
 | ||||
|                         var body = { | ||||
|                             client_id: "node-red-editor", | ||||
|                             grant_type: "password", | ||||
|                             scope:"*" | ||||
|                             scope:"" | ||||
|                         } | ||||
|                         for (var i=0;i<data.prompts.length;i++) { | ||||
|                             var field = data.prompts[i]; | ||||
| @@ -93,7 +116,7 @@ RED.user = (function() { | ||||
|                     } | ||||
|                 } | ||||
|                 dialog.dialog("open"); | ||||
|             }      | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @@ -108,17 +131,17 @@ RED.user = (function() { | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     function updateUserMenu() { | ||||
|         $("#btn-usermenu-submenu li").remove(); | ||||
|         $("#usermenu-submenu li").remove(); | ||||
|         if (RED.settings.user.anonymous) { | ||||
|             RED.menu.addItem("btn-usermenu",{ | ||||
|                 id:"btn-login", | ||||
|                 label:"Login", | ||||
|                 id:"usermenu-item-login", | ||||
|                 label:RED._("menu.label.login"), | ||||
|                 onselect: function() { | ||||
|                     RED.user.login({cancelable:true},function() { | ||||
|                         RED.settings.load(function() { | ||||
|                             RED.notify("Logged in as "+RED.settings.user.username,"success"); | ||||
|                             RED.notify(RED._("user.loggedInAs",{name:RED.settings.user.username}),"success"); | ||||
|                             updateUserMenu(); | ||||
|                         }); | ||||
|                     }); | ||||
| @@ -126,33 +149,36 @@ RED.user = (function() { | ||||
|             }); | ||||
|         } else { | ||||
|             RED.menu.addItem("btn-usermenu",{ | ||||
|                 id:"btn-username", | ||||
|                 id:"usermenu-item-username", | ||||
|                 label:"<b>"+RED.settings.user.username+"</b>" | ||||
|             }); | ||||
|             RED.menu.addItem("btn-usermenu",{ | ||||
|                 id:"btn-logout", | ||||
|                 label:"Logout", | ||||
|                 id:"usermenu-item-logout", | ||||
|                 label:RED._("menu.label.logout"), | ||||
|                 onselect: function() { | ||||
|                     RED.user.logout(); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     function init() { | ||||
|         if (RED.settings.user) { | ||||
|             $('<li><a id="btn-usermenu" class="button hide" data-toggle="dropdown" href="#"><i class="fa fa-user"></i></a></li>') | ||||
|                 .prependTo(".header-toolbar"); | ||||
|             if (!RED.settings.editorTheme || !RED.settings.editorTheme.hasOwnProperty("userMenu")) { | ||||
| 
 | ||||
|             RED.menu.init({id:"btn-usermenu", | ||||
|                 options: [] | ||||
|             }); | ||||
|             updateUserMenu(); | ||||
|                 $('<li><a id="btn-usermenu" class="button hide" data-toggle="dropdown" href="#"><i class="fa fa-user"></i></a></li>') | ||||
|                     .prependTo(".header-toolbar"); | ||||
| 
 | ||||
|                 RED.menu.init({id:"btn-usermenu", | ||||
|                     options: [] | ||||
|                 }); | ||||
|                 updateUserMenu(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|     } | ||||
|     return { | ||||
|         init: init, | ||||
							
								
								
									
										21
									
								
								editor/sass/bootstrap.scss
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| .popover-title { display: none; } | ||||
|  | ||||
| div.btn-group, a.btn { | ||||
|    @include disable-selection; | ||||
| } | ||||
							
								
								
									
										45
									
								
								editor/sass/colors.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,45 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| $form-placeholder-color: #bbbbbb; | ||||
| $form-text-color: #444; | ||||
| $form-input-focus-color:  rgba(85,150,230,0.8); | ||||
| $form-input-border-color:  #ccc; | ||||
|  | ||||
|  | ||||
| $node-selected-color: #ff7f0e; | ||||
| $port-selected-color: #ff7f0e; | ||||
| $link-color: #888; | ||||
| $link-subflow-color: #bbb; | ||||
| $link-unknown-color: #f00; | ||||
|  | ||||
| $primary-border-color: #bbbbbb; | ||||
| $secondary-border-color: #dddddd; | ||||
|  | ||||
| $tab-background-active: #fff; | ||||
| $tab-background-inactive: #f0f0f0; | ||||
| $tab-background-hover: #ddd; | ||||
|  | ||||
| $palette-header-background: #f3f3f3; | ||||
|  | ||||
| $workspace-button-background: #fff; | ||||
| $workspace-button-background-hover: #ddd; | ||||
| $workspace-button-background-active: #efefef; | ||||
| $workspace-button-color: #999; | ||||
| $workspace-button-color-disabled: #ccc; | ||||
| $workspace-button-color-focus: #999; | ||||
| $workspace-button-color-hover: #666; | ||||
| $workspace-button-color-active: #666; | ||||
							
								
								
									
										37
									
								
								editor/sass/dragdrop.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| #dropTarget { | ||||
|     position: absolute; | ||||
|     top: 0; bottom: 0; | ||||
|     left: 0; right: 0; | ||||
|     background: rgba(0,0,0,0.1); | ||||
|     display:table; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     display: none; | ||||
| } | ||||
| #dropTarget div { | ||||
|     display: table-cell; | ||||
|     vertical-align: middle; | ||||
|     text-align: center; | ||||
|     font-size: 40px; | ||||
|     color: #fff; | ||||
| } | ||||
| #dropTarget div i { | ||||
|     font-size: 80px; | ||||
| } | ||||
|  | ||||
							
								
								
									
										84
									
								
								editor/sass/dropdownMenu.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,84 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
|  | ||||
| .dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus, .dropdown-submenu:hover>a, .dropdown-submenu:focus>a { | ||||
|     background: #999; | ||||
| } | ||||
|  | ||||
| .dropdown-menu * .fa-check-square { | ||||
|     display: none; | ||||
|     color: #e0e0e0; | ||||
|     margin-left: -25px; | ||||
|     margin-top: 3px; | ||||
| } | ||||
| .dropdown-menu * a.active > .fa-check-square { | ||||
|     display: inline-block; | ||||
| } | ||||
| .dropdown-menu * .fa-square { | ||||
|     display: inline-block; | ||||
|     color: #e0e0e0; | ||||
|     margin-left: -25px; | ||||
|     margin-top: 3px; | ||||
| } | ||||
| .dropdown-menu * a.active > .fa-square { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
|  | ||||
| .dropdown-menu>li.disabled>a:hover>[class^="icon-"] { | ||||
|     background-image: url("vendor/bootstrap/img/glyphicons-halflings.png") !important; | ||||
| } | ||||
| /** Fix for unreachable dropdown menu **/ | ||||
| .dropdown-menu  { | ||||
|     border-radius: 0; | ||||
|     width: 200px !important; | ||||
|     margin-left: 0px !important; | ||||
| } | ||||
| .dropdown-menu > li > a > i { | ||||
|     width: 10px; | ||||
|     text-align: center; | ||||
|     margin-left: -8px; | ||||
| } | ||||
| .dropdown-menu > li > a { | ||||
|      padding-left: 38px ; | ||||
|      text-indent: -8px ; | ||||
|      white-space: normal !important; | ||||
| } | ||||
|  | ||||
| .dropdown-submenu>a:after { | ||||
|     display: none; | ||||
| } | ||||
| .dropdown-submenu>a:before { | ||||
|     display: block; | ||||
|     float: left; | ||||
|     width: 0; | ||||
|     height: 0; | ||||
|     margin-top: 5px; | ||||
|     margin-left: -30px; | ||||
|     border-color: transparent; | ||||
|     border-right-color: #e0e0e0; | ||||
|     border-style: solid; | ||||
|     border-width: 5px 5px 5px 0; | ||||
|     content: " "; | ||||
| } | ||||
|  | ||||
| .dropdown-submenu.disabled > a:before { | ||||
|     border-right-color: #444; | ||||
| } | ||||
| .dropdown-submenu.pull-left>.dropdown-menu { | ||||
|     border-radius: 0; | ||||
| } | ||||
							
								
								
									
										106
									
								
								editor/sass/editor.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,106 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| .dialog-form, #dialog-form, #dialog-config-form { | ||||
|     margin: 0; | ||||
|     height: 100%; | ||||
| } | ||||
|  | ||||
| .input-error { | ||||
|     border-color: rgb(214, 97, 95) !important; | ||||
| } | ||||
|  | ||||
| .ui-dialog .ui-dialog-buttonpane button.leftButton { | ||||
|     margin-right: 40px; | ||||
| } | ||||
|  | ||||
| .form-row { | ||||
|     clear: both; | ||||
|     color: $form-text-color; | ||||
|     margin-bottom:12px; | ||||
| } | ||||
| .form-row label { | ||||
|     display: inline-block; | ||||
|     width: 100px; | ||||
| } | ||||
| .form-row input { | ||||
|     width:70%; | ||||
| } | ||||
|  | ||||
| .form-tips { | ||||
|     background: #ffe; | ||||
|     padding: 8px; | ||||
|     border-radius: 2px; | ||||
|     border: 1px solid $secondary-border-color; | ||||
|     max-width: 450px; | ||||
| } | ||||
| .form-tips code { | ||||
|     border: none; | ||||
|     padding: auto; | ||||
| } | ||||
| .form-tips a { | ||||
|     text-decoration: underline; | ||||
| } | ||||
|  | ||||
| .node-text-editor { | ||||
|     border:1px solid #ccc; | ||||
|     border-radius:5px; | ||||
|     overflow: hidden; | ||||
|     font-size: 14px !important; | ||||
|     font-family: monospace !important; | ||||
| } | ||||
|  | ||||
| .editor-button { | ||||
|     @include workspace-button; | ||||
|     height: 34px; | ||||
|     line-height: 32px; | ||||
|     font-size: 13px; | ||||
|     border-radius: 4px; | ||||
|     padding: 0 10px; | ||||
| } | ||||
| .editor-button-small { | ||||
|     height: 20px; | ||||
|     line-height: 18px; | ||||
|     font-size: 10px; | ||||
|     border-radius: 2px; | ||||
|     padding: 0 5px; | ||||
| } | ||||
|  | ||||
| #node-config-dialog-scope-container { | ||||
|     cursor: auto; | ||||
|     float: right; | ||||
|     font-size: 12px !important; | ||||
| } | ||||
| #node-config-dialog-scope-warning { | ||||
|     display: inline-block; | ||||
|     margin-right: 5px; | ||||
|     color: #AD1625; | ||||
|     vertical-align: middle; | ||||
| } | ||||
| #node-config-dialog-scope { | ||||
|     margin: 1px 0 0 0; | ||||
|     padding: 0; | ||||
|     height: 22px; | ||||
|     width: 110px; | ||||
| } | ||||
| #node-config-dialog-user-count { | ||||
|     vertical-align: middle; | ||||
|     display:inline-block; | ||||
|     margin-top: 10px; | ||||
|     margin-right: 20px; | ||||
|     float:left; | ||||
|     font-size: 12px; | ||||
| } | ||||
							
								
								
									
										236
									
								
								editor/sass/flow.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,236 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| .hidden { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| .lasso { | ||||
|     stroke-width: 1px; | ||||
|     stroke: #ff7f0e; | ||||
|     fill: rgba(20,125,255,0.1); | ||||
|     stroke-dasharray: 10 5; | ||||
| } | ||||
|  | ||||
| .group-box { | ||||
|     stroke-width: 1px; | ||||
|     stroke: #aaaaaa; | ||||
|     fill: rgba(208, 211, 238, 0.1); | ||||
|     stroke-dasharray: 3 3; | ||||
| } | ||||
| .group-box-active { | ||||
|     fill: #fff; | ||||
|     stroke: #ff7f0e; | ||||
| } | ||||
|  | ||||
| .group_label { | ||||
|     stroke-width: 0; | ||||
|     fill: #999; | ||||
|     font-size: 11px; | ||||
|     pointer-events: none; | ||||
|     -webkit-touch-callout: none; | ||||
|     @include disable-selection; | ||||
| } | ||||
|  | ||||
| .pull-right { | ||||
|     margin-left: 20px; | ||||
| } | ||||
|  | ||||
| .node_label_italic { | ||||
|     font-style: italic; | ||||
| } | ||||
| .node_label_unknown { | ||||
|     font-style: italic; | ||||
|     fill: #e00 !important; | ||||
| } | ||||
| .node_label_white { | ||||
|     fill: #eee !important; | ||||
| } | ||||
| .node_label { | ||||
|     stroke-width: 0; | ||||
|     fill: #333; | ||||
|     font-size: 14px; | ||||
|     pointer-events: none; | ||||
|     -webkit-touch-callout: none; | ||||
|     @include disable-selection; | ||||
| } | ||||
|  | ||||
| .port_label { | ||||
|     stroke-width: 0; | ||||
|     fill: #888; | ||||
|     font-size: 16px; | ||||
|     alignment-baseline: middle; | ||||
|     text-anchor: middle; | ||||
|     pointer-events: none; | ||||
|     -webkit-touch-callout: none; | ||||
|     @include disable-selection; | ||||
| } | ||||
|  | ||||
|  | ||||
| .function_label { | ||||
|     font-size: 12px; | ||||
| } | ||||
| .node { | ||||
|     stroke: #999; | ||||
|     cursor: move; | ||||
|     stroke-width: 1; | ||||
| } | ||||
| .node_unknown { | ||||
|     stroke-dasharray:10,4; | ||||
|     stroke: #f33; | ||||
| } | ||||
| .tool_arrow { | ||||
|     stroke-width: 1; | ||||
|     stroke: #999; | ||||
|     fill: #999; | ||||
|     cursor: pointer; | ||||
| } | ||||
| .node_tools { | ||||
|     fill: #ddd; | ||||
|     stroke: #999; | ||||
|     cursor: move; | ||||
|     stroke-width: 1; | ||||
|     cursor: pointer; | ||||
| } | ||||
| .node_tools_hovered { | ||||
|     stroke: #ff7f0e; | ||||
|     fill: #eee; | ||||
| } | ||||
|  | ||||
| .node_button { | ||||
|     fill: inherit; | ||||
|  | ||||
| } | ||||
| .port { | ||||
|     stroke: #999; | ||||
|     stroke-width: 1; | ||||
|     fill: #ddd; | ||||
|     cursor: crosshair; | ||||
| } | ||||
|  | ||||
| .port_highlight { | ||||
|     stroke: #6DA332; | ||||
|     stroke-width: 3; | ||||
|     fill: #fff; | ||||
|     pointer-events:none; | ||||
|     fill-opacity: 0.5; | ||||
| } | ||||
|  | ||||
|  | ||||
| .node_error { | ||||
|     stroke: #ff0000; | ||||
|     stroke-width: 2; | ||||
|     fill: #ff7f0e; | ||||
| } | ||||
|  | ||||
| .node_badge { | ||||
|     stroke: rgb(93, 114, 145); | ||||
|     stroke-width: 1; | ||||
|     fill: rgb(190, 209, 255); | ||||
| } | ||||
| .node_badge_label { | ||||
|     stroke-width:0; | ||||
|     fill: #fff; | ||||
|     font-size: 11px; | ||||
|     pointer-events: none; | ||||
|     -webkit-touch-callout: none; | ||||
|     @include disable-selection; | ||||
|  | ||||
| } | ||||
| .node_invalid { | ||||
|     stroke: #ff0000; | ||||
| } | ||||
| .node_selected { | ||||
|     stroke-width: 2; | ||||
|     stroke: $node-selected-color !important; | ||||
| } | ||||
| .node_highlighted { | ||||
|     stroke: #dd1616; | ||||
|     stroke-width: 2; | ||||
|     stroke-dasharray: 10, 4; | ||||
| } | ||||
| .node_hovered { | ||||
| } | ||||
|  | ||||
| .node_status_label { | ||||
|     @include disable-selection; | ||||
|     stroke-width: 0; | ||||
|     fill: #888; | ||||
|     font-size:9pt; | ||||
|     stroke:#000; | ||||
|     text-anchor:start; | ||||
| } | ||||
|  | ||||
| .port_hovered { | ||||
|     stroke: $port-selected-color; | ||||
|     fill:  $port-selected-color; | ||||
| } | ||||
| .subflowport { | ||||
|     stroke-dasharray: 5,5; | ||||
|     fill: #eee; | ||||
|     stroke: #999; | ||||
| } | ||||
|  | ||||
| .drag_line { | ||||
|     stroke: $node-selected-color; | ||||
|     stroke-width: 4; | ||||
|     fill: none; | ||||
|     pointer-events: none; | ||||
| } | ||||
|  | ||||
| .drag_line_hidden { | ||||
|     stroke: $node-selected-color; | ||||
|     stroke-width: 0; | ||||
|     pointer-events: none; | ||||
|     fill: none; | ||||
| } | ||||
|  | ||||
| .link_line { | ||||
|     stroke: $link-color; | ||||
|     stroke-width: 3; | ||||
|     fill: none; | ||||
|     pointer-events: none; | ||||
| } | ||||
|  | ||||
| .link_subflow { | ||||
|     stroke: $link-subflow-color; | ||||
|     stroke-dasharray: 10,5; | ||||
|     stroke-width: 2; | ||||
| } | ||||
|  | ||||
| .link_outline { | ||||
|     stroke: #fff; | ||||
|     stroke-width: 4; | ||||
|     cursor: crosshair; | ||||
|     fill: none; | ||||
|     pointer-events: none; | ||||
| } | ||||
| .link_background { | ||||
|     stroke: #fff; | ||||
|     opacity: 0; | ||||
|     stroke-width: 20; | ||||
|     cursor: crosshair; | ||||
|     fill: none; | ||||
| } | ||||
|  | ||||
| g.link_selected path.link_line { | ||||
|     stroke: $node-selected-color; | ||||
| } | ||||
| g.link_unknown path.link_line { | ||||
|     stroke: $link-unknown-color; | ||||
|     stroke-width: 2; | ||||
|     stroke-dasharray: 10, 4; | ||||
| } | ||||
							
								
								
									
										1046
									
								
								editor/sass/forms.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										266
									
								
								editor/sass/header.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,266 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| $activeButton: #121212; | ||||
|  | ||||
| $deployButton: #8C101C; | ||||
| $deployButtonHover: #6E0A1E; | ||||
| $deployButtonActive: #4C0A17; | ||||
|  | ||||
| $deployDisabledButton: #444; | ||||
| $deployDisabledButtonHover: #555; | ||||
| $deployDisabledButtonActive: #444; | ||||
|  | ||||
| $headerMenuBackground: #121212; | ||||
| $headerMenuItemHover: #323232; | ||||
| $headerMenuItemDivider: #464646; | ||||
|  | ||||
| #header { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     width: 100%; | ||||
|     height: 40px; | ||||
|     background: #000; | ||||
|     box-sizing: border-box; | ||||
|     padding: 0px 0px 0px 20px; | ||||
|     color: #C7C7C7; | ||||
|     font-size: 14px; | ||||
| } | ||||
|  | ||||
| span.logo { | ||||
|     float: left; | ||||
|     margin-top: 5px; | ||||
|     font-size: 30px; | ||||
|     line-height: 30px; | ||||
|     text-decoration: none; | ||||
|  | ||||
|     span { | ||||
|         vertical-align: middle; | ||||
|         font-size: 16px !important; | ||||
|     } | ||||
|     img { | ||||
|         height: 18px; | ||||
|     } | ||||
|  | ||||
|     a { | ||||
|         color: inherit; | ||||
|         &:hover { | ||||
|             text-decoration: none; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| .header-toolbar { | ||||
|     padding: 0; | ||||
|     margin: 0; | ||||
|     list-style: none; | ||||
|     float: right; | ||||
|  | ||||
|     > li { | ||||
|         display: inline-block; | ||||
|         padding: 0; | ||||
|         margin: 0; | ||||
|         position: relative; | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| .button { | ||||
|     @include disable-selection; | ||||
| } | ||||
|  | ||||
| #header .button { | ||||
|     min-width: 20px; | ||||
|     text-align: center; | ||||
|     line-height: 40px; | ||||
|     display: inline-block; | ||||
|     font-size: 20px; | ||||
|     padding: 0px 12px; | ||||
|     text-decoration: none; | ||||
|     color: #C7C7C7; | ||||
|     margin: auto 5px; | ||||
|     vertical-align: middle; | ||||
|     border-left: 2px solid #000; | ||||
|     border-right: 2px solid #000; | ||||
|  | ||||
|     &:hover { | ||||
|         border-color: $headerMenuItemHover; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #header .button-group { | ||||
|     display: inline-block; | ||||
|     margin: auto 15px; | ||||
|     vertical-align: middle; | ||||
|     clear: both; | ||||
| } | ||||
| #header .button-group > a { | ||||
|     display: inline-block; | ||||
|     float: left; | ||||
|     line-height: 22px; | ||||
|     font-size: 14px; | ||||
|     text-decoration: none; | ||||
|     padding: 4px 8px; | ||||
|     margin: 0; | ||||
| } | ||||
|  | ||||
| .deploy-button { | ||||
|     background: $deployButton; | ||||
|     color: #eee !important; | ||||
|  | ||||
|     &:hover { | ||||
|         background: $deployButtonHover; | ||||
|     } | ||||
|  | ||||
|     &:active { | ||||
|         background: $deployButtonActive; | ||||
|         color: #ccc !important; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #btn-deploy { | ||||
|  | ||||
|     padding: 4px 12px; | ||||
|  | ||||
|     &.disabled { | ||||
|         cursor: default; | ||||
|         background: $deployDisabledButton; | ||||
|         color: #999 !important; | ||||
|  | ||||
|         img { | ||||
|             opacity: 0.3; | ||||
|         } | ||||
|  | ||||
|         &+ #btn-deploy-options { | ||||
|             background: $deployDisabledButton; | ||||
|             color: #ddd; | ||||
|         } | ||||
|         &+ #btn-deploy-options:hover { | ||||
|             background: $deployDisabledButtonHover; | ||||
|         } | ||||
|         &+ #btn-deploy-options:active { | ||||
|             background: $deployDisabledButton; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     img { | ||||
|         margin-right: 8px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .deploy-button-group.open { | ||||
|     #btn-deploy-options { | ||||
|         background: $activeButton !important; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #header .button { | ||||
|     &:active, &.active { | ||||
|         background: $activeButton; | ||||
|     } | ||||
|     &:focus { | ||||
|         outline: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #header li.open .button { | ||||
|     background: $activeButton; | ||||
|     border-color: $activeButton; | ||||
| } | ||||
|  | ||||
|  | ||||
| #header ul.dropdown-menu { | ||||
|     background: $headerMenuBackground; | ||||
|     width: 250px !important; | ||||
|     margin-top: 0; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu li a { | ||||
|     color: #C7C7C7; | ||||
|     padding: 3px 40px; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu li a img { | ||||
|     margin-right: 10px; | ||||
|     padding: 4px; | ||||
|     border: 3px solid rgba(0,0,0,0); | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu li a.active img { | ||||
|     border: 3px solid #777677; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu li a span.menu-label-container { | ||||
|     width: 180px; | ||||
|     vertical-align: top; | ||||
|     display: inline-block; | ||||
|     text-indent: 0px; | ||||
| } | ||||
| #header ul.dropdown-menu li a span.menu-label { | ||||
|     font-size: 14px; | ||||
|     display: inline-block; | ||||
|     text-indent: 0px; | ||||
| } | ||||
| #header ul.dropdown-menu li a span.menu-sublabel { | ||||
|     color: #aeaeae; | ||||
|     font-size: 13px; | ||||
|     display: inline-block; | ||||
|     text-indent: 0px; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu > li:hover > a, | ||||
| #header ul.dropdown-menu > li:focus > a { | ||||
|     background: $headerMenuItemHover !important; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu li.divider { | ||||
|     background: $headerMenuItemDivider; | ||||
|     border-bottom-color: $headerMenuItemHover; | ||||
| } | ||||
| #header ul.dropdown-menu li.disabled a { | ||||
|     color: #666; | ||||
| } | ||||
|  | ||||
| #header ul.dropdown-menu > li.disabled:hover > a, | ||||
| #header ul.dropdown-menu > li.disabled:focus > a { | ||||
|     background: none !important; | ||||
| } | ||||
|  | ||||
| /* Deploy menu customisations */ | ||||
| #header ul#btn-deploy-options-submenu { | ||||
|     width: 300px !important; | ||||
| } | ||||
| #header ul#btn-deploy-options-submenu li a span.menu-label { | ||||
|     font-size: 16px; | ||||
|     display: inline-block; | ||||
|     text-indent: 0px; | ||||
| } | ||||
| #header ul#btn-deploy-options-submenu li a { | ||||
|     padding: 10px 30px; | ||||
|     color: #fff; | ||||
| } | ||||
| #header ul#btn-deploy-options-submenu li a > i.fa { | ||||
|     display: none !important; | ||||
| } | ||||
|  | ||||
| /* User menu customisations */ | ||||
| #header ul#btn-usermenu-submenu li a#btn-username > .menu-label { | ||||
|     font-size: 16px; | ||||
|     color: #fff; | ||||
| } | ||||
							
								
								
									
										75
									
								
								editor/sass/jquery.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,75 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| .ui-widget { | ||||
|     font-size: 14px !important; | ||||
|     font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important; | ||||
| } | ||||
| .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { | ||||
|     font-size: 14px !important; | ||||
|     font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif !important; | ||||
| } | ||||
|  | ||||
| /* jQuery Theme overrides */ | ||||
| .ui-tabs .ui-tabs-panel { | ||||
|     padding: 0px; | ||||
| } | ||||
|  | ||||
| .ui-autocomplete { | ||||
|     max-height: 250px; | ||||
|     overflow-x: hidden; | ||||
|     overflow-y: scroll; | ||||
| } | ||||
|  | ||||
| .ui-dialog { | ||||
|     border-radius: 1px; | ||||
|     background: #fff; | ||||
|     padding: 0; | ||||
|     @include component-shadow; | ||||
| } | ||||
| .ui-dialog .ui-dialog-content { | ||||
|     padding: 25px 25px 10px 25px; | ||||
| } | ||||
| .ui-dialog .ui-dialog-title { | ||||
|     width: auto; | ||||
| } | ||||
| .ui-dialog .ui-dialog-titlebar { | ||||
|     padding: 10px; | ||||
|     background: #f3f3f3; | ||||
|     border: none; | ||||
|     border-bottom: 1px solid #999; | ||||
|     border-radius: 0; | ||||
| } | ||||
| .ui-corner-all { | ||||
|     border-radius: 1px; | ||||
| } | ||||
| .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { | ||||
|     background: #f3f3f3; | ||||
| } | ||||
| .ui-dialog-no-close .ui-dialog-titlebar-close { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| .ui-dialog-buttonset { | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { | ||||
|     float: none; | ||||
| } | ||||
| .ui-dialog .ui-dialog-buttonpane { | ||||
|     padding: .3em 1em .5em 1em; | ||||
| } | ||||
							
								
								
									
										40
									
								
								editor/sass/keyboard.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,40 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| #keyboard-help-dialog { | ||||
|     font-size: 0.9em; | ||||
| } | ||||
| .keyboard-shortcuts { | ||||
|     padding: 10px; | ||||
| } | ||||
| .keyboard-shortcuts td  { | ||||
|     padding: 7px 5px; | ||||
|     margin-bottom: 10px; | ||||
|     white-space: pre; | ||||
| } | ||||
| .keyboard-shortcuts td:first-child { | ||||
|     text-align: right; | ||||
|     padding-right: 10px; | ||||
| } | ||||
| .help-key { | ||||
|     border: 1px solid #ddd; | ||||
|     padding: 4px; | ||||
|     border-radius: 3px; | ||||
|     background: #f6f6f6; | ||||
|     font-family: Courier, monospace; | ||||
|     box-shadow: #999 1px 1px 1px; | ||||
| } | ||||
|  | ||||
							
								
								
									
										33
									
								
								editor/sass/library.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,33 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| #node-select-library { | ||||
|     overflow: hidden; | ||||
| } | ||||
| #node-select-library ul { | ||||
|     list-style: none; | ||||
|     padding: 0px; | ||||
|     margin: 2px; | ||||
| } | ||||
| #node-select-library li { | ||||
|     cursor: pointer; | ||||
| } | ||||
| #node-select-library li.list-selected { | ||||
|     background: #eee; | ||||
| } | ||||
| #node-select-library li.list-hover { | ||||
|     background: #ffffd0; | ||||
| } | ||||
							
								
								
									
										90
									
								
								editor/sass/mixins.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,90 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| @mixin disable-selection { | ||||
|     -webkit-user-select: none; | ||||
|     -khtml-user-select: none; | ||||
|     -moz-user-select: none; | ||||
|     -ms-user-select: none; | ||||
|     user-select: none; | ||||
| } | ||||
|  | ||||
| @mixin component-border { | ||||
|    border: 1px solid $primary-border-color; | ||||
|    box-sizing: border-box; | ||||
|  | ||||
| } | ||||
|  | ||||
| @mixin workspace-button { | ||||
|     @include disable-selection; | ||||
|     color: $workspace-button-color; | ||||
|     box-sizing: border-box; | ||||
|     display: inline-block; | ||||
|     background: $workspace-button-background; | ||||
|     border: 1px solid $secondary-border-color; | ||||
|     text-align: center; | ||||
|     margin:0; | ||||
|     text-decoration: none; | ||||
|     cursor:pointer; | ||||
|     &.disabled { | ||||
|         cursor: default; | ||||
|         color: $workspace-button-color-disabled; | ||||
|     } | ||||
|     &:not(.disabled):hover { | ||||
|         text-decoration: none; | ||||
|         color: $workspace-button-color-hover; | ||||
|         background: $workspace-button-background-hover; | ||||
|     } | ||||
|     &:not(.disabled):focus { | ||||
|         color: $workspace-button-color-focus; | ||||
|         text-decoration: none; | ||||
|     } | ||||
|     &:not(.disabled):active { | ||||
|         color: $workspace-button-color-active; | ||||
|         background: $workspace-button-background-active; | ||||
|         text-decoration: none; | ||||
|     } | ||||
| } | ||||
| @mixin component-footer { | ||||
|     border-top: 1px solid $primary-border-color; | ||||
|     background: #f3f3f3; | ||||
|     text-align: right; | ||||
|     position: absolute; | ||||
|     bottom: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     height: 25px; | ||||
|     line-height: 23px; | ||||
|     padding: 0 10px; | ||||
| } | ||||
|  | ||||
| @mixin component-footer-button { | ||||
|     @include workspace-button; | ||||
|     font-size: 11px; | ||||
|     line-height: 17px; | ||||
|     width: 18px; | ||||
|     height: 18px; | ||||
|     &.text-button { | ||||
|         width: auto; | ||||
|         padding: 0 5px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @mixin component-shadow { | ||||
|     border: 1px solid $secondary-border-color; | ||||
|     box-shadow: 1px 1px 4px rgba(0,0,0,0.2); | ||||
|  | ||||
| } | ||||
							
								
								
									
										45
									
								
								editor/sass/notifications.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,45 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| #notifications { | ||||
|     z-index: 10000; | ||||
|     width: 500px; | ||||
|     margin-left: -250px; | ||||
|     left: 50%; | ||||
|     position: absolute; | ||||
|     top: 1px; | ||||
| } | ||||
| .notification { | ||||
|     box-sizing: border-box; | ||||
|     position: relative; | ||||
|     padding: 14px 18px; | ||||
|     margin-bottom: 4px; | ||||
|     box-shadow: 0 1px 1px 1px rgba(0,0,0, 0.15); | ||||
|     background-color: #fff; | ||||
|     color: #666; | ||||
|     border: 1px solid #325C80; | ||||
|     border-left-width: 16px; | ||||
| } | ||||
|  | ||||
| .notification-success { | ||||
|     border-color: #4B8400; | ||||
| } | ||||
| .notification-warning { | ||||
|     border-color: #D74108; | ||||
| } | ||||
| .notification-error { | ||||
|     border-color: #AD1625; | ||||
| } | ||||
							
								
								
									
										212
									
								
								editor/sass/palette.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,212 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
|  | ||||
| #palette { | ||||
|     position: absolute; | ||||
|     top: 0px; | ||||
|     bottom: 0px; | ||||
|     left:0px; | ||||
|     background: #f3f3f3; | ||||
|     width: 180px; | ||||
|     text-align: center; | ||||
|     @include disable-selection; | ||||
|     @include component-border; | ||||
|  | ||||
| } | ||||
| .palette-scroll { | ||||
|     display: none; | ||||
|     position: absolute; | ||||
|     top: 35px; | ||||
|     right: 0; | ||||
|     bottom: 25px; | ||||
|     left:0; | ||||
|     padding: 0; | ||||
|     overflow-y: auto; | ||||
|     box-sizing:border-box; | ||||
| } | ||||
| .palette-spinner { | ||||
|     padding-top: 40px; | ||||
| } | ||||
| #palette-search { | ||||
|     position: absolute; | ||||
|     display: none; | ||||
|     top: 0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     overflow: hidden; | ||||
|     background: #ffffff; | ||||
|     text-align: center; | ||||
|     height: 35px; | ||||
|     padding: 3px; | ||||
|     border-bottom: 1px solid $primary-border-color; | ||||
|     box-sizing:border-box; | ||||
| } | ||||
| #palette-search i { | ||||
|     font-size: 10px; | ||||
|     color: #666; | ||||
| } | ||||
| #palette-search i.fa-search { | ||||
|     position: absolute; | ||||
|     pointer-events: none; | ||||
|     left: 12px; | ||||
|     top: 12px; | ||||
| } | ||||
| #palette-search i.fa-times { | ||||
|     position: absolute; | ||||
|     right: 7px; | ||||
|     top: 12px; | ||||
| } | ||||
|  | ||||
| #palette-search-clear { | ||||
|     position: absolute; | ||||
|     right: 0; | ||||
|     top: 0; | ||||
|     bottom: 0; | ||||
|     width: 20px; | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| #palette-search input { | ||||
|     border-radius: 0; | ||||
|     border: none; | ||||
|     width: 100%; | ||||
|     box-shadow: none; | ||||
|     -webkit-box-shadow: none; | ||||
|     padding: 3px 17px 3px 22px; | ||||
|     margin: 0px; | ||||
|     height: 30px; | ||||
|     box-sizing:border-box; | ||||
| } | ||||
|  | ||||
| #palette-search input:focus { | ||||
|     border: none; | ||||
|     box-shadow: none; | ||||
|     -webkit-box-shadow: none; | ||||
| } | ||||
| #palette-footer { | ||||
|     @include component-footer; | ||||
| } | ||||
| .palette-button { | ||||
|     @include component-footer-button; | ||||
| } | ||||
|  | ||||
|  | ||||
| .palette-category { | ||||
|     border-bottom: 1px solid #ccc; | ||||
| } | ||||
| .palette-content { | ||||
|     background: #fff; | ||||
|     padding: 3px; | ||||
| } | ||||
|  | ||||
| .palette-header { | ||||
|     background: $palette-header-background; | ||||
|     cursor: pointer; | ||||
|     text-align: left; | ||||
|     padding: 9px; | ||||
|     font-weight: bold; | ||||
| } | ||||
| .palette-header i { | ||||
|     margin: 3px 10px 3px 3px; | ||||
|     -webkit-transition: all 0.2s ease-in-out; | ||||
|     -moz-transition: all 0.2s ease-in-out; | ||||
|     -o-transition: all 0.2s ease-in-out; | ||||
|     -webkit-transform: rotate(-90deg); | ||||
|     -moz-transform: rotate(-90deg); | ||||
|     -o-transform: rotate(-90deg); | ||||
| } | ||||
| .palette-header i.expanded { | ||||
|     -webkit-transform: rotate(0deg); | ||||
|     -moz-transform: rotate(0deg); | ||||
|     -o-transform: rotate(0deg); | ||||
| } | ||||
| .palette-header span { | ||||
|     clear: both; | ||||
| } | ||||
| .palette_label { | ||||
|     font-size: 13px; | ||||
|     margin: 4px 0 4px 28px; | ||||
|     line-height: 20px; | ||||
|     overflow: hidden; | ||||
|     text-align: center; | ||||
| } | ||||
| .palette_label_right { | ||||
|     margin: 4px 28px 4px 0; | ||||
| } | ||||
|  | ||||
| .palette_node { | ||||
|     cursor:move; | ||||
|     background: #ddd; | ||||
|     margin: 10px auto; | ||||
|     height: 25px; | ||||
|     border-radius: 5px; | ||||
|     border: 1px solid #999; | ||||
|     background-position: 5% 50%; | ||||
|     background-repeat: no-repeat; | ||||
|     width: 120px; | ||||
|     background-size: contain; | ||||
|     position: relative; | ||||
| } | ||||
| .palette_node:hover { | ||||
|     border-color: $node-selected-color; | ||||
|     background-color: #eee; | ||||
| } | ||||
| .palette_port { | ||||
|     position: absolute; | ||||
|     top:8px; | ||||
|     left: -5px; | ||||
|     box-sizing: border-box; | ||||
|     -moz-box-sizing: border-box; | ||||
|     background:#d9d9d9; | ||||
|     border-radius: 3px; | ||||
|     width: 10px; | ||||
|     height: 10px; | ||||
|     border: 1px solid #999; | ||||
| } | ||||
| .palette_port_output { | ||||
|     left:auto; | ||||
|     right: -6px; | ||||
| } | ||||
|  | ||||
| .palette_node:hover .palette_port { | ||||
|     border-color: #999; | ||||
|     background-color: #eee; | ||||
| } | ||||
| .palette_icon_container { | ||||
|     position: absolute; | ||||
|     text-align: center; | ||||
|     top:0; | ||||
|     bottom:0; | ||||
|     left:0; | ||||
|     width: 30px; | ||||
|     border-right: 1px solid rgba(0,0,0,0.1); | ||||
|     background-color: rgba(0,0,0,0.05); | ||||
| } | ||||
| .palette_icon_container_right { | ||||
|     left: auto; | ||||
|     right: 0; | ||||
|     border-right: none; | ||||
|     border-left: 1px solid rgba(0,0,0,0.1); | ||||
| } | ||||
| .palette_icon { | ||||
|     display: inline-block; | ||||
|     width: 20px; | ||||
|     height: 100%; | ||||
|     background-position: 50% 50%; | ||||
|     background-size: contain; | ||||
|     background-repeat: no-repeat; | ||||
| } | ||||
							
								
								
									
										53
									
								
								editor/sass/popover.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | ||||
| /** | ||||
|  * 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-ui-popover { | ||||
|      display: none; | ||||
|      position: absolute; | ||||
|      width: 300px; | ||||
|      padding: 10px; | ||||
|      height: auto; | ||||
|      background: #fff; | ||||
|       | ||||
|      z-index: 1000; | ||||
|      font-size: 14px; | ||||
|      line-height: 1.4em; | ||||
|      @include component-shadow; | ||||
|  } | ||||
|  .red-ui-popover:after, .red-ui-popover:before { | ||||
|  	right: 100%; | ||||
|  	top: 50%; | ||||
|  	border: solid transparent; | ||||
|  	content: " "; | ||||
|  	height: 0; | ||||
|  	width: 0; | ||||
|  	position: absolute; | ||||
|  	pointer-events: none; | ||||
|  } | ||||
|  | ||||
|  .red-ui-popover:after { | ||||
|  	border-color: rgba(136, 183, 213, 0); | ||||
|  	border-right-color: #fff; | ||||
|  	border-width: 10px; | ||||
|  	margin-top: -10px; | ||||
|  } | ||||
|  .red-ui-popover:before { | ||||
|  	border-color: rgba(194, 225, 245, 0); | ||||
|  	border-right-color: $primary-border-color; | ||||
|  	border-width: 11px; | ||||
|  	margin-top: -11px; | ||||
|  } | ||||
							
								
								
									
										68
									
								
								editor/sass/sidebar.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,68 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| #sidebar { | ||||
|     position: absolute; | ||||
|     top: 0px; | ||||
|     right: 0px; | ||||
|     bottom: 0px; | ||||
|     width: 315px; | ||||
|     background: #fff; | ||||
|     box-sizing: border-box; | ||||
|     @include component-border; | ||||
| } | ||||
|  | ||||
| #sidebar.closing { | ||||
|     background: #eee; | ||||
|     border-color: #900; | ||||
|     border-style: dashed; | ||||
| } | ||||
|  | ||||
| #sidebar-content { | ||||
|     position: absolute; | ||||
|     top: 35px; | ||||
|     right: 0; | ||||
|     bottom: 25px; | ||||
|     left: 0px; | ||||
|     padding-top: 3px; | ||||
|     overflow-y: auto; | ||||
| } | ||||
|  | ||||
| #sidebar-separator { | ||||
|     position: absolute; | ||||
|     top: 5px; | ||||
|     right: 315px; | ||||
|     bottom:10px; | ||||
|     width: 7px; | ||||
|     background: url(images/grip.png) no-repeat 50% 50%; | ||||
|     cursor: col-resize; | ||||
| } | ||||
|  | ||||
| .sidebar-closed > #sidebar { display: none; } | ||||
| .sidebar-closed > #sidebar-separator { right: 0px !important; } | ||||
| .sidebar-closed > #workspace { right: 7px !important; } | ||||
|  | ||||
| #sidebar .button { | ||||
|     @include workspace-button; | ||||
|     line-height: 18px; | ||||
|     font-size: 12px; | ||||
|     margin-right: 5px; | ||||
|     padding: 2px 8px; | ||||
| } | ||||
|  | ||||
| #sidebar-footer { | ||||
|     @include component-footer; | ||||
| } | ||||