mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	| @@ -16,7 +16,6 @@ node_js: | ||||
|   - "7" | ||||
|   - "6" | ||||
|   - "4" | ||||
|   - "0.10" | ||||
| script: | ||||
|   - istanbul cover ./node_modules/.bin/grunt --report lcovonly && istanbul report text && ( cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true ) && rm -rf coverage | ||||
| before_script: | ||||
|   | ||||
							
								
								
									
										112
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								Gruntfile.js
									
									
									
									
									
								
							| @@ -98,7 +98,7 @@ module.exports = function(grunt) { | ||||
|                 src: [ | ||||
|                     // Ensure editor source files are concatenated in | ||||
|                     // the right order | ||||
|                     "editor/js/main.js", | ||||
|                     "editor/js/red.js", | ||||
|                     "editor/js/events.js", | ||||
|                     "editor/js/i18n.js", | ||||
|                     "editor/js/settings.js", | ||||
| @@ -110,13 +110,16 @@ module.exports = function(grunt) { | ||||
|                     "editor/js/nodes.js", | ||||
|                     "editor/js/history.js", | ||||
|                     "editor/js/validators.js", | ||||
|                     "editor/js/ui/utils.js", | ||||
|                     "editor/js/ui/common/editableList.js", | ||||
|                     "editor/js/ui/common/menu.js", | ||||
|                     "editor/js/ui/common/popover.js", | ||||
|                     "editor/js/ui/common/searchBox.js", | ||||
|                     "editor/js/ui/common/tabs.js", | ||||
|                     "editor/js/ui/common/typedInput.js", | ||||
|                     "editor/js/ui/actions.js", | ||||
|                     "editor/js/ui/deploy.js", | ||||
|                     "editor/js/ui/diff.js", | ||||
|                     "editor/js/ui/keyboard.js", | ||||
|                     "editor/js/ui/workspaces.js", | ||||
|                     "editor/js/ui/view.js", | ||||
| @@ -131,6 +134,7 @@ module.exports = function(grunt) { | ||||
|                     "editor/js/ui/library.js", | ||||
|                     "editor/js/ui/notifications.js", | ||||
|                     "editor/js/ui/search.js", | ||||
|                     "editor/js/ui/typeSearch.js", | ||||
|                     "editor/js/ui/subflow.js", | ||||
|                     "editor/js/ui/touch/radialMenu.js" | ||||
|                 ], | ||||
| @@ -150,6 +154,10 @@ module.exports = function(grunt) { | ||||
|                     "public/vendor/vendor.css": [ | ||||
|                         // TODO: resolve relative resource paths in | ||||
|                         //       bootstrap/FA/jquery | ||||
|                     ], | ||||
|                     "public/vendor/jsonata/jsonata.js": [ | ||||
|                         "node_modules/jsonata/jsonata.js", | ||||
|                         "editor/vendor/jsonata/formatter.js" | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
| @@ -157,7 +165,12 @@ module.exports = function(grunt) { | ||||
|         uglify: { | ||||
|             build: { | ||||
|                 files: { | ||||
|                     'public/red/red.min.js': 'public/red/red.js' | ||||
|                     'public/red/red.min.js': 'public/red/red.js', | ||||
|                     'public/red/main.min.js': 'public/red/main.js', | ||||
|                     'public/vendor/jsonata/jsonata.min.js': 'public/vendor/jsonata/jsonata.js', | ||||
|                     'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js', | ||||
|                     'public/vendor/ace/worker-jsonata.js': 'editor/vendor/jsonata/worker-jsonata.js', | ||||
|                     'public/vendor/ace/snippets/jsonata.js': 'editor/vendor/jsonata/snippets-jsonata.js' | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
| @@ -183,12 +196,18 @@ module.exports = function(grunt) { | ||||
|                     'red/api/locales/en-US/editor.json', | ||||
|                     'red/runtime/locales/en-US/runtime.json' | ||||
|                 ] | ||||
|             }, | ||||
|             keymaps: { | ||||
|                 src: [ | ||||
|                     'editor/js/keymap.json' | ||||
|                 ] | ||||
|             } | ||||
|         }, | ||||
|         attachCopyright: { | ||||
|             js: { | ||||
|                 src: [ | ||||
|                     'public/red/red.min.js' | ||||
|                     'public/red/red.min.js', | ||||
|                     'public/red/main.min.js' | ||||
|                 ] | ||||
|             }, | ||||
|             css: { | ||||
| @@ -218,7 +237,7 @@ module.exports = function(grunt) { | ||||
|                 files: [ | ||||
|                     'editor/js/**/*.js' | ||||
|                 ], | ||||
|                 tasks: ['concat','uglify','attachCopyright:js'] | ||||
|                 tasks: ['copy:build','concat','uglify','attachCopyright:js'] | ||||
|             }, | ||||
|             sass: { | ||||
|                 files: [ | ||||
| @@ -234,6 +253,12 @@ module.exports = function(grunt) { | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:messages'] | ||||
|             }, | ||||
|             keymaps: { | ||||
|                 files: [ | ||||
|                     'editor/js/keymap.json' | ||||
|                 ], | ||||
|                 tasks: ['jsonlint:keymaps','copy:build'] | ||||
|             }, | ||||
|             misc: { | ||||
|                 files: [ | ||||
|                     'CHANGELOG.md' | ||||
| @@ -267,40 +292,49 @@ module.exports = function(grunt) { | ||||
|  | ||||
|         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 | ||||
|                 }, | ||||
|                 { | ||||
|                     src: 'CHANGELOG.md', | ||||
|                     dest: 'public/red/about' | ||||
|                 } | ||||
|                 files:[ | ||||
|                     { | ||||
|                         src: 'editor/js/main.js', | ||||
|                         dest: 'public/red/main.js' | ||||
|                     }, | ||||
|                     { | ||||
|                         src: 'editor/js/keymap.json', | ||||
|                         dest: 'public/red/keymap.json' | ||||
|                     }, | ||||
|                     { | ||||
|                         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 | ||||
|                     }, | ||||
|                     { | ||||
|                         src: 'CHANGELOG.md', | ||||
|                         dest: 'public/red/about' | ||||
|                     } | ||||
|                 ] | ||||
|             }, | ||||
|             release: { | ||||
| @@ -426,7 +460,7 @@ module.exports = function(grunt) { | ||||
|  | ||||
|     grunt.registerTask('build', | ||||
|         'Builds editor content', | ||||
|         ['clean:build','concat:build','concat:vendor','uglify:build','sass:build','jsonlint:messages','copy:build','attachCopyright']); | ||||
|         ['clean:build','jsonlint','concat:build','concat:vendor','copy:build','uglify:build','sass:build','attachCopyright']); | ||||
|  | ||||
|     grunt.registerTask('dev', | ||||
|         'Developer mode: run node-red, watch for source changes and build/restart', | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								editor/icons/parser-yaml.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/icons/parser-yaml.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 423 B | 
							
								
								
									
										
											BIN
										
									
								
								editor/images/typedInput/expr.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/images/typedInput/expr.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 563 B | 
| @@ -16,6 +16,289 @@ | ||||
| RED.history = (function() { | ||||
|     var undo_history = []; | ||||
|  | ||||
|     function undoEvent(ev) { | ||||
|         var i; | ||||
|         var len; | ||||
|         var node; | ||||
|         var subflow; | ||||
|         var modifiedTabs = {}; | ||||
|         if (ev) { | ||||
|             if (ev.t == 'multi') { | ||||
|                 len = ev.events.length; | ||||
|                 for (i=len-1;i>=0;i--) { | ||||
|                     undoEvent(ev.events[i]); | ||||
|                 } | ||||
|             } else if (ev.t == 'replace') { | ||||
|                 RED.nodes.clear(); | ||||
|                 var imported = RED.nodes.import(ev.config); | ||||
|                 imported[0].forEach(function(n) { | ||||
|                     if (ev.changed[n.id]) { | ||||
|                         n.changed = true; | ||||
|                     } | ||||
|                 }) | ||||
|  | ||||
|                 RED.nodes.version(ev.rev); | ||||
|             } else if (ev.t == 'add') { | ||||
|                 if (ev.nodes) { | ||||
|                     for (i=0;i<ev.nodes.length;i++) { | ||||
|                         node = RED.nodes.node(ev.nodes[i]); | ||||
|                         if (node.z) { | ||||
|                             modifiedTabs[node.z] = true; | ||||
|                         } | ||||
|                         RED.nodes.remove(ev.nodes[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
|                         RED.nodes.removeLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.workspaces) { | ||||
|                     for (i=0;i<ev.workspaces.length;i++) { | ||||
|                         RED.nodes.removeWorkspace(ev.workspaces[i].id); | ||||
|                         RED.workspaces.remove(ev.workspaces[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflows) { | ||||
|                     for (i=0;i<ev.subflows.length;i++) { | ||||
|                         RED.nodes.removeSubflow(ev.subflows[i]); | ||||
|                         RED.workspaces.remove(ev.subflows[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflow) { | ||||
|                     if (ev.subflow.instances) { | ||||
|                         ev.subflow.instances.forEach(function(n) { | ||||
|                             var node = RED.nodes.node(n.id); | ||||
|                             if (node) { | ||||
|                                 node.changed = n.changed; | ||||
|                                 node.dirty = true; | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                     if (ev.subflow.hasOwnProperty('changed')) { | ||||
|                         subflow = RED.nodes.subflow(ev.subflow.id); | ||||
|                         if (subflow) { | ||||
|                             subflow.changed = ev.subflow.changed; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.removedLinks) { | ||||
|                     for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                         RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } else if (ev.t == "delete") { | ||||
|                 if (ev.workspaces) { | ||||
|                     for (i=0;i<ev.workspaces.length;i++) { | ||||
|                         RED.nodes.addWorkspace(ev.workspaces[i]); | ||||
|                         RED.workspaces.add(ev.workspaces[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflow && ev.subflow.subflow) { | ||||
|                     RED.nodes.addSubflow(ev.subflow.subflow); | ||||
|                 } | ||||
|                 if (ev.subflowInputs && ev.subflowInputs.length > 0) { | ||||
|                     subflow = RED.nodes.subflow(ev.subflowInputs[0].z); | ||||
|                     subflow.in.push(ev.subflowInputs[0]); | ||||
|                     subflow.in[0].dirty = true; | ||||
|                 } | ||||
|                 if (ev.subflowOutputs && ev.subflowOutputs.length > 0) { | ||||
|                     subflow = RED.nodes.subflow(ev.subflowOutputs[0].z); | ||||
|                     ev.subflowOutputs.sort(function(a,b) { return a.i-b.i}); | ||||
|                     for (i=0;i<ev.subflowOutputs.length;i++) { | ||||
|                         var output = ev.subflowOutputs[i]; | ||||
|                         subflow.out.splice(output.i,0,output); | ||||
|                         for (var j=output.i+1;j<subflow.out.length;j++) { | ||||
|                             subflow.out[j].i++; | ||||
|                             subflow.out[j].dirty = true; | ||||
|                         } | ||||
|                         RED.nodes.eachLink(function(l) { | ||||
|                             if (l.source.type == "subflow:"+subflow.id) { | ||||
|                                 if (l.sourcePort >= output.i) { | ||||
|                                     l.sourcePort++; | ||||
|                                 } | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflow && ev.subflow.hasOwnProperty('instances')) { | ||||
|                     ev.subflow.instances.forEach(function(n) { | ||||
|                         var node = RED.nodes.node(n.id); | ||||
|                         if (node) { | ||||
|                             node.changed = n.changed; | ||||
|                             node.dirty = true; | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|                 if (subflow) { | ||||
|                     RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) { | ||||
|                         n.inputs = subflow.in.length; | ||||
|                         n.outputs = subflow.out.length; | ||||
|                         while (n.outputs > n.ports.length) { | ||||
|                             n.ports.push(n.ports.length); | ||||
|                         } | ||||
|                         n.resize = true; | ||||
|                         n.dirty = true; | ||||
|                     }); | ||||
|                 } | ||||
|                 if (ev.nodes) { | ||||
|                     for (i=0;i<ev.nodes.length;i++) { | ||||
|                         RED.nodes.add(ev.nodes[i]); | ||||
|                         modifiedTabs[ev.nodes[i].z] = true; | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
|                         RED.nodes.addLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.changes) { | ||||
|                     for (i in ev.changes) { | ||||
|                         if (ev.changes.hasOwnProperty(i)) { | ||||
|                             node = RED.nodes.node(i); | ||||
|                             if (node) { | ||||
|                                 for (var d in ev.changes[i]) { | ||||
|                                     if (ev.changes[i].hasOwnProperty(d)) { | ||||
|                                         node[d] = ev.changes[i][d]; | ||||
|                                     } | ||||
|                                 } | ||||
|                                 node.dirty = true; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } else if (ev.t == "move") { | ||||
|                 for (i=0;i<ev.nodes.length;i++) { | ||||
|                     var n = ev.nodes[i]; | ||||
|                     n.n.x = n.ox; | ||||
|                     n.n.y = n.oy; | ||||
|                     n.n.dirty = true; | ||||
|                     n.n.changed = n.changed; | ||||
|                 } | ||||
|                 // A move could have caused a link splice | ||||
|                 if (ev.links) { | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
|                         RED.nodes.removeLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.removedLinks) { | ||||
|                     for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                         RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                     } | ||||
|                 } | ||||
|             } else if (ev.t == "edit") { | ||||
|                 for (i in ev.changes) { | ||||
|                     if (ev.changes.hasOwnProperty(i)) { | ||||
|                         if (ev.node._def.defaults[i].type) { | ||||
|                             // This is a config node property | ||||
|                             var currentConfigNode = RED.nodes.node(ev.node[i]); | ||||
|                             if (currentConfigNode) { | ||||
|                                 currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); | ||||
|                             } | ||||
|                             var newConfigNode = RED.nodes.node(ev.changes[i]); | ||||
|                             if (newConfigNode) { | ||||
|                                 newConfigNode.users.push(ev.node); | ||||
|                             } | ||||
|                         } | ||||
|                         ev.node[i] = ev.changes[i]; | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.subflow) { | ||||
|                     if (ev.subflow.hasOwnProperty('inputCount')) { | ||||
|                         if (ev.node.in.length > ev.subflow.inputCount) { | ||||
|                             ev.node.in.splice(ev.subflow.inputCount); | ||||
|                         } else if (ev.subflow.inputs.length > 0) { | ||||
|                             ev.node.in = ev.node.in.concat(ev.subflow.inputs); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow.hasOwnProperty('outputCount')) { | ||||
|                         if (ev.node.out.length > ev.subflow.outputCount) { | ||||
|                             ev.node.out.splice(ev.subflow.outputCount); | ||||
|                         } else if (ev.subflow.outputs.length > 0) { | ||||
|                             ev.node.out = ev.node.out.concat(ev.subflow.outputs); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow.hasOwnProperty('instances')) { | ||||
|                         ev.subflow.instances.forEach(function(n) { | ||||
|                             var node = RED.nodes.node(n.id); | ||||
|                             if (node) { | ||||
|                                 node.changed = n.changed; | ||||
|                                 node.dirty = true; | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                     RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) { | ||||
|                         n.inputs = ev.node.in.length; | ||||
|                         n.outputs = ev.node.out.length; | ||||
|                         RED.editor.updateNodeProperties(n); | ||||
|                     }); | ||||
|                 } else { | ||||
|                     var outputMap; | ||||
|                     if (ev.outputMap) { | ||||
|                         outputMap = {}; | ||||
|                         for (var port in ev.outputMap) { | ||||
|                             if (ev.outputMap.hasOwnProperty(port) && ev.outputMap[port] !== -1) { | ||||
|                                 outputMap[ev.outputMap[port]] = port; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     RED.editor.updateNodeProperties(ev.node,outputMap); | ||||
|                     RED.editor.validateNode(ev.node); | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
|                         RED.nodes.addLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 ev.node.dirty = true; | ||||
|                 ev.node.changed = ev.changed; | ||||
|             } else if (ev.t == "createSubflow") { | ||||
|                 if (ev.nodes) { | ||||
|                     RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) { | ||||
|                         n.z = ev.activeWorkspace; | ||||
|                         n.dirty = true; | ||||
|                     }); | ||||
|                     for (i=0;i<ev.nodes.length;i++) { | ||||
|                         RED.nodes.remove(ev.nodes[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ev.links) { | ||||
|                     for (i=0;i<ev.links.length;i++) { | ||||
|                         RED.nodes.removeLink(ev.links[i]); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 RED.nodes.removeSubflow(ev.subflow.subflow); | ||||
|                 RED.workspaces.remove(ev.subflow.subflow); | ||||
|  | ||||
|                 if (ev.removedLinks) { | ||||
|                     for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                         RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                     } | ||||
|                 } | ||||
|             } else if (ev.t == "reorder") { | ||||
|                 if (ev.order) { | ||||
|                     RED.workspaces.order(ev.order); | ||||
|                 } | ||||
|             } | ||||
|             Object.keys(modifiedTabs).forEach(function(id) { | ||||
|                 var subflow = RED.nodes.subflow(id); | ||||
|                 if (subflow) { | ||||
|                     RED.editor.validateNode(subflow); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             RED.nodes.dirty(ev.dirty); | ||||
|             RED.view.redraw(true); | ||||
|             RED.palette.refresh(); | ||||
|             RED.workspaces.refresh(); | ||||
|             RED.sidebar.config.refresh(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         //TODO: this function is a placeholder until there is a 'save' event that can be listened to | ||||
|         markAllDirty: function() { | ||||
| @@ -34,260 +317,7 @@ RED.history = (function() { | ||||
|         }, | ||||
|         pop: function() { | ||||
|             var ev = undo_history.pop(); | ||||
|             var i; | ||||
|             var node; | ||||
|             var subflow; | ||||
|             var modifiedTabs = {}; | ||||
|             if (ev) { | ||||
|                 if (ev.t == 'add') { | ||||
|                     if (ev.nodes) { | ||||
|                         for (i=0;i<ev.nodes.length;i++) { | ||||
|                             node = RED.nodes.node(ev.nodes[i]); | ||||
|                             if (node.z) { | ||||
|                                 modifiedTabs[node.z] = true; | ||||
|                             } | ||||
|                             RED.nodes.remove(ev.nodes[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.links) { | ||||
|                         for (i=0;i<ev.links.length;i++) { | ||||
|                             RED.nodes.removeLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.workspaces) { | ||||
|                         for (i=0;i<ev.workspaces.length;i++) { | ||||
|                             RED.nodes.removeWorkspace(ev.workspaces[i].id); | ||||
|                             RED.workspaces.remove(ev.workspaces[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflows) { | ||||
|                         for (i=0;i<ev.subflows.length;i++) { | ||||
|                             RED.nodes.removeSubflow(ev.subflows[i]); | ||||
|                             RED.workspaces.remove(ev.subflows[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow) { | ||||
|                         if (ev.subflow.instances) { | ||||
|                             ev.subflow.instances.forEach(function(n) { | ||||
|                                 var node = RED.nodes.node(n.id); | ||||
|                                 if (node) { | ||||
|                                     node.changed = n.changed; | ||||
|                                     node.dirty = true; | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                         if (ev.subflow.hasOwnProperty('changed')) { | ||||
|                             subflow = RED.nodes.subflow(ev.subflow.id); | ||||
|                             if (subflow) { | ||||
|                                 subflow.changed = ev.subflow.changed; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.removedLinks) { | ||||
|                         for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                             RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } else if (ev.t == "delete") { | ||||
|                     if (ev.workspaces) { | ||||
|                         for (i=0;i<ev.workspaces.length;i++) { | ||||
|                             RED.nodes.addWorkspace(ev.workspaces[i]); | ||||
|                             RED.workspaces.add(ev.workspaces[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow && ev.subflow.subflow) { | ||||
|                         RED.nodes.addSubflow(ev.subflow.subflow); | ||||
|                     } | ||||
|                     if (ev.subflowInputs && ev.subflowInputs.length > 0) { | ||||
|                         subflow = RED.nodes.subflow(ev.subflowInputs[0].z); | ||||
|                         subflow.in.push(ev.subflowInputs[0]); | ||||
|                         subflow.in[0].dirty = true; | ||||
|                     } | ||||
|                     if (ev.subflowOutputs && ev.subflowOutputs.length > 0) { | ||||
|                         subflow = RED.nodes.subflow(ev.subflowOutputs[0].z); | ||||
|                         ev.subflowOutputs.sort(function(a,b) { return a.i-b.i}); | ||||
|                         for (i=0;i<ev.subflowOutputs.length;i++) { | ||||
|                             var output = ev.subflowOutputs[i]; | ||||
|                             subflow.out.splice(output.i,0,output); | ||||
|                             for (var j=output.i+1;j<subflow.out.length;j++) { | ||||
|                                 subflow.out[j].i++; | ||||
|                                 subflow.out[j].dirty = true; | ||||
|                             } | ||||
|                             RED.nodes.eachLink(function(l) { | ||||
|                                 if (l.source.type == "subflow:"+subflow.id) { | ||||
|                                     if (l.sourcePort >= output.i) { | ||||
|                                         l.sourcePort++; | ||||
|                                     } | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow && ev.subflow.hasOwnProperty('instances')) { | ||||
|                         ev.subflow.instances.forEach(function(n) { | ||||
|                             var node = RED.nodes.node(n.id); | ||||
|                             if (node) { | ||||
|                                 node.changed = n.changed; | ||||
|                                 node.dirty = true; | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                     if (subflow) { | ||||
|                         RED.nodes.filterNodes({type:"subflow:"+subflow.id}).forEach(function(n) { | ||||
|                             n.inputs = subflow.in.length; | ||||
|                             n.outputs = subflow.out.length; | ||||
|                             while (n.outputs > n.ports.length) { | ||||
|                                 n.ports.push(n.ports.length); | ||||
|                             } | ||||
|                             n.resize = true; | ||||
|                             n.dirty = true; | ||||
|                         }); | ||||
|                     } | ||||
|                     if (ev.nodes) { | ||||
|                         for (i=0;i<ev.nodes.length;i++) { | ||||
|                             RED.nodes.add(ev.nodes[i]); | ||||
|                             modifiedTabs[ev.nodes[i].z] = true; | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.links) { | ||||
|                         for (i=0;i<ev.links.length;i++) { | ||||
|                             RED.nodes.addLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.changes) { | ||||
|                         for (i in ev.changes) { | ||||
|                             if (ev.changes.hasOwnProperty(i)) { | ||||
|                                 node = RED.nodes.node(i); | ||||
|                                 if (node) { | ||||
|                                     for (var d in ev.changes[i]) { | ||||
|                                         if (ev.changes[i].hasOwnProperty(d)) { | ||||
|                                             node[d] = ev.changes[i][d]; | ||||
|                                         } | ||||
|                                     } | ||||
|                                     node.dirty = true; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } else if (ev.t == "move") { | ||||
|                     for (i=0;i<ev.nodes.length;i++) { | ||||
|                         var n = ev.nodes[i]; | ||||
|                         n.n.x = n.ox; | ||||
|                         n.n.y = n.oy; | ||||
|                         n.n.dirty = true; | ||||
|                         n.n.changed = n.changed; | ||||
|                     } | ||||
|                     // A move could have caused a link splice | ||||
|                     if (ev.links) { | ||||
|                         for (i=0;i<ev.links.length;i++) { | ||||
|                             RED.nodes.removeLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.removedLinks) { | ||||
|                         for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                             RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                         } | ||||
|                     } | ||||
|                 } else if (ev.t == "edit") { | ||||
|                     for (i in ev.changes) { | ||||
|                         if (ev.changes.hasOwnProperty(i)) { | ||||
|                             if (ev.node._def.defaults[i].type) { | ||||
|                                 // This is a config node property | ||||
|                                 var currentConfigNode = RED.nodes.node(ev.node[i]); | ||||
|                                 if (currentConfigNode) { | ||||
|                                     currentConfigNode.users.splice(currentConfigNode.users.indexOf(ev.node),1); | ||||
|                                 } | ||||
|                                 var newConfigNode = RED.nodes.node(ev.changes[i]); | ||||
|                                 if (newConfigNode) { | ||||
|                                     newConfigNode.users.push(ev.node); | ||||
|                                 } | ||||
|                             } | ||||
|                             ev.node[i] = ev.changes[i]; | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.subflow) { | ||||
|                         if (ev.subflow.hasOwnProperty('inputCount')) { | ||||
|                             if (ev.node.in.length > ev.subflow.inputCount) { | ||||
|                                 ev.node.in.splice(ev.subflow.inputCount); | ||||
|                             } else if (ev.subflow.inputs.length > 0) { | ||||
|                                 ev.node.in = ev.node.in.concat(ev.subflow.inputs); | ||||
|                             } | ||||
|                         } | ||||
|                         if (ev.subflow.hasOwnProperty('outputCount')) { | ||||
|                             if (ev.node.out.length > ev.subflow.outputCount) { | ||||
|                                 ev.node.out.splice(ev.subflow.outputCount); | ||||
|                             } else if (ev.subflow.outputs.length > 0) { | ||||
|                                 ev.node.out = ev.node.out.concat(ev.subflow.outputs); | ||||
|                             } | ||||
|                         } | ||||
|                         if (ev.subflow.hasOwnProperty('instances')) { | ||||
|                             ev.subflow.instances.forEach(function(n) { | ||||
|                                 var node = RED.nodes.node(n.id); | ||||
|                                 if (node) { | ||||
|                                     node.changed = n.changed; | ||||
|                                     node.dirty = true; | ||||
|                                 } | ||||
|                             }); | ||||
|                         } | ||||
|                         RED.nodes.filterNodes({type:"subflow:"+ev.node.id}).forEach(function(n) { | ||||
|                             n.inputs = ev.node.in.length; | ||||
|                             n.outputs = ev.node.out.length; | ||||
|                             RED.editor.updateNodeProperties(n); | ||||
|                         }); | ||||
|                     } else { | ||||
|                         RED.editor.updateNodeProperties(ev.node); | ||||
|                         RED.editor.validateNode(ev.node); | ||||
|                     } | ||||
|                     if (ev.links) { | ||||
|                         for (i=0;i<ev.links.length;i++) { | ||||
|                             RED.nodes.addLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     ev.node.dirty = true; | ||||
|                     ev.node.changed = ev.changed; | ||||
|                 } else if (ev.t == "createSubflow") { | ||||
|                     if (ev.nodes) { | ||||
|                         RED.nodes.filterNodes({z:ev.subflow.subflow.id}).forEach(function(n) { | ||||
|                             n.z = ev.activeWorkspace; | ||||
|                             n.dirty = true; | ||||
|                         }); | ||||
|                         for (i=0;i<ev.nodes.length;i++) { | ||||
|                             RED.nodes.remove(ev.nodes[i]); | ||||
|                         } | ||||
|                     } | ||||
|                     if (ev.links) { | ||||
|                         for (i=0;i<ev.links.length;i++) { | ||||
|                             RED.nodes.removeLink(ev.links[i]); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     RED.nodes.removeSubflow(ev.subflow.subflow); | ||||
|                     RED.workspaces.remove(ev.subflow.subflow); | ||||
|  | ||||
|                     if (ev.removedLinks) { | ||||
|                         for (i=0;i<ev.removedLinks.length;i++) { | ||||
|                             RED.nodes.addLink(ev.removedLinks[i]); | ||||
|                         } | ||||
|                     } | ||||
|                 } else if (ev.t == "reorder") { | ||||
|                     if (ev.order) { | ||||
|                         RED.workspaces.order(ev.order); | ||||
|                     } | ||||
|                 } | ||||
|                 Object.keys(modifiedTabs).forEach(function(id) { | ||||
|                     var subflow = RED.nodes.subflow(id); | ||||
|                     if (subflow) { | ||||
|                         RED.editor.validateNode(subflow); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 RED.nodes.dirty(ev.dirty); | ||||
|                 RED.view.redraw(true); | ||||
|                 RED.palette.refresh(); | ||||
|                 RED.workspaces.refresh(); | ||||
|                 RED.sidebar.config.refresh(); | ||||
|             } | ||||
|             undoEvent(ev); | ||||
|         }, | ||||
|         peek: function() { | ||||
|             return undo_history[undo_history.length-1]; | ||||
|   | ||||
| @@ -23,7 +23,7 @@ RED.i18n = (function() { | ||||
|                 dynamicLoad: false, | ||||
|                 load:'current', | ||||
|                 ns: { | ||||
|                     namespaces: ["editor","node-red"], | ||||
|                     namespaces: ["editor","node-red","jsonata","infotips"], | ||||
|                     defaultNs: "editor" | ||||
|                 }, | ||||
|                 fallbackLng: ['en-US'], | ||||
|   | ||||
							
								
								
									
										38
									
								
								editor/js/keymap.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								editor/js/keymap.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| { | ||||
|     "*": { | ||||
|         "ctrl-shift-p":"core:manage-palette", | ||||
|         "ctrl-f": "core:search", | ||||
|         "ctrl-=": "core:zoom-in", | ||||
|         "ctrl--": "core:zoom-out", | ||||
|         "ctrl-0": "core:zoom-reset", | ||||
|         "ctrl-enter": "core:confirm-edit-tray", | ||||
|         "ctrl-escape": "core:cancel-edit-tray", | ||||
|         "ctrl-g i": "core:show-info-tab", | ||||
|         "ctrl-g d": "core:show-debug-tab", | ||||
|         "ctrl-g c": "core:show-config-tab" | ||||
|     }, | ||||
|     "workspace": { | ||||
|         "ctrl-e": "core:export", | ||||
|         "ctrl-i": "core:import", | ||||
|         "backspace": "core:delete", | ||||
|         "delete": "core:delete", | ||||
|         "enter": "core:edit", | ||||
|         "ctrl-c": "core:copy", | ||||
|         "ctrl-x": "core:cut", | ||||
|         "ctrl-v": "core:paste", | ||||
|         "ctrl-z": "core:undo", | ||||
|         "ctrl-a": "core:select-all", | ||||
|         "shift-?": "core:show-help", | ||||
|         "ctrl-space": "core:toggle-sidebar", | ||||
|         "up": "core:move-selection-up", | ||||
|         "right": "core:move-selection-right", | ||||
|         "down": "core:move-selection-down", | ||||
|         "left": "core:move-selection-left", | ||||
|         "shift-up": "core:step-selection-up", | ||||
|         "shift-right": "core:step-selection-right", | ||||
|         "shift-down": "core:step-selection-down", | ||||
|         "shift-left": "core:step-selection-left", | ||||
|         "ctrl-shift-j": "core:show-previous-tab", | ||||
|         "ctrl-shift-k": "core:show-next-tab" | ||||
|      } | ||||
| } | ||||
| @@ -13,8 +13,7 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| var RED = (function() { | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|     function loadNodeList() { | ||||
|         $.ajax({ | ||||
| @@ -80,6 +79,23 @@ var RED = (function() { | ||||
|                 if (/^#flow\/.+$/.test(currentHash)) { | ||||
|                     RED.workspaces.show(currentHash.substring(6)); | ||||
|                 } | ||||
|  | ||||
|                 var persistentNotifications = {}; | ||||
|                 RED.comms.subscribe("notification/#",function(topic,msg) { | ||||
|                     var parts = topic.split("/"); | ||||
|                     var notificationId = parts[1]; | ||||
|                     if (msg.text) { | ||||
|                         var text = RED._(msg.text,{default:msg.text}); | ||||
|                         if (!persistentNotifications.hasOwnProperty(notificationId)) { | ||||
|                             persistentNotifications[notificationId] = RED.notify(text,msg.type,msg.timeout === undefined,msg.timeout); | ||||
|                         } else { | ||||
|                             persistentNotifications[notificationId].update(text,msg.timeout); | ||||
|                         } | ||||
|                     } else if (persistentNotifications.hasOwnProperty(notificationId)) { | ||||
|                         persistentNotifications[notificationId].close(); | ||||
|                         delete persistentNotifications[notificationId]; | ||||
|                     } | ||||
|                 }); | ||||
|                 RED.comms.subscribe("status/#",function(topic,msg) { | ||||
|                     var parts = topic.split("/"); | ||||
|                     var node = RED.nodes.node(parts[1]); | ||||
| @@ -98,11 +114,9 @@ var RED = (function() { | ||||
|                     var i,m; | ||||
|                     var typeList; | ||||
|                     var info; | ||||
|  | ||||
|                     if (topic == "node/added") { | ||||
|                         var addedTypes = []; | ||||
|                         for (i=0;i<msg.length;i++) { | ||||
|                             m = msg[i]; | ||||
|                         msg.forEach(function(m) { | ||||
|                             var id = m.id; | ||||
|                             RED.nodes.addNodeSet(m); | ||||
|                             addedTypes = addedTypes.concat(m.types); | ||||
| @@ -111,7 +125,7 @@ var RED = (function() { | ||||
|                                     $("body").append(data); | ||||
|                                 }); | ||||
|                             }); | ||||
|                         } | ||||
|                         }); | ||||
|                         if (addedTypes.length) { | ||||
|                             typeList = "<ul><li>"+addedTypes.join("</li><li>")+"</li></ul>"; | ||||
|                             RED.notify(RED._("palette.event.nodeAdded", {count:addedTypes.length})+typeList,"success"); | ||||
| @@ -172,12 +186,11 @@ var RED = (function() { | ||||
|     } | ||||
|  | ||||
|     function loadEditor() { | ||||
|  | ||||
|         var menuOptions = []; | ||||
|         menuOptions.push({id:"menu-item-view-menu",label:RED._("menu.label.view.view"),options:[ | ||||
|             {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:RED.view.toggleShowGrid}, | ||||
|             {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:RED.view.toggleSnapGrid}, | ||||
|             {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:toggleStatus, selected: true}, | ||||
|             {id:"menu-item-view-show-grid",label:RED._("menu.label.view.showGrid"),toggle:true,onselect:"core:toggle-show-grid"}, | ||||
|             {id:"menu-item-view-snap-grid",label:RED._("menu.label.view.snapGrid"),toggle:true,onselect:"core:toggle-snap-grid"}, | ||||
|             {id:"menu-item-status",label:RED._("menu.label.displayStatus"),toggle:true,onselect:"core:toggle-status", selected: true}, | ||||
|             null, | ||||
|             // {id:"menu-item-bidi",label:RED._("menu.label.view.textDir"),options:[ | ||||
|             //     {id:"menu-item-bidi-default",toggle:"text-direction",label:RED._("menu.label.view.defaultDir"),selected: true, onselect:function(s) { if(s){RED.text.bidi.setTextDirection("")}}}, | ||||
| @@ -186,48 +199,47 @@ var RED = (function() { | ||||
|             //     {id:"menu-item-bidi-auto",toggle:"text-direction",label:RED._("menu.label.view.auto"), onselect:function(s) { if(s){RED.text.bidi.setTextDirection("auto")}}} | ||||
|             // ]}, | ||||
|             // null, | ||||
|             {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:RED.sidebar.toggleSidebar, selected: true} | ||||
|             {id:"menu-item-sidebar",label:RED._("menu.label.sidebar.show"),toggle:true,onselect:"core:toggle-sidebar", selected: true} | ||||
|         ]}); | ||||
|         menuOptions.push(null); | ||||
|         menuOptions.push({id:"menu-item-import",label:RED._("menu.label.import"),options:[ | ||||
|             {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:RED.clipboard.import}, | ||||
|             {id:"menu-item-import-clipboard",label:RED._("menu.label.clipboard"),onselect:"core:import"}, | ||||
|             {id:"menu-item-import-library",label:RED._("menu.label.library"),options:[]} | ||||
|         ]}); | ||||
|         menuOptions.push({id:"menu-item-export",label:RED._("menu.label.export"),disabled:true,options:[ | ||||
|             {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:RED.clipboard.export}, | ||||
|             {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:RED.library.export} | ||||
|             {id:"menu-item-export-clipboard",label:RED._("menu.label.clipboard"),disabled:true,onselect:"core:export"}, | ||||
|             {id:"menu-item-export-library",label:RED._("menu.label.library"),disabled:true,onselect:"core:library-export"} | ||||
|         ]}); | ||||
|         menuOptions.push(null); | ||||
|         menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:RED.search.show}); | ||||
|         menuOptions.push({id:"menu-item-search",label:RED._("menu.label.search"),onselect:"core:search"}); | ||||
|         menuOptions.push(null); | ||||
|         menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:function() {}}); | ||||
|         menuOptions.push({id:"menu-item-config-nodes",label:RED._("menu.label.displayConfig"),onselect:"core:show-config-tab"}); | ||||
|         menuOptions.push({id:"menu-item-workspace",label:RED._("menu.label.flows"),options:[ | ||||
|             {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:RED.workspaces.add}, | ||||
|             {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:RED.workspaces.edit}, | ||||
|             {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:RED.workspaces.remove} | ||||
|             {id:"menu-item-workspace-add",label:RED._("menu.label.add"),onselect:"core:add-flow"}, | ||||
|             {id:"menu-item-workspace-edit",label:RED._("menu.label.rename"),onselect:"core:edit-flow"}, | ||||
|             {id:"menu-item-workspace-delete",label:RED._("menu.label.delete"),onselect:"core:remove-flow"} | ||||
|         ]}); | ||||
|         menuOptions.push({id:"menu-item-subflow",label:RED._("menu.label.subflows"), options: [ | ||||
|             {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:RED.subflow.createSubflow}, | ||||
|             {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:RED.subflow.convertToSubflow}, | ||||
|             {id:"menu-item-subflow-create",label:RED._("menu.label.createSubflow"),onselect:"core:create-subflow"}, | ||||
|             {id:"menu-item-subflow-convert",label:RED._("menu.label.selectionToSubflow"),disabled:true,onselect:"core:convert-to-subflow"}, | ||||
|         ]}); | ||||
|         menuOptions.push(null); | ||||
|         if (RED.settings.theme('palette.editable') !== false) { | ||||
|             RED.palette.editor.init(); | ||||
|             menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:RED.palette.editor.show}); | ||||
|             menuOptions.push({id:"menu-item-edit-palette",label:RED._("menu.label.editPalette"),onselect:"core:manage-palette"}); | ||||
|             menuOptions.push(null); | ||||
|         } | ||||
|  | ||||
|         menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:RED.keyboard.showHelp}); | ||||
|         menuOptions.push({id:"menu-item-keyboard-shortcuts",label:RED._("menu.label.keyboardShortcuts"),onselect:"core:show-help"}); | ||||
|         menuOptions.push({id:"menu-item-show-tips",label:RED._("menu.label.showTips"),toggle:true,selected:true,onselect:"core:toggle-show-tips"}); | ||||
|         menuOptions.push({id:"menu-item-help", | ||||
|             label: RED.settings.theme("menu.menu-item-help.label","Node-RED website"), | ||||
|             href: RED.settings.theme("menu.menu-item-help.url","http://nodered.org/docs") | ||||
|         }); | ||||
|         menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: showAbout }); | ||||
|         menuOptions.push({id:"menu-item-node-red-version", label:"v"+RED.settings.version, onselect: "core:show-about" }); | ||||
|  | ||||
|         RED.menu.init({id:"btn-sidemenu",options: menuOptions}); | ||||
|  | ||||
|         RED.user.init(); | ||||
|  | ||||
|         RED.library.init(); | ||||
|         RED.palette.init(); | ||||
|         RED.sidebar.init(); | ||||
| @@ -237,15 +249,21 @@ var RED = (function() { | ||||
|         RED.search.init(); | ||||
|         RED.view.init(); | ||||
|         RED.editor.init(); | ||||
|         RED.keyboard.init(); | ||||
|         RED.diff.init(); | ||||
|  | ||||
|         RED.menu.init({id:"btn-sidemenu",options: menuOptions}); | ||||
|  | ||||
|         RED.deploy.init(RED.settings.theme("deployButton",null)); | ||||
|  | ||||
|         RED.keyboard.add("workspace", /* ? */ 191,{shift:true},function() {RED.keyboard.showHelp();d3.event.preventDefault();}); | ||||
|         RED.actions.add("core:show-about", showAbout); | ||||
|  | ||||
|         RED.comms.connect(); | ||||
|  | ||||
|         $("#main-container").show(); | ||||
|         $(".header-toolbar").show(); | ||||
|  | ||||
|  | ||||
|         loadNodeList(); | ||||
|     } | ||||
|  | ||||
| @@ -261,8 +279,4 @@ var RED = (function() { | ||||
|             RED.settings.init(loadEditor); | ||||
|         }) | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     return { | ||||
|     }; | ||||
| })(); | ||||
|   | ||||
| @@ -24,21 +24,13 @@ RED.nodes = (function() { | ||||
|     var workspacesOrder =[]; | ||||
|     var subflows = {}; | ||||
|     var loadedFlowVersion = null; | ||||
|     var pending = { | ||||
|         deleted: {}, | ||||
|         added: {} | ||||
|     }; | ||||
|  | ||||
|     var initialLoad; | ||||
|  | ||||
|     var dirty = false; | ||||
|  | ||||
|     function setDirty(d) { | ||||
|         dirty = d; | ||||
|         if (!d) { | ||||
|             pending = { | ||||
|                 deleted: {}, | ||||
|                 added: {} | ||||
|             }; | ||||
|         } | ||||
|         RED.events.emit("nodes:change",{dirty:dirty}); | ||||
|     } | ||||
|  | ||||
| @@ -62,6 +54,9 @@ RED.nodes = (function() { | ||||
|             getNodeList: function() { | ||||
|                 return nodeList; | ||||
|             }, | ||||
|             getNodeTypes: function() { | ||||
|                 return Object.keys(nodeDefinitions); | ||||
|             }, | ||||
|             setNodeList: function(list) { | ||||
|                 nodeList = []; | ||||
|                 for(var i=0;i<list.length;i++) { | ||||
| @@ -170,6 +165,7 @@ RED.nodes = (function() { | ||||
|             configNodes[n.id] = n; | ||||
|         } else { | ||||
|             n.ports = []; | ||||
|             if (n.wires && (n.wires.length > n.outputs)) { n.outputs = n.wires.length; } | ||||
|             if (n.outputs) { | ||||
|                 for (var i=0;i<n.outputs;i++) { | ||||
|                     n.ports.push(i); | ||||
| @@ -186,8 +182,6 @@ RED.nodes = (function() { | ||||
|             } | ||||
|             nodes.push(n); | ||||
|         } | ||||
|         delete pending.deleted[n.id]; | ||||
|         pending.added[n.id] = true; | ||||
|         RED.events.emit('nodes:add',n); | ||||
|     } | ||||
|     function addLink(l) { | ||||
| @@ -253,12 +247,6 @@ RED.nodes = (function() { | ||||
|         if (node && node._def.onremove) { | ||||
|             node._def.onremove.call(n); | ||||
|         } | ||||
|         delete pending.added[id]; | ||||
|         pending.deleted[id] = true; | ||||
|         removedNodes.forEach(function(node) { | ||||
|             delete pending.added[node.id]; | ||||
|             pending.deleted[node.id] = true; | ||||
|         }); | ||||
|         return {links:removedLinks,nodes:removedNodes}; | ||||
|     } | ||||
|  | ||||
| @@ -271,8 +259,6 @@ RED.nodes = (function() { | ||||
|  | ||||
|     function addWorkspace(ws) { | ||||
|         workspaces[ws.id] = ws; | ||||
|         pending.added[ws.id] = true; | ||||
|         delete pending.deleted[ws.id]; | ||||
|         ws._def = { | ||||
|             defaults: { | ||||
|                 label: {value:""} | ||||
| @@ -310,8 +296,6 @@ RED.nodes = (function() { | ||||
|             var result = removeNode(removedNodes[n].id); | ||||
|             removedLinks = removedLinks.concat(result.links); | ||||
|         } | ||||
|         pending.deleted[id] = true; | ||||
|         delete pending.added[id] | ||||
|         return {nodes:removedNodes,links:removedLinks}; | ||||
|     } | ||||
|  | ||||
| @@ -341,8 +325,6 @@ RED.nodes = (function() { | ||||
|             outputs: sf.out.length | ||||
|         } | ||||
|         subflows[sf.id] = sf; | ||||
|         delete pending.deleted[sf.id]; | ||||
|         pending.added[sf.id] = true; | ||||
|         RED.nodes.registerType("subflow:"+sf.id, { | ||||
|             defaults:{name:{value:""}}, | ||||
|             info: sf.info, | ||||
| @@ -366,8 +348,6 @@ RED.nodes = (function() { | ||||
|     } | ||||
|     function removeSubflow(sf) { | ||||
|         delete subflows[sf.id]; | ||||
|         delete pending.added[sf.id]; | ||||
|         pending.deleted[sf.id] = true; | ||||
|         registry.removeNodeType("subflow:"+sf.id); | ||||
|     } | ||||
|  | ||||
| @@ -694,6 +674,9 @@ RED.nodes = (function() { | ||||
|         if (!$.isArray(newNodes)) { | ||||
|             newNodes = [newNodes]; | ||||
|         } | ||||
|         if (!initialLoad) { | ||||
|             initialLoad = JSON.parse(JSON.stringify(newNodes)); | ||||
|         } | ||||
|         var unknownTypes = []; | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
| @@ -719,17 +702,19 @@ RED.nodes = (function() { | ||||
|         } | ||||
|  | ||||
|         var activeWorkspace = RED.workspaces.active(); | ||||
|         //TODO: check the z of the subflow instance and check _that_ if it exists | ||||
|         var activeSubflow = getSubflow(activeWorkspace); | ||||
|         if (activeSubflow) { | ||||
|             for (i=0;i<newNodes.length;i++) { | ||||
|                 var m = /^subflow:(.+)$/.exec(newNodes[i].type); | ||||
|                 if (m) { | ||||
|                     var subflowId = m[1]; | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             var m = /^subflow:(.+)$/.exec(newNodes[i].type); | ||||
|             if (m) { | ||||
|                 var subflowId = m[1]; | ||||
|                 var parent = getSubflow(newNodes[i].z || activeWorkspace); | ||||
|                 if (parent) { | ||||
|                     var err; | ||||
|                     if (subflowId === activeSubflow.id) { | ||||
|                     if (subflowId === parent.id) { | ||||
|                         err = new Error(RED._("notification.errors.cannotAddSubflowToItself")); | ||||
|                     } | ||||
|                     if (subflowContains(m[1],activeSubflow.id)) { | ||||
|                     if (subflowContains(subflowId,parent.id)) { | ||||
|                         err = new Error(RED._("notification.errors.cannotAddCircularReference")); | ||||
|                     } | ||||
|                     if (err) { | ||||
| @@ -1019,8 +1004,9 @@ RED.nodes = (function() { | ||||
|                 for (var w1=0;w1<n.wires.length;w1++) { | ||||
|                     var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]]; | ||||
|                     for (var w2=0;w2<wires.length;w2++) { | ||||
|                         if (wires[w2] in node_map) { | ||||
|                             var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]}; | ||||
|                         var existingNode = node_map[wires[w2]] || getNode(wires[w2]); | ||||
|                         if (existingNode) { | ||||
|                             var link = {source:n,sourcePort:w1,target:existingNode}; | ||||
|                             addLink(link); | ||||
|                             new_links.push(link); | ||||
|                         } | ||||
| @@ -1161,6 +1147,38 @@ RED.nodes = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function clear() { | ||||
|         nodes = []; | ||||
|         links = []; | ||||
|         configNodes = {}; | ||||
|         workspacesOrder = []; | ||||
|         var subflowIds = Object.keys(subflows); | ||||
|         subflowIds.forEach(function(id) { | ||||
|             RED.subflow.removeSubflow(id) | ||||
|         }); | ||||
|         var workspaceIds = Object.keys(workspaces); | ||||
|         workspaceIds.forEach(function(id) { | ||||
|             RED.workspaces.remove(workspaces[id]); | ||||
|         }); | ||||
|         defaultWorkspace = null; | ||||
|  | ||||
|         RED.nodes.dirty(true); | ||||
|         RED.view.redraw(true); | ||||
|         RED.palette.refresh(); | ||||
|         RED.workspaces.refresh(); | ||||
|         RED.sidebar.config.refresh(); | ||||
|  | ||||
|         // var node_defs = {}; | ||||
|         // var nodes = []; | ||||
|         // var configNodes = {}; | ||||
|         // var links = []; | ||||
|         // var defaultWorkspace; | ||||
|         // var workspaces = {}; | ||||
|         // var workspacesOrder =[]; | ||||
|         // var subflows = {}; | ||||
|         // var loadedFlowVersion = null; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         registry:registry, | ||||
|         setNodeList: registry.setNodeList, | ||||
| @@ -1177,6 +1195,7 @@ RED.nodes = (function() { | ||||
|  | ||||
|         add: addNode, | ||||
|         remove: removeNode, | ||||
|         clear: clear, | ||||
|  | ||||
|         addLink: addLink, | ||||
|         removeLink: removeLink, | ||||
| @@ -1225,14 +1244,19 @@ RED.nodes = (function() { | ||||
|         node: getNode, | ||||
|  | ||||
|         version: flowVersion, | ||||
|         originalFlow: function(flow) { | ||||
|             if (flow === undefined) { | ||||
|                 return initialLoad; | ||||
|             } else { | ||||
|                 initialLoad = flow; | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         filterNodes: filterNodes, | ||||
|         filterLinks: filterLinks, | ||||
|  | ||||
|         import: importNodes, | ||||
|  | ||||
|         pending: function() { return pending }, | ||||
|  | ||||
|         getAllFlowNodes: getAllFlowNodes, | ||||
|         createExportableNodeSet: createExportableNodeSet, | ||||
|         createCompleteNodeSet: createCompleteNodeSet, | ||||
|   | ||||
							
								
								
									
										16
									
								
								editor/js/red.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								editor/js/red.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| var RED = {}; | ||||
							
								
								
									
										35
									
								
								editor/js/ui/actions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								editor/js/ui/actions.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| RED.actions = (function() { | ||||
|     var actions = { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function addAction(name,handler) { | ||||
|         actions[name] = handler; | ||||
|     } | ||||
|     function removeAction(name) { | ||||
|         delete actions[name]; | ||||
|     } | ||||
|     function getAction(name) { | ||||
|         return actions[name]; | ||||
|     } | ||||
|     function invokeAction(name) { | ||||
|         if (actions.hasOwnProperty(name)) { | ||||
|             actions[name](); | ||||
|         } | ||||
|     } | ||||
|     function listActions() { | ||||
|         var result = []; | ||||
|         Object.keys(actions).forEach(function(action) { | ||||
|             var shortcut = RED.keyboard.getShortcut(action); | ||||
|             result.push({id:action,scope:shortcut?shortcut.scope:undefined,key:shortcut?shortcut.key:undefined}) | ||||
|         }) | ||||
|         return result; | ||||
|     } | ||||
|     return { | ||||
|         add: addAction, | ||||
|         remove: removeAction, | ||||
|         get: getAction, | ||||
|         invoke: invokeAction, | ||||
|         list: listActions | ||||
|     } | ||||
| })(); | ||||
| @@ -257,7 +257,7 @@ RED.clipboard = (function() { | ||||
|  | ||||
|     function hideDropTarget() { | ||||
|         $("#dropTarget").hide(); | ||||
|         RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|         RED.keyboard.remove("escape"); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
| @@ -274,13 +274,15 @@ RED.clipboard = (function() { | ||||
|                     RED.menu.setDisabled("menu-item-export-library",false); | ||||
|                 } | ||||
|             }); | ||||
|             RED.keyboard.add("workspace", /* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();}); | ||||
|             RED.keyboard.add("workspace", /* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();}); | ||||
|  | ||||
|             RED.actions.add("core:export",exportNodes); | ||||
|             RED.actions.add("core:import",importNodes); | ||||
|  | ||||
|  | ||||
|             $('#chart').on("dragenter",function(event) { | ||||
|                 if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { | ||||
|                     $("#dropTarget").css({display:'table'}); | ||||
|                     RED.keyboard.add("*", /* ESCAPE */ 27,hideDropTarget); | ||||
|                     RED.keyboard.add("*", "escape" ,hideDropTarget); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|   | ||||
| @@ -27,6 +27,9 @@ | ||||
|  *   - removable : boolean - whether to display delete button on items | ||||
|  *   - addItem : function(row,index,itemData) - when an item is added | ||||
|  *   - removeItem : function(itemData) - called when an item is removed | ||||
|  *   - filter : function(itemData) - called for each item to determine if it should be shown | ||||
|  *   - sort : function(itemDataA,itemDataB) - called to sort items | ||||
|  *   - scrollOnAdd : boolean - whether to scroll to newly added items | ||||
|  * methods: | ||||
|  *   - addItem(itemData) | ||||
|  *   - removeItem(itemData) | ||||
| @@ -34,6 +37,9 @@ | ||||
|  *   - height(height) | ||||
|  *   - items() | ||||
|  *   - empty() | ||||
|  *   - filter(filter) | ||||
|  *   - sort(sort) | ||||
|  *   - length() | ||||
|  */ | ||||
|     $.widget( "nodered.editableList", { | ||||
|         _create: function() { | ||||
| @@ -234,7 +240,8 @@ | ||||
|                 var deleteButton = $('<a/>',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(li); | ||||
|                 $('<i/>',{class:"fa fa-remove"}).appendTo(deleteButton); | ||||
|                 li.addClass("red-ui-editableList-item-removable"); | ||||
|                 deleteButton.click(function() { | ||||
|                 deleteButton.click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     var data = row.data('data'); | ||||
|                     li.addClass("red-ui-editableList-item-deleting") | ||||
|                     li.fadeOut(300, function() { | ||||
|   | ||||
| @@ -13,9 +13,6 @@ | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
|  | ||||
|  | ||||
| RED.menu = (function() { | ||||
|  | ||||
|     var menuItems = {}; | ||||
| @@ -34,17 +31,17 @@ RED.menu = (function() { | ||||
|             var savedStateActive = isSavedStateActive(opt.id); | ||||
|             if (savedStateActive) { | ||||
|                 link.addClass("active"); | ||||
|                 opt.onselect.call(opt, true); | ||||
|                 triggerAction(opt.id,true); | ||||
|             } else if (savedStateActive === false) { | ||||
|                 link.removeClass("active"); | ||||
|                 opt.onselect.call(opt, false); | ||||
|                 triggerAction(opt.id,false); | ||||
|             } else if (opt.hasOwnProperty("selected")) { | ||||
|                 if (opt.selected) { | ||||
|                     link.addClass("active"); | ||||
|                 } else { | ||||
|                     link.removeClass("active"); | ||||
|                 } | ||||
|                 opt.onselect.call(opt, opt.selected); | ||||
|                 triggerAction(opt.id,opt.selected); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -107,10 +104,12 @@ RED.menu = (function() { | ||||
|                             setSelected(opt.id, !selected); | ||||
|                         } | ||||
|                     } else { | ||||
|                         opt.onselect.call(opt); | ||||
|                         triggerAction(opt.id); | ||||
|                     } | ||||
|                 }); | ||||
|                 setInitialState(); | ||||
|                 if (opt.toggle) { | ||||
|                     setInitialState(); | ||||
|                 } | ||||
|             } else if (opt.href) { | ||||
|                 link.attr("target","_blank").attr("href",opt.href); | ||||
|             } else if (!opt.options) { | ||||
| @@ -164,6 +163,19 @@ RED.menu = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function triggerAction(id, args) { | ||||
|         var opt = menuItems[id]; | ||||
|         var callback = opt.onselect; | ||||
|         if (typeof opt.onselect === 'string') { | ||||
|             callback = RED.actions.get(opt.onselect); | ||||
|         } | ||||
|         if (callback) { | ||||
|             callback.call(opt,args); | ||||
|         } else { | ||||
|             console.log("No callback for",id,opt.onselect); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function isSavedStateActive(id) { | ||||
|         return RED.settings.get("menu-" + id); | ||||
|     } | ||||
| @@ -187,11 +199,15 @@ RED.menu = (function() { | ||||
|             $("#"+id).removeClass("active"); | ||||
|         } | ||||
|         if (opt && opt.onselect) { | ||||
|             opt.onselect.call(opt,state); | ||||
|             triggerAction(opt.id,state); | ||||
|         } | ||||
|         setSavedState(id, state); | ||||
|     } | ||||
|  | ||||
|     function toggleSelected(id) { | ||||
|         setSelected(id,!isSelected(id)); | ||||
|     } | ||||
|  | ||||
|     function setDisabled(id,state) { | ||||
|         if (state) { | ||||
|             $("#"+id).parent().addClass("disabled"); | ||||
| @@ -231,16 +247,6 @@ RED.menu = (function() { | ||||
|         var opt = menuItems[id]; | ||||
|         if (opt) { | ||||
|             opt.onselect = action; | ||||
|             // $("#"+id).click(function() { | ||||
|             //     if ($(this).parent().hasClass("disabled")) { | ||||
|             //         return; | ||||
|             //     } | ||||
|             //     if (menuItems[id].toggle) { | ||||
|             //         setSelected(id,!isSelected(id)); | ||||
|             //     } else { | ||||
|             //         menuItems[id].onselect.call(menuItems[id]); | ||||
|             //     } | ||||
|             // }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -248,6 +254,7 @@ RED.menu = (function() { | ||||
|         init: createMenu, | ||||
|         setSelected: setSelected, | ||||
|         isSelected: isSelected, | ||||
|         toggleSelected: toggleSelected, | ||||
|         setDisabled: setDisabled, | ||||
|         addItem: addItem, | ||||
|         removeItem: removeItem, | ||||
|   | ||||
| @@ -105,6 +105,9 @@ RED.tabs = (function() { | ||||
|             if (typeof link === "string") { | ||||
|                 link = ul.find("a[href='#"+link+"']"); | ||||
|             } | ||||
|             if (link.length === 0) { | ||||
|                 return; | ||||
|             } | ||||
|             if (!link.parent().hasClass("active")) { | ||||
|                 ul.children().removeClass("active"); | ||||
|                 ul.children().css({"transition": "width 100ms"}); | ||||
| @@ -126,6 +129,18 @@ RED.tabs = (function() { | ||||
|                 },100); | ||||
|             } | ||||
|         } | ||||
|         function activatePreviousTab() { | ||||
|             var previous = ul.find("li.active").prev(); | ||||
|             if (previous.length > 0) { | ||||
|                 activateTab(previous.find("a")); | ||||
|             } | ||||
|         } | ||||
|         function activateNextTab() { | ||||
|             var next = ul.find("li.active").next(); | ||||
|             if (next.length > 0) { | ||||
|                 activateTab(next.find("a")); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         function updateTabWidths() { | ||||
|             var tabs = ul.find("li.red-ui-tab"); | ||||
| @@ -303,6 +318,8 @@ RED.tabs = (function() { | ||||
|             }, | ||||
|             removeTab: removeTab, | ||||
|             activateTab: activateTab, | ||||
|             nextTab: activateNextTab, | ||||
|             previousTab: activatePreviousTab, | ||||
|             resize: updateTabWidths, | ||||
|             count: function() { | ||||
|                 return ul.find("li.red-ui-tab").size(); | ||||
|   | ||||
| @@ -14,87 +14,31 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
| (function($) { | ||||
|     function validateExpression(str) { | ||||
|         var length = str.length; | ||||
|         var start = 0; | ||||
|         var inString = false; | ||||
|         var inBox = false; | ||||
|         var quoteChar; | ||||
|         var v; | ||||
|         for (var i=0;i<length;i++) { | ||||
|             var c = str[i]; | ||||
|             if (!inString) { | ||||
|                 if (c === "'" || c === '"') { | ||||
|                     if (!inBox) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     inString = true; | ||||
|                     quoteChar = c; | ||||
|                     start = i+1; | ||||
|                 } else if (c === '.') { | ||||
|                     if (i===0 || i===length-1) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     // Next char is a-z | ||||
|                     if (!/[a-z0-9\$\_]/i.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                 } else if (c === '[') { | ||||
|                     if (i === 0) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     if (i===length-1) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     // Next char is either a quote or a number | ||||
|                     if (!/["'\d]/.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inBox = true; | ||||
|                 } else if (c === ']') { | ||||
|                     if (!inBox) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     if (start != i) { | ||||
|                         v = str.substring(start,i); | ||||
|                         if (!/^\d+$/.test(v)) { | ||||
|                             return false; | ||||
|                         } | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inBox = false; | ||||
|                 } else if (c === ' ') { | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (c === quoteChar) { | ||||
|                     // Next char must be a ] | ||||
|                     if (!/\]/.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inString = false; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         if (inBox || inString) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     var allOptions = { | ||||
|         msg: {value:"msg",label:"msg.",validate:validateExpression}, | ||||
|         flow: {value:"flow",label:"flow.",validate:validateExpression}, | ||||
|         global: {value:"global",label:"global.",validate:validateExpression}, | ||||
|         msg: {value:"msg",label:"msg.",validate:RED.utils.validatePropertyExpression}, | ||||
|         flow: {value:"flow",label:"flow.",validate:RED.utils.validatePropertyExpression}, | ||||
|         global: {value:"global",label:"global.",validate:RED.utils.validatePropertyExpression}, | ||||
|         str: {value:"str",label:"string",icon:"red/images/typedInput/az.png"}, | ||||
|         num: {value:"num",label:"number",icon:"red/images/typedInput/09.png",validate:/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/}, | ||||
|         bool: {value:"bool",label:"boolean",icon:"red/images/typedInput/bool.png",options:["true","false"]}, | ||||
|         json: {value:"json",label:"JSON",icon:"red/images/typedInput/json.png", validate: function(v) { try{JSON.parse(v);return true;}catch(e){return false;}}}, | ||||
|         re: {value:"re",label:"regular expression",icon:"red/images/typedInput/re.png"}, | ||||
|         date: {value:"date",label:"timestamp",hasValue:false} | ||||
|         date: {value:"date",label:"timestamp",hasValue:false}, | ||||
|         jsonata: { | ||||
|             value: "jsonata", | ||||
|             label: "expression", | ||||
|             icon: "red/images/typedInput/expr.png", | ||||
|             validate: function(v) { try{jsonata(v);return true;}catch(e){return false;}}, | ||||
|             expand:function() { | ||||
|                 var that = this; | ||||
|                 RED.editor.editExpression({ | ||||
|                     value: this.value().replace(/\t/g,"\n"), | ||||
|                     complete: function(v) { | ||||
|                         that.value(v.replace(/\n/g,"\t")); | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     var nlsd = false; | ||||
|  | ||||
| @@ -117,7 +61,7 @@ | ||||
|             this.uiSelect = this.elementDiv.wrap( "<div>" ).parent(); | ||||
|             var attrStyle = this.element.attr('style'); | ||||
|             var m; | ||||
|             if ((m = /width\s*:\s*(\d+%)/i.exec(attrStyle)) !== null) { | ||||
|             if ((m = /width\s*:\s*(\d+(%|px))/i.exec(attrStyle)) !== null) { | ||||
|                 this.element.css('width','100%'); | ||||
|                 this.uiSelect.width(m[1]); | ||||
|                 this.uiWidth = null; | ||||
| @@ -133,7 +77,7 @@ | ||||
|  | ||||
|             this.options.types = this.options.types||Object.keys(allOptions); | ||||
|  | ||||
|             this.selectTrigger = $('<a href="#"></a>').prependTo(this.uiSelect); | ||||
|             this.selectTrigger = $('<button tabindex="0"></button>').prependTo(this.uiSelect); | ||||
|             $('<i class="fa fa-sort-desc"></i>').appendTo(this.selectTrigger); | ||||
|             this.selectLabel = $('<span></span>').appendTo(this.selectTrigger); | ||||
|  | ||||
| @@ -160,32 +104,72 @@ | ||||
|             }) | ||||
|             this.selectTrigger.click(function(event) { | ||||
|                 event.preventDefault(); | ||||
|                 if (that.typeList.length > 1) { | ||||
|                     that._showMenu(that.menu,that.selectTrigger); | ||||
|                 } else { | ||||
|                     that.element.focus(); | ||||
|                 } | ||||
|                 that._showTypeMenu(); | ||||
|             }); | ||||
|             this.selectTrigger.on('keydown',function(evt) { | ||||
|                 if (evt.keyCode === 40) { | ||||
|                     // Down | ||||
|                     that._showTypeMenu(); | ||||
|                 } | ||||
|             }).on('focus', function() { | ||||
|                 that.uiSelect.addClass('red-ui-typedInput-focus'); | ||||
|             }) | ||||
|  | ||||
|             // explicitly set optionSelectTrigger display to inline-block otherwise jQ sets it to 'inline' | ||||
|             this.optionSelectTrigger = $('<a href="#" class="red-ui-typedInput-option-trigger" style="display:inline-block"><i class="fa fa-sort-desc"></i></a>').appendTo(this.uiSelect); | ||||
|             this.optionSelectLabel = $('<span></span>').prependTo(this.optionSelectTrigger); | ||||
|             this.optionSelectTrigger = $('<button tabindex="0" class="red-ui-typedInput-option-trigger" style="display:inline-block"><span class="red-ui-typedInput-option-caret"><i class="fa fa-sort-desc"></i></span></button>').appendTo(this.uiSelect); | ||||
|             this.optionSelectLabel = $('<span class="red-ui-typedInput-option-label"></span>').prependTo(this.optionSelectTrigger); | ||||
|             this.optionSelectTrigger.click(function(event) { | ||||
|                 event.preventDefault(); | ||||
|                 if (that.optionMenu) { | ||||
|                     that.optionMenu.css({ | ||||
|                         minWidth:that.optionSelectLabel.width() | ||||
|                     }); | ||||
|  | ||||
|                     that._showMenu(that.optionMenu,that.optionSelectLabel) | ||||
|                 that._showOptionSelectMenu(); | ||||
|             }).on('keydown', function(evt) { | ||||
|                 if (evt.keyCode === 40) { | ||||
|                     // Down | ||||
|                     that._showOptionSelectMenu(); | ||||
|                 } | ||||
|             }).on('blur', function() { | ||||
|                 that.uiSelect.removeClass('red-ui-typedInput-focus'); | ||||
|             }).on('focus', function() { | ||||
|                 that.uiSelect.addClass('red-ui-typedInput-focus'); | ||||
|             }); | ||||
|  | ||||
|             this.optionExpandButton = $('<button tabindex="0" class="red-ui-typedInput-option-expand" style="display:inline-block"><i class="fa fa-ellipsis-h"></i></button>').appendTo(this.uiSelect); | ||||
|  | ||||
|  | ||||
|             this.type(this.options.default||this.typeList[0].value); | ||||
|         }, | ||||
|         _showTypeMenu: function() { | ||||
|             if (this.typeList.length > 1) { | ||||
|                 this._showMenu(this.menu,this.selectTrigger); | ||||
|                 this.menu.find("[value='"+this.propertyType+"']").focus(); | ||||
|             } else { | ||||
|                 this.element.focus(); | ||||
|             } | ||||
|         }, | ||||
|         _showOptionSelectMenu: function() { | ||||
|             if (this.optionMenu) { | ||||
|                 this.optionMenu.css({ | ||||
|                     minWidth:this.optionSelectLabel.width() | ||||
|                 }); | ||||
|  | ||||
|                 this._showMenu(this.optionMenu,this.optionSelectLabel); | ||||
|                 var selectedOption = this.optionMenu.find("[value='"+this.value()+"']"); | ||||
|                 if (selectedOption.length === 0) { | ||||
|                     selectedOption = this.optionMenu.children(":first"); | ||||
|                 } | ||||
|                 selectedOption.focus(); | ||||
|  | ||||
|             } | ||||
|         }, | ||||
|         _hideMenu: function(menu) { | ||||
|             $(document).off("mousedown.close-property-select"); | ||||
|             menu.hide(); | ||||
|             this.element.focus(); | ||||
|             if (this.elementDiv.is(":visible")) { | ||||
|                 this.element.focus(); | ||||
|             } else if (this.optionSelectTrigger.is(":visible")){ | ||||
|                 this.optionSelectTrigger.focus(); | ||||
|             } else { | ||||
|                 this.selectTrigger.focus(); | ||||
|             } | ||||
|         }, | ||||
|         _createMenu: function(opts,callback) { | ||||
|             var that = this; | ||||
| @@ -194,7 +178,7 @@ | ||||
|                 if (typeof opt === 'string') { | ||||
|                     opt = {value:opt,label:opt}; | ||||
|                 } | ||||
|                 var op = $('<a href="#">').attr("value",opt.value).appendTo(menu); | ||||
|                 var op = $('<a href="#"></a>').attr("value",opt.value).appendTo(menu); | ||||
|                 if (opt.label) { | ||||
|                     op.text(opt.label); | ||||
|                 } | ||||
| @@ -214,6 +198,21 @@ | ||||
|                 display: "none", | ||||
|             }); | ||||
|             menu.appendTo(document.body); | ||||
|  | ||||
|             menu.on('keydown', function(evt) { | ||||
|                 if (evt.keyCode === 40) { | ||||
|                     // DOWN | ||||
|                     $(this).children(":focus").next().focus(); | ||||
|                 } else if (evt.keyCode === 38) { | ||||
|                     // UP | ||||
|                     $(this).children(":focus").prev().focus(); | ||||
|                 } else if (evt.keyCode === 27) { | ||||
|                     that._hideMenu(menu); | ||||
|                 } | ||||
|             }) | ||||
|  | ||||
|  | ||||
|  | ||||
|             return menu; | ||||
|  | ||||
|         }, | ||||
| @@ -267,13 +266,18 @@ | ||||
|                 this.uiSelect.width(this.uiWidth); | ||||
|             } | ||||
|             if (this.typeMap[this.propertyType] && this.typeMap[this.propertyType].hasValue === false) { | ||||
|                 this.selectTrigger.css('width',"100%"); | ||||
|                 this.selectTrigger.addClass("red-ui-typedInput-full-width"); | ||||
|             } else { | ||||
|                 this.selectTrigger.width('auto'); | ||||
|                 this.selectTrigger.removeClass("red-ui-typedInput-full-width"); | ||||
|                 var labelWidth = this._getLabelWidth(this.selectTrigger); | ||||
|                 this.elementDiv.css('left',labelWidth+"px"); | ||||
|                 if (this.optionExpandButton.is(":visible")) { | ||||
|                     this.elementDiv.css('right',"22px"); | ||||
|                 } else { | ||||
|                     this.elementDiv.css('right','0'); | ||||
|                 } | ||||
|                 if (this.optionSelectTrigger) { | ||||
|                     this.optionSelectTrigger.css('left',(labelWidth+5)+"px"); | ||||
|                     this.optionSelectTrigger.css({'left':(labelWidth)+"px",'width':'calc( 100% - '+labelWidth+'px )'}); | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
| @@ -341,6 +345,9 @@ | ||||
|                         this.selectLabel.text(opt.label); | ||||
|                     } | ||||
|                     if (opt.options) { | ||||
|                         if (this.optionExpandButton) { | ||||
|                             this.optionExpandButton.hide(); | ||||
|                         } | ||||
|                         if (this.optionSelectTrigger) { | ||||
|                             this.optionSelectTrigger.show(); | ||||
|                             this.elementDiv.hide(); | ||||
| @@ -374,6 +381,16 @@ | ||||
|                             } | ||||
|                             this.elementDiv.show(); | ||||
|                         } | ||||
|                         if (opt.expand && typeof opt.expand === 'function') { | ||||
|                             this.optionExpandButton.show(); | ||||
|                             this.optionExpandButton.off('click'); | ||||
|                             this.optionExpandButton.on('click',function(evt) { | ||||
|                                 evt.preventDefault(); | ||||
|                                 opt.expand.call(that); | ||||
|                             }) | ||||
|                         } else { | ||||
|                             this.optionExpandButton.hide(); | ||||
|                         } | ||||
|                         this.element.trigger('change',this.propertyType,this.value()); | ||||
|                     } | ||||
|                     if (image) { | ||||
|   | ||||
| @@ -30,13 +30,13 @@ RED.deploy = (function() { | ||||
|  | ||||
|     var deploymentType = "full"; | ||||
|  | ||||
|     var currentDiff = null; | ||||
|  | ||||
|     function changeDeploymentType(type) { | ||||
|         deploymentType = type; | ||||
|         $("#btn-deploy-icon").attr("src",deploymentTypes[type].img); | ||||
|     } | ||||
|  | ||||
|     var currentDiff = null; | ||||
|  | ||||
|     /** | ||||
|      * options: | ||||
|      *   type: "default" - Button with drop-down options - no further customisation available | ||||
| @@ -98,21 +98,33 @@ RED.deploy = (function() { | ||||
|                 height: "auto", | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         text: RED._("deploy.confirm.button.cancel"), | ||||
|                         text: RED._("common.label.cancel"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     // { | ||||
|                     //     id: "node-dialog-confirm-deploy-review", | ||||
|                     //     text: RED._("deploy.confirm.button.review"), | ||||
|                     //     class: "primary", | ||||
|                     //     click: function() { | ||||
|                     //         showDiff(); | ||||
|                     //         $( this ).dialog( "close" ); | ||||
|                     //     } | ||||
|                     // }, | ||||
|                     { | ||||
|                         id: "node-dialog-confirm-deploy-review", | ||||
|                         text: RED._("deploy.confirm.button.review"), | ||||
|                         class: "primary disabled", | ||||
|                         click: function() { | ||||
|                             if (!$("#node-dialog-confirm-deploy-review").hasClass('disabled')) { | ||||
|                                 RED.diff.showRemoteDiff(); | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "node-dialog-confirm-deploy-merge", | ||||
|                         text: RED._("deploy.confirm.button.merge"), | ||||
|                         class: "primary disabled", | ||||
|                         click: function() { | ||||
|                             RED.diff.mergeDiff(currentDiff); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "node-dialog-confirm-deploy-deploy", | ||||
|                         text: RED._("deploy.confirm.button.confirm"), | ||||
|                         class: "primary", | ||||
|                         click: function() { | ||||
| @@ -136,10 +148,37 @@ RED.deploy = (function() { | ||||
|                 }, | ||||
|                 open: function() { | ||||
|                     if ($( "#node-dialog-confirm-deploy-type" ).val() === "conflict") { | ||||
|                         // $("#node-dialog-confirm-deploy-review").show(); | ||||
|                         $("#node-dialog-confirm-deploy-deploy").hide(); | ||||
|                         $("#node-dialog-confirm-deploy-review").addClass('disabled').show(); | ||||
|                         $("#node-dialog-confirm-deploy-merge").addClass('disabled').show(); | ||||
|                         currentDiff = null; | ||||
|                         $("#node-dialog-confirm-deploy-conflict-checking").show(); | ||||
|                         $("#node-dialog-confirm-deploy-conflict-auto-merge").hide(); | ||||
|                         $("#node-dialog-confirm-deploy-conflict-manual-merge").hide(); | ||||
|  | ||||
|                         var now = Date.now(); | ||||
|                         RED.diff.getRemoteDiff(function(diff) { | ||||
|                             var ellapsed = Math.max(1000 - (Date.now()-now), 0); | ||||
|                             currentDiff = diff; | ||||
|                             setTimeout(function() { | ||||
|                                 $("#node-dialog-confirm-deploy-conflict-checking").hide(); | ||||
|                                 var d = Object.keys(diff.conflicts); | ||||
|                                 if (d.length === 0) { | ||||
|                                     $("#node-dialog-confirm-deploy-conflict-auto-merge").show(); | ||||
|                                     $("#node-dialog-confirm-deploy-merge").removeClass('disabled') | ||||
|                                 } else { | ||||
|                                     $("#node-dialog-confirm-deploy-conflict-manual-merge").show(); | ||||
|                                 } | ||||
|                                 $("#node-dialog-confirm-deploy-review").removeClass('disabled') | ||||
|                             },ellapsed); | ||||
|                         }) | ||||
|  | ||||
|  | ||||
|                         $("#node-dialog-confirm-deploy-hide").parent().hide(); | ||||
|                     } else { | ||||
|                         // $("#node-dialog-confirm-deploy-review").hide(); | ||||
|                         $("#node-dialog-confirm-deploy-deploy").show(); | ||||
|                         $("#node-dialog-confirm-deploy-review").hide(); | ||||
|                         $("#node-dialog-confirm-deploy-merge").hide(); | ||||
|                         $("#node-dialog-confirm-deploy-hide").parent().show(); | ||||
|                     } | ||||
|                 } | ||||
| @@ -157,198 +196,7 @@ RED.deploy = (function() { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         // $("#node-dialog-view-diff").dialog({ | ||||
|         //     title: RED._('deploy.confirm.button.review'), | ||||
|         //     modal: true, | ||||
|         //     autoOpen: false, | ||||
|         //     buttons: [ | ||||
|         //         { | ||||
|         //             text: RED._("deploy.confirm.button.cancel"), | ||||
|         //             click: function() { | ||||
|         //                 $( this ).dialog( "close" ); | ||||
|         //             } | ||||
|         //         }, | ||||
|         //         { | ||||
|         //             text: RED._("deploy.confirm.button.merge"), | ||||
|         //             class: "primary", | ||||
|         //             click: function() { | ||||
|         //                 $( this ).dialog( "close" ); | ||||
|         //             } | ||||
|         //         } | ||||
|         //     ], | ||||
|         //     open: function() { | ||||
|         //         $(this).dialog({width:Math.min($(window).width(),900),height:Math.min($(window).height(),600)}); | ||||
|         //     } | ||||
|         // }); | ||||
|  | ||||
|         // $("#node-dialog-view-diff-diff").editableList({ | ||||
|         //     addButton: false, | ||||
|         //     scrollOnAdd: false, | ||||
|         //     addItem: function(container,i,object) { | ||||
|         //         var tab = object.tab.n; | ||||
|         //         var tabDiv = $('<div>',{class:"node-diff-tab collapsed"}).appendTo(container); | ||||
|         // | ||||
|         //         var titleRow = $('<div>',{class:"node-diff-tab-title"}).appendTo(tabDiv); | ||||
|         //         titleRow.click(function(evt) { | ||||
|         //             evt.preventDefault(); | ||||
|         //             titleRow.parent().toggleClass('collapsed'); | ||||
|         //         }) | ||||
|         //         var chevron = $('<i class="fa fa-angle-down node-diff-chevron ">').appendTo(titleRow); | ||||
|         //         var title = $('<span>').html(tab.label||tab.id).appendTo(titleRow); | ||||
|         // | ||||
|         //         var stats = $('<span>',{class:"node-diff-tab-stats"}).appendTo(titleRow); | ||||
|         // | ||||
|         //         var addedCount = 0; | ||||
|         //         var deletedCount = 0; | ||||
|         //         var changedCount = 0; | ||||
|         //         var conflictedCount = 0; | ||||
|         // | ||||
|         //         object.tab.nodes.forEach(function(node) { | ||||
|         //             var realNode = RED.nodes.node(node.id); | ||||
|         //             var hasChanges = false; | ||||
|         //             if (currentDiff.added[node.id]) { | ||||
|         //                 addedCount++; | ||||
|         //                 hasChanges = true; | ||||
|         //             } | ||||
|         //             if (currentDiff.deleted[node.id]) { | ||||
|         //                 deletedCount++; | ||||
|         //                 hasChanges = true; | ||||
|         //             } | ||||
|         //             if (currentDiff.changed[node.id]) { | ||||
|         //                 changedCount++; | ||||
|         //                 hasChanges = true; | ||||
|         //             } | ||||
|         //             if (currentDiff.conflicted[node.id]) { | ||||
|         //                 conflictedCount++; | ||||
|         //                 hasChanges = true; | ||||
|         //             } | ||||
|         // | ||||
|         //             if (hasChanges) { | ||||
|         //                 var def = RED.nodes.getType(node.type)||{}; | ||||
|         //                 var div = $("<div>",{class:"node-diff-node-entry collapsed"}).appendTo(tabDiv); | ||||
|         //                 var nodeTitleDiv = $("<div>",{class:"node-diff-node-entry-title"}).appendTo(div); | ||||
|         //                 nodeTitleDiv.click(function(evt) { | ||||
|         //                     evt.preventDefault(); | ||||
|         //                     $(this).parent().toggleClass('collapsed'); | ||||
|         //                 }) | ||||
|         //                 var newNode = currentDiff.newConfig.all[node.id]; | ||||
|         //                 var nodePropertiesDiv = $("<div>",{class:"node-diff-node-entry-properties"}).appendTo(div); | ||||
|         // | ||||
|         //                 var nodePropertiesTable = $("<table>").appendTo(nodePropertiesDiv); | ||||
|         // | ||||
|         //                 if (node.hasOwnProperty('x')) { | ||||
|         //                     if (newNode.x !== node.x || newNode.y !== node.y) { | ||||
|         //                         var currentPosition = node.x+", "+node.y | ||||
|         //                         var newPosition = newNode.x+", "+newNode.y; | ||||
|         //                         $("<tr><td>position</td><td>"+currentPosition+"</td><td>"+newPosition+"</td></tr>").appendTo(nodePropertiesTable); | ||||
|         //                     } | ||||
|         //                 } | ||||
|         //                 var properties = Object.keys(node).filter(function(p) { return p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); | ||||
|         //                 if (def.defaults) { | ||||
|         //                     properties = properties.concat(Object.keys(def.defaults)); | ||||
|         //                 } | ||||
|         //                 properties.forEach(function(d) { | ||||
|         //                     var localValue = JSON.stringify(node[d]); | ||||
|         //                     var remoteValue = JSON.stringify(newNode[d]); | ||||
|         //                     var originalValue = realNode._config[d]; | ||||
|         // | ||||
|         //                     if (remoteValue !== originalValue) { | ||||
|         //                         var formattedProperty = formatNodeProperty(node[d]); | ||||
|         //                         var newFormattedProperty = formatNodeProperty(newNode[d]); | ||||
|         //                         if (localValue === originalValue) { | ||||
|         //                             // no conflict change | ||||
|         //                         } else { | ||||
|         //                             // conflicting change | ||||
|         //                         } | ||||
|         //                         $("<tr><td>"+d+'</td><td class="">'+formattedProperty+'</td><td class="node-diff-property-changed">'+newFormattedProperty+"</td></tr>").appendTo(nodePropertiesTable); | ||||
|         //                     } | ||||
|         // | ||||
|         //                 }) | ||||
|         //                 var nodeChevron = $('<i class="fa fa-angle-down node-diff-chevron">').appendTo(nodeTitleDiv); | ||||
|         // | ||||
|         // | ||||
|         //                 // var leftColumn = $('<div>',{class:"node-diff-column"}).appendTo(div); | ||||
|         //                 // var rightColumn = $('<div>',{class:"node-diff-column"}).appendTo(div); | ||||
|         //                 // rightColumn.html(" "); | ||||
|         // | ||||
|         // | ||||
|         // | ||||
|         //                 var nodeDiv = $("<div>",{class:"node-diff-node-entry-node"}).appendTo(nodeTitleDiv); | ||||
|         //                 var colour = def.color; | ||||
|         //                 var icon_url = "arrow-in.png"; | ||||
|         //                 if (node.type === 'tab') { | ||||
|         //                     colour = "#C0DEED"; | ||||
|         //                     icon_url = "subflow.png"; | ||||
|         //                 } else if (def.category === 'config') { | ||||
|         //                     icon_url = "cog.png"; | ||||
|         //                 } else if (node.type === 'unknown') { | ||||
|         //                     icon_url = "alert.png"; | ||||
|         //                 } else { | ||||
|         //                     icon_url = def.icon; | ||||
|         //                 } | ||||
|         //                 nodeDiv.css('backgroundColor',colour); | ||||
|         // | ||||
|         //                 var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv); | ||||
|         //                 $('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer); | ||||
|         // | ||||
|         // | ||||
|         // | ||||
|         //                 var contentDiv = $('<div>',{class:"node-diff-node-description"}).appendTo(nodeTitleDiv); | ||||
|         // | ||||
|         //                 $('<span>',{class:"node-diff-node-label"}).html(node.label || node.name || node.id).appendTo(contentDiv); | ||||
|         //                 //$('<div>',{class:"red-ui-search-result-node-type"}).html(node.type).appendTo(contentDiv); | ||||
|         //                 //$('<div>',{class:"red-ui-search-result-node-id"}).html(node.id).appendTo(contentDiv); | ||||
|         //             } | ||||
|         // | ||||
|         //         }); | ||||
|         // | ||||
|         //         var statsInfo = '<span class="node-diff-count">'+object.tab.nodes.length+" nodes"+ | ||||
|         //                         (addedCount+deletedCount+changedCount+conflictedCount > 0 ? " : ":"")+ | ||||
|         //                         "</span> "+ | ||||
|         //                         ((addedCount > 0)?'<span class="node-diff-added">'+addedCount+' added</span> ':'')+ | ||||
|         //                         ((deletedCount > 0)?'<span class="node-diff-deleted">'+deletedCount+' deleted</span> ':'')+ | ||||
|         //                         ((changedCount > 0)?'<span class="node-diff-changed">'+changedCount+' changed</span> ':'')+ | ||||
|         //                         ((conflictedCount > 0)?'<span class="node-diff-conflicted">'+conflictedCount+' conflicts</span>':''); | ||||
|         //         stats.html(statsInfo); | ||||
|         // | ||||
|         // | ||||
|         // | ||||
|         //         // | ||||
|         //         // | ||||
|         //         // | ||||
|         //         // var node = object.node; | ||||
|         //         // var realNode = RED.nodes.node(node.id); | ||||
|         //         // var def = RED.nodes.getType(object.node.type)||{}; | ||||
|         //         // var l = ""; | ||||
|         //         // if (def && def.label && realNode) { | ||||
|         //         //     l = def.label; | ||||
|         //         //     try { | ||||
|         //         //         l = (typeof l === "function" ? l.call(realNode) : l); | ||||
|         //         //     } catch(err) { | ||||
|         //         //         console.log("Definition error: "+node.type+".label",err); | ||||
|         //         //     } | ||||
|         //         // } | ||||
|         //         // l = l||node.label||node.name||node.id||""; | ||||
|         //         // console.log(node); | ||||
|         //         // var div = $('<div>').appendTo(container); | ||||
|         //         // div.html(l); | ||||
|         //     } | ||||
|         // }); | ||||
|     } | ||||
|  | ||||
|     function formatNodeProperty(prop) { | ||||
|         var formattedProperty = prop; | ||||
|         if (formattedProperty === null) { | ||||
|             formattedProperty = 'null'; | ||||
|         } else if (formattedProperty === undefined) { | ||||
|             formattedProperty = 'undefined'; | ||||
|         } else if (typeof formattedProperty === 'object') { | ||||
|             formattedProperty = JSON.stringify(formattedProperty); | ||||
|         } | ||||
|         if (/\n/.test(formattedProperty)) { | ||||
|             formattedProperty = "<pre>"+formattedProperty+"</pre>" | ||||
|         } | ||||
|         return formattedProperty; | ||||
|     } | ||||
|  | ||||
|     function getNodeInfo(node) { | ||||
| @@ -393,145 +241,8 @@ RED.deploy = (function() { | ||||
|         $( "#node-dialog-confirm-deploy-conflict" ).show(); | ||||
|         $( "#node-dialog-confirm-deploy-type" ).val("conflict"); | ||||
|         $( "#node-dialog-confirm-deploy" ).dialog( "open" ); | ||||
|  | ||||
|         // $("#node-dialog-confirm-deploy-review").append($('<img src="red/images/spin.svg" style="background: rgba(255,255,255,0.8); margin-top: -16px; margin-left: -8px; height:16px; position: absolute; "/>')); | ||||
|         // $("#node-dialog-confirm-deploy-review .ui-button-text").css("opacity",0.4); | ||||
|         // $("#node-dialog-confirm-deploy-review").attr("disabled",true).addClass("disabled"); | ||||
|         // $.ajax({ | ||||
|         //     headers: { | ||||
|         //         "Accept":"application/json", | ||||
|         //     }, | ||||
|         //     cache: false, | ||||
|         //     url: 'flows', | ||||
|         //     success: function(nodes) { | ||||
|         //         var newNodes = nodes.flows; | ||||
|         //         var newRevision = nodes.rev; | ||||
|         //         generateDiff(currentNodes,newNodes); | ||||
|         //         $("#node-dialog-confirm-deploy-review").attr("disabled",false).removeClass("disabled"); | ||||
|         //         $("#node-dialog-confirm-deploy-review img").remove(); | ||||
|         //         $("#node-dialog-confirm-deploy-review .ui-button-text").css("opacity",1); | ||||
|         //     } | ||||
|         // }); | ||||
|     } | ||||
|  | ||||
|     // function parseNodes(nodeList) { | ||||
|     //     var tabOrder = []; | ||||
|     //     var tabs = {}; | ||||
|     //     var subflows = {}; | ||||
|     //     var globals = []; | ||||
|     //     var all = {}; | ||||
|     // | ||||
|     //     nodeList.forEach(function(node) { | ||||
|     //         all[node.id] = node; | ||||
|     //         if (node.type === 'tab') { | ||||
|     //             tabOrder.push(node.id); | ||||
|     //             tabs[node.id] = {n:node,nodes:[]}; | ||||
|     //         } else if (node.type === 'subflow') { | ||||
|     //             subflows[node.id] = {n:node,nodes:[]}; | ||||
|     //         } | ||||
|     //     }); | ||||
|     // | ||||
|     //     nodeList.forEach(function(node) { | ||||
|     //         if (node.type !== 'tab' && node.type !== 'subflow') { | ||||
|     //             if (tabs[node.z]) { | ||||
|     //                 tabs[node.z].nodes.push(node); | ||||
|     //             } else if (subflows[node.z]) { | ||||
|     //                 subflows[node.z].nodes.push(node); | ||||
|     //             } else { | ||||
|     //                 globals.push(node); | ||||
|     //             } | ||||
|     //         } | ||||
|     //     }); | ||||
|     // | ||||
|     //     return { | ||||
|     //         all: all, | ||||
|     //         tabOrder: tabOrder, | ||||
|     //         tabs: tabs, | ||||
|     //         subflows: subflows, | ||||
|     //         globals: globals | ||||
|     //     } | ||||
|     // } | ||||
|  | ||||
|     // function generateDiff(currentNodes,newNodes) { | ||||
|     //     var currentConfig = parseNodes(currentNodes); | ||||
|     //     var newConfig = parseNodes(newNodes); | ||||
|     //     var pending = RED.nodes.pending(); | ||||
|     //     var added = {}; | ||||
|     //     var deleted = {}; | ||||
|     //     var changed = {}; | ||||
|     //     var conflicted = {}; | ||||
|     // | ||||
|     // | ||||
|     //     Object.keys(currentConfig.all).forEach(function(id) { | ||||
|     //         var node = RED.nodes.workspace(id)||RED.nodes.subflow(id)||RED.nodes.node(id); | ||||
|     //         if (!newConfig.all.hasOwnProperty(id)) { | ||||
|     //             if (!pending.added.hasOwnProperty(id)) { | ||||
|     //                 deleted[id] = true; | ||||
|     //                 conflicted[id] = node.changed; | ||||
|     //             } | ||||
|     //         } else if (JSON.stringify(currentConfig.all[id]) !== JSON.stringify(newConfig.all[id])) { | ||||
|     //             changed[id] = true; | ||||
|     //             conflicted[id] = node.changed; | ||||
|     //         } | ||||
|     //     }); | ||||
|     //     Object.keys(newConfig.all).forEach(function(id) { | ||||
|     //         if (!currentConfig.all.hasOwnProperty(id) && !pending.deleted.hasOwnProperty(id)) { | ||||
|     //             added[id] = true; | ||||
|     //         } | ||||
|     //     }); | ||||
|     // | ||||
|     //     // console.log("Added",added); | ||||
|     //     // console.log("Deleted",deleted); | ||||
|     //     // console.log("Changed",changed); | ||||
|     //     // console.log("Conflicted",conflicted); | ||||
|     // | ||||
|     //     var formatString = function(id) { | ||||
|     //         return conflicted[id]?"!":(added[id]?"+":(deleted[id]?"-":(changed[id]?"~":" "))); | ||||
|     //     } | ||||
|     //     newConfig.tabOrder.forEach(function(tabId) { | ||||
|     //         var tab = newConfig.tabs[tabId]; | ||||
|     //         console.log(formatString(tabId),"Flow:",tab.n.label, "("+tab.n.id+")"); | ||||
|     //         tab.nodes.forEach(function(node) { | ||||
|     //             console.log(" ",formatString(node.id),node.type,node.name || node.id); | ||||
|     //         }) | ||||
|     //         if (currentConfig.tabs[tabId]) { | ||||
|     //             currentConfig.tabs[tabId].nodes.forEach(function(node) { | ||||
|     //                 if (deleted[node.id]) { | ||||
|     //                     console.log(" ",formatString(node.id),node.type,node.name || node.id); | ||||
|     //                 } | ||||
|     //             }) | ||||
|     //         } | ||||
|     //     }); | ||||
|     //     currentConfig.tabOrder.forEach(function(tabId) { | ||||
|     //         if (deleted[tabId]) { | ||||
|     //             console.log(formatString(tabId),"Flow:",tab.n.label, "("+tab.n.id+")"); | ||||
|     //         } | ||||
|     //     }); | ||||
|     // | ||||
|     //     currentDiff = { | ||||
|     //         currentConfig: currentConfig, | ||||
|     //         newConfig: newConfig, | ||||
|     //         added: added, | ||||
|     //         deleted: deleted, | ||||
|     //         changed: changed, | ||||
|     //         conflicted: conflicted | ||||
|     //     } | ||||
|     // } | ||||
|  | ||||
|     // function showDiff() { | ||||
|     //     if (currentDiff) { | ||||
|     //         var list = $("#node-dialog-view-diff-diff"); | ||||
|     //         list.editableList('empty'); | ||||
|     //         var currentConfig = currentDiff.currentConfig; | ||||
|     //         currentConfig.tabOrder.forEach(function(tabId) { | ||||
|     //             var tab = currentConfig.tabs[tabId]; | ||||
|     //             list.editableList('addItem',{tab:tab}) | ||||
|     //         }); | ||||
|     //     } | ||||
|     //     $("#node-dialog-view-diff").dialog("open"); | ||||
|     // } | ||||
|  | ||||
|  | ||||
|     function save(skipValidation,force) { | ||||
|         if (!$("#btn-deploy").hasClass("disabled")) { | ||||
|             if (!skipValidation) { | ||||
| @@ -624,6 +335,7 @@ RED.deploy = (function() { | ||||
|             }).done(function(data,textStatus,xhr) { | ||||
|                 RED.nodes.dirty(false); | ||||
|                 RED.nodes.version(data.rev); | ||||
|                 RED.nodes.originalFlow(nns); | ||||
|                 if (hasUnusedConfig) { | ||||
|                     RED.notify( | ||||
|                     '<p>'+RED._("deploy.successfulDeploy")+'</p>'+ | ||||
|   | ||||
							
								
								
									
										1278
									
								
								editor/js/ui/diff.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1278
									
								
								editor/js/ui/diff.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -180,19 +180,31 @@ RED.editor = (function() { | ||||
|      * Marks the node as dirty and needing a size check. | ||||
|      * Removes any links to non-existant outputs. | ||||
|      * @param node - the node that has been updated | ||||
|      * @param outputMap - (optional) a map of old->new port numbers if wires should be moved | ||||
|      * @returns {array} the links that were removed due to this update | ||||
|      */ | ||||
|     function updateNodeProperties(node) { | ||||
|     function updateNodeProperties(node, outputMap) { | ||||
|         node.resize = true; | ||||
|         node.dirty = true; | ||||
|         var removedLinks = []; | ||||
|         if (node.ports) { | ||||
|             if (outputMap) { | ||||
|                 RED.nodes.eachLink(function(l) { | ||||
|                     if (l.source === node && outputMap.hasOwnProperty(l.sourcePort)) { | ||||
|                         if (outputMap[l.sourcePort] === -1) { | ||||
|                             removedLinks.push(l); | ||||
|                         } else { | ||||
|                             l.sourcePort = outputMap[l.sourcePort]; | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|             if (node.outputs < node.ports.length) { | ||||
|                 while (node.outputs < node.ports.length) { | ||||
|                     node.ports.pop(); | ||||
|                 } | ||||
|                 RED.nodes.eachLink(function(l) { | ||||
|                     if (l.source === node && l.sourcePort >= node.outputs) { | ||||
|                     if (l.source === node && l.sourcePort >= node.outputs && removedLinks.indexOf(l) === -1) { | ||||
|                         removedLinks.push(l); | ||||
|                     } | ||||
|                 }); | ||||
| @@ -482,12 +494,13 @@ RED.editor = (function() { | ||||
|     } | ||||
|  | ||||
|     function getEditStackTitle() { | ||||
|  | ||||
|         var title = '<ul class="editor-tray-breadcrumbs">'; | ||||
|         for (var i=0;i<editStack.length;i++) { | ||||
|             var node = editStack[i]; | ||||
|             var label = node.type; | ||||
|             if (node.type === 'subflow') { | ||||
|             if (node.type === '_expression') { | ||||
|                 label = "Expression editor"; | ||||
|             } else if (node.type === 'subflow') { | ||||
|                 label = RED._("subflow.editSubflow",{name:node.name}) | ||||
|             } else if (node.type.indexOf("subflow:")===0) { | ||||
|                 var subflow = RED.nodes.subflow(node.type.substring(8)); | ||||
| @@ -514,6 +527,33 @@ RED.editor = (function() { | ||||
|         return title; | ||||
|     } | ||||
|  | ||||
|     function buildEditForm(tray,formId,type,ns) { | ||||
|         var trayBody = tray.find('.editor-tray-body'); | ||||
|         var dialogForm = $('<form id="'+formId+'" class="form-horizontal"></form>').appendTo(trayBody); | ||||
|         dialogForm.html($("script[data-template-name='"+type+"']").html()); | ||||
|         ns = ns||"node-red"; | ||||
|         dialogForm.find('[data-i18n]').each(function() { | ||||
|             var current = $(this).attr("data-i18n"); | ||||
|             var keys = current.split(";"); | ||||
|             for (var i=0;i<keys.length;i++) { | ||||
|                 var key = keys[i]; | ||||
|                 if (key.indexOf(":") === -1) { | ||||
|                     var prefix = ""; | ||||
|                     if (key.indexOf("[")===0) { | ||||
|                         var parts = key.split("]"); | ||||
|                         prefix = parts[0]+"]"; | ||||
|                         key = parts[1]; | ||||
|                     } | ||||
|                     keys[i] = prefix+ns+":"+key; | ||||
|                 } | ||||
|             } | ||||
|             $(this).attr("data-i18n",keys.join(";")); | ||||
|         }); | ||||
|         $('<input type="text" style="display: none;" />').prependTo(dialogForm); | ||||
|         dialogForm.submit(function(e) { e.preventDefault();}); | ||||
|         return dialogForm; | ||||
|     } | ||||
|  | ||||
|     function showEditDialog(node) { | ||||
|         var editing_node = node; | ||||
|         editStack.push(node); | ||||
| @@ -525,6 +565,33 @@ RED.editor = (function() { | ||||
|         var trayOptions = { | ||||
|             title: getEditStackTitle(), | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     id: "node-dialog-delete", | ||||
|                     class: 'leftButton', | ||||
|                     text: RED._("common.label.delete"), | ||||
|                     click: function() { | ||||
|                         var startDirty = RED.nodes.dirty(); | ||||
|                         var removedNodes = []; | ||||
|                         var removedLinks = []; | ||||
|                         var removedEntities = RED.nodes.remove(editing_node.id); | ||||
|                         removedNodes.push(editing_node); | ||||
|                         removedNodes = removedNodes.concat(removedEntities.nodes); | ||||
|                         removedLinks = removedLinks.concat(removedEntities.links); | ||||
|  | ||||
|                         var historyEvent = { | ||||
|                             t:'delete', | ||||
|                             nodes:removedNodes, | ||||
|                             links:removedLinks, | ||||
|                             changes: {}, | ||||
|                             dirty: startDirty | ||||
|                         } | ||||
|  | ||||
|                         RED.nodes.dirty(true); | ||||
|                         RED.view.redraw(true); | ||||
|                         RED.history.push(historyEvent); | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     id: "node-dialog-cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
| @@ -570,6 +637,7 @@ RED.editor = (function() { | ||||
|                         var changed = false; | ||||
|                         var wasDirty = RED.nodes.dirty(); | ||||
|                         var d; | ||||
|                         var outputMap; | ||||
|  | ||||
|                         if (editing_node._def.oneditsave) { | ||||
|                             var oldValues = {}; | ||||
| @@ -654,8 +722,14 @@ RED.editor = (function() { | ||||
|                             var credsChanged = updateNodeCredentials(editing_node,credDefinition,prefix); | ||||
|                             changed = changed || credsChanged; | ||||
|                         } | ||||
|  | ||||
|                         var removedLinks = updateNodeProperties(editing_node); | ||||
|                         if (editing_node.hasOwnProperty("_outputs")) { | ||||
|                             outputMap = editing_node._outputs; | ||||
|                             delete editing_node._outputs; | ||||
|                             if (Object.keys(outputMap).length > 0) { | ||||
|                                 changed = true; | ||||
|                             } | ||||
|                         } | ||||
|                         var removedLinks = updateNodeProperties(editing_node,outputMap); | ||||
|                         if (changed) { | ||||
|                             var wasChanged = editing_node.changed; | ||||
|                             editing_node.changed = true; | ||||
| @@ -685,6 +759,9 @@ RED.editor = (function() { | ||||
|                                 dirty:wasDirty, | ||||
|                                 changed:wasChanged | ||||
|                             }; | ||||
|                             if (outputMap) { | ||||
|                                 historyEvent.outputMap = outputMap; | ||||
|                             } | ||||
|                             if (subflowInstances) { | ||||
|                                 historyEvent.subflow = { | ||||
|                                     instances:subflowInstances | ||||
| @@ -714,33 +791,13 @@ RED.editor = (function() { | ||||
|                 if (editing_node) { | ||||
|                     RED.sidebar.info.refresh(editing_node); | ||||
|                 } | ||||
|                 var trayBody = tray.find('.editor-tray-body'); | ||||
|                 var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody); | ||||
|                 dialogForm.html($("script[data-template-name='"+type+"']").html()); | ||||
|                 var ns; | ||||
|                 if (node._def.set.module === "node-red") { | ||||
|                     ns = "node-red"; | ||||
|                 } else { | ||||
|                     ns = node._def.set.id; | ||||
|                 } | ||||
|                 dialogForm.find('[data-i18n]').each(function() { | ||||
|                     var current = $(this).attr("data-i18n"); | ||||
|                     var keys = current.split(";"); | ||||
|                     for (var i=0;i<keys.length;i++) { | ||||
|                         var key = keys[i]; | ||||
|                         if (key.indexOf(":") === -1) { | ||||
|                             var prefix = ""; | ||||
|                             if (key.indexOf("[")===0) { | ||||
|                                 var parts = key.split("]"); | ||||
|                                 prefix = parts[0]+"]"; | ||||
|                                 key = parts[1]; | ||||
|                             } | ||||
|                             keys[i] = prefix+ns+":"+key; | ||||
|                         } | ||||
|                     } | ||||
|                     $(this).attr("data-i18n",keys.join(";")); | ||||
|                 }); | ||||
|                 $('<input type="text" style="display: none;" />').prependTo(dialogForm); | ||||
|                 var dialogForm = buildEditForm(tray,"dialog-form",type,ns); | ||||
|                 prepareEditDialog(node,node._def,"node-input"); | ||||
|                 dialogForm.i18n(); | ||||
|             }, | ||||
| @@ -833,7 +890,6 @@ RED.editor = (function() { | ||||
|             }, | ||||
|             open: function(tray) { | ||||
|                 var trayHeader = tray.find(".editor-tray-header"); | ||||
|                 var trayBody = tray.find(".editor-tray-body"); | ||||
|                 var trayFooter = tray.find(".editor-tray-footer"); | ||||
|  | ||||
|                 if (node_def.hasUsers !== false) { | ||||
| @@ -841,21 +897,8 @@ RED.editor = (function() { | ||||
|                 } | ||||
|                 trayFooter.append('<span id="node-config-dialog-scope-container"><span id="node-config-dialog-scope-warning" data-i18n="[title]editor.errors.scopeChange"><i class="fa fa-warning"></i></span><select id="node-config-dialog-scope"></select></span>'); | ||||
|  | ||||
|                 var dialogForm = $('<form id="node-config-dialog-edit-form" class="form-horizontal"></form>').appendTo(trayBody); | ||||
|                 dialogForm.html($("script[data-template-name='"+type+"']").html()); | ||||
|                 dialogForm.find('[data-i18n]').each(function() { | ||||
|                     var current = $(this).attr("data-i18n"); | ||||
|                     if (current.indexOf(":") === -1) { | ||||
|                         var prefix = ""; | ||||
|                         if (current.indexOf("[")===0) { | ||||
|                             var parts = current.split("]"); | ||||
|                             prefix = parts[0]+"]"; | ||||
|                             current = parts[1]; | ||||
|                         } | ||||
|                         $(this).attr("data-i18n",prefix+ns+":"+current); | ||||
|                     } | ||||
|                 }); | ||||
|                 $('<input type="text" style="display: none;" />').prependTo(dialogForm); | ||||
|                 var dialogForm = buildEditForm(tray,"node-config-dialog-edit-form",type,ns); | ||||
|  | ||||
|                 prepareEditDialog(editing_config_node,node_def,"node-config-input"); | ||||
|                 if (editing_config_node._def.exclusive) { | ||||
|                     $("#node-config-dialog-scope").hide(); | ||||
| @@ -1289,30 +1332,7 @@ RED.editor = (function() { | ||||
|                 if (editing_node) { | ||||
|                     RED.sidebar.info.refresh(editing_node); | ||||
|                 } | ||||
|                 var trayBody = tray.find('.editor-tray-body'); | ||||
|                 var dialogForm = $('<form id="dialog-form" class="form-horizontal"></form>').appendTo(trayBody); | ||||
|                 dialogForm.html($("script[data-template-name='subflow-template']").html()); | ||||
|                 var ns = "node-red"; | ||||
|                 dialogForm.find('[data-i18n]').each(function() { | ||||
|                     var current = $(this).attr("data-i18n"); | ||||
|                     var keys = current.split(";"); | ||||
|                     for (var i=0;i<keys.length;i++) { | ||||
|                         var key = keys[i]; | ||||
|                         if (key.indexOf(":") === -1) { | ||||
|                             var prefix = ""; | ||||
|                             if (key.indexOf("[")===0) { | ||||
|                                 var parts = key.split("]"); | ||||
|                                 prefix = parts[0]+"]"; | ||||
|                                 key = parts[1]; | ||||
|                             } | ||||
|                             keys[i] = prefix+ns+":"+key; | ||||
|                         } | ||||
|                     } | ||||
|                     $(this).attr("data-i18n",keys.join(";")); | ||||
|                 }); | ||||
|                 $('<input type="text" style="display: none;" />').prependTo(dialogForm); | ||||
|  | ||||
|                 dialogForm.submit(function(e) { e.preventDefault();}); | ||||
|                 var dialogForm = buildEditForm(tray,"dialog-form","subflow-template"); | ||||
|                 subflowEditor = RED.editor.createEditor({ | ||||
|                     id: 'subflow-input-info-editor', | ||||
|                     mode: 'ace/mode/markdown', | ||||
| @@ -1348,22 +1368,183 @@ RED.editor = (function() { | ||||
|         RED.tray.show(trayOptions); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function editExpression(options) { | ||||
|         var value = options.value; | ||||
|         var onComplete = options.complete; | ||||
|         var type = "_expression" | ||||
|         editStack.push({type:type}); | ||||
|         RED.view.state(RED.state.EDITING); | ||||
|         var expressionEditor; | ||||
|  | ||||
|         var trayOptions = { | ||||
|             title: getEditStackTitle(), | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     id: "node-dialog-cancel", | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     id: "node-dialog-ok", | ||||
|                     text: RED._("common.label.done"), | ||||
|                     class: "primary", | ||||
|                     click: function() { | ||||
|                         $("#node-input-expression-help").html(""); | ||||
|                         onComplete(expressionEditor.getValue()); | ||||
|                         RED.tray.close(); | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             resize: function(dimensions) { | ||||
|                 editTrayWidthCache[type] = dimensions.width; | ||||
|  | ||||
|                 var rows = $("#dialog-form>div:not(.node-text-editor-row)"); | ||||
|                 var editorRow = $("#dialog-form>div.node-text-editor-row"); | ||||
|                 var height = $("#dialog-form").height(); | ||||
|                 for (var i=0;i<rows.size();i++) { | ||||
|                     height -= $(rows[i]).outerHeight(true); | ||||
|                 } | ||||
|                 height -= (parseInt($("#dialog-form").css("marginTop"))+parseInt($("#dialog-form").css("marginBottom"))); | ||||
|                 $(".node-text-editor").css("height",height+"px"); | ||||
|                 expressionEditor.resize(); | ||||
|             }, | ||||
|             open: function(tray) { | ||||
|                 var trayBody = tray.find('.editor-tray-body'); | ||||
|                 var dialogForm = buildEditForm(tray,'dialog-form','_expression','editor'); | ||||
|                 var funcSelect = $("#node-input-expression-func"); | ||||
|                 Object.keys(jsonata.functions).forEach(function(f) { | ||||
|                     funcSelect.append($("<option></option>").val(f).text(f)); | ||||
|                 }) | ||||
|                 funcSelect.change(function(e) { | ||||
|                     var f = $(this).val(); | ||||
|                     var args = RED._('jsonata:'+f+".args",{defaultValue:''}); | ||||
|                     var title = "<h5>"+f+"("+args+")</h5>"; | ||||
|                     var body = marked(RED._('jsonata:'+f+'.desc',{defaultValue:''})); | ||||
|                     $("#node-input-expression-help").html(title+"<p>"+body+"</p>"); | ||||
|  | ||||
|                 }) | ||||
|                 expressionEditor = RED.editor.createEditor({ | ||||
|                     id: 'node-input-expression', | ||||
|                     value: "", | ||||
|                     mode:"ace/mode/jsonata", | ||||
|                     options: { | ||||
|                         enableBasicAutocompletion:true, | ||||
|                         enableSnippets:true, | ||||
|                         enableLiveAutocompletion: true | ||||
|                     } | ||||
|                 }); | ||||
|                 var currentToken = null; | ||||
|                 var currentTokenPos = -1; | ||||
|                 var currentFunctionMarker = null; | ||||
|  | ||||
|                 expressionEditor.getSession().setValue(value||"",-1); | ||||
|                 expressionEditor.on("changeSelection", function() { | ||||
|                     var c = expressionEditor.getCursorPosition(); | ||||
|                     var token = expressionEditor.getSession().getTokenAt(c.row,c.column); | ||||
|                     if (token !== currentToken || (token && /paren/.test(token.type) && c.column !== currentTokenPos)) { | ||||
|                         currentToken = token; | ||||
|                         var r,p; | ||||
|                         var scopedFunction = null; | ||||
|                         if (token && token.type === 'keyword') { | ||||
|                             r = c.row; | ||||
|                             scopedFunction = token; | ||||
|                         } else { | ||||
|                             var depth = 0; | ||||
|                             var next = false; | ||||
|                             if (token) { | ||||
|                                 if (token.type === 'paren.rparen') { | ||||
|                                     // If this is a block of parens ')))', set | ||||
|                                     // depth to offset against the cursor position | ||||
|                                     // within the block | ||||
|                                     currentTokenPos = c.column; | ||||
|                                     depth = c.column - (token.start + token.value.length); | ||||
|                                 } | ||||
|                                 r = c.row; | ||||
|                                 p = token.index; | ||||
|                             } else { | ||||
|                                 r = c.row-1; | ||||
|                                 p = -1; | ||||
|                             } | ||||
|                             while ( scopedFunction === null && r > -1) { | ||||
|                                 var rowTokens = expressionEditor.getSession().getTokens(r); | ||||
|                                 if (p === -1) { | ||||
|                                     p = rowTokens.length-1; | ||||
|                                 } | ||||
|                                 while (p > -1) { | ||||
|                                     var type = rowTokens[p].type; | ||||
|                                     if (next) { | ||||
|                                         if (type === 'keyword') { | ||||
|                                             scopedFunction = rowTokens[p]; | ||||
|                                             // console.log("HIT",scopedFunction); | ||||
|                                             break; | ||||
|                                         } | ||||
|                                         next = false; | ||||
|                                     } | ||||
|                                     if (type === 'paren.lparen') { | ||||
|                                         depth-=rowTokens[p].value.length; | ||||
|                                     } else if (type === 'paren.rparen') { | ||||
|                                         depth+=rowTokens[p].value.length; | ||||
|                                     } | ||||
|                                     if (depth < 0) { | ||||
|                                         next = true; | ||||
|                                         depth = 0; | ||||
|                                     } | ||||
|                                     // console.log(r,p,depth,next,rowTokens[p]); | ||||
|                                     p--; | ||||
|                                 } | ||||
|                                 if (!scopedFunction) { | ||||
|                                     r--; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         expressionEditor.session.removeMarker(currentFunctionMarker); | ||||
|                         if (scopedFunction) { | ||||
|                         //console.log(token,.map(function(t) { return t.type})); | ||||
|                             funcSelect.val(scopedFunction.value).change(); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 dialogForm.i18n(); | ||||
|                 $("#node-input-expression-func-insert").click(function(e) { | ||||
|                     e.preventDefault(); | ||||
|                     var pos = expressionEditor.getCursorPosition(); | ||||
|                     var f = funcSelect.val(); | ||||
|                     var snippet = jsonata.getFunctionSnippet(f); | ||||
|                     expressionEditor.insertSnippet(snippet); | ||||
|                     expressionEditor.focus(); | ||||
|                 }) | ||||
|             }, | ||||
|             close: function() { | ||||
|                 editStack.pop(); | ||||
|             }, | ||||
|             show: function() {} | ||||
|         } | ||||
|         if (editTrayWidthCache.hasOwnProperty(type)) { | ||||
|             trayOptions.width = editTrayWidthCache[type]; | ||||
|         } | ||||
|         RED.tray.show(trayOptions); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|             RED.tray.init(); | ||||
|             $(window).on('keydown', function(evt) { | ||||
|                 if (evt.keyCode === $.ui.keyCode.ESCAPE && (evt.metaKey || evt.ctrlKey)) { | ||||
|                     $("#node-dialog-cancel").click(); | ||||
|                     $("#node-config-dialog-cancel").click(); | ||||
|                 } else if (evt.keyCode === $.ui.keyCode.ENTER && (evt.metaKey || evt.ctrlKey)) { | ||||
|                     $("#node-dialog-ok").click(); | ||||
|                     $("#node-config-dialog-ok").click(); | ||||
|                 } | ||||
|             RED.actions.add("core:confirm-edit-tray", function() { | ||||
|                 $("#node-dialog-ok").click(); | ||||
|                 $("#node-config-dialog-ok").click(); | ||||
|             }); | ||||
|             RED.actions.add("core:cancel-edit-tray", function() { | ||||
|                 $("#node-dialog-cancel").click(); | ||||
|                 $("#node-config-dialog-cancel").click(); | ||||
|             }); | ||||
|         }, | ||||
|         edit: showEditDialog, | ||||
|         editConfig: showEditConfigNodeDialog, | ||||
|         editSubflow: showEditSubflowDialog, | ||||
|         editExpression: editExpression, | ||||
|         validateNode: validateNode, | ||||
|         updateNodeProperties: updateNodeProperties, // TODO: only exposed for edit-undo | ||||
|  | ||||
| @@ -1398,7 +1579,6 @@ RED.editor = (function() { | ||||
|                     } | ||||
|                 },100); | ||||
|             } | ||||
|  | ||||
|             return editor; | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -15,10 +15,101 @@ | ||||
|  **/ | ||||
| RED.keyboard = (function() { | ||||
|  | ||||
|     var isMac = /Mac/i.test(window.navigator.platform); | ||||
|  | ||||
|     var handlers = {}; | ||||
|     var partialState; | ||||
|  | ||||
|     var keyMap = { | ||||
|         "left":37, | ||||
|         "up":38, | ||||
|         "right":39, | ||||
|         "down":40, | ||||
|         "escape":27, | ||||
|         "enter": 13, | ||||
|         "backspace": 8, | ||||
|         "delete": 46, | ||||
|         "space": 32, | ||||
|         ";":186, | ||||
|         "=":187, | ||||
|         ",":188, | ||||
|         "-":189, | ||||
|         ".":190, | ||||
|         "/":191, | ||||
|         "\\":220, | ||||
|         "'":222, | ||||
|         "?":191 // <- QWERTY specific | ||||
|     } | ||||
|     var metaKeyCodes = { | ||||
|         16:true, | ||||
|         17:true, | ||||
|         18: true, | ||||
|         91:true, | ||||
|         93: true | ||||
|     } | ||||
|     var actionToKeyMap = {} | ||||
|  | ||||
|     // FF generates some different keycodes because reasons. | ||||
|     var firefoxKeyCodeMap = { | ||||
|         59:186, | ||||
|         61:187, | ||||
|         173:189 | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         $.getJSON("red/keymap.json",function(data) { | ||||
|             for (var scope in data) { | ||||
|                 if (data.hasOwnProperty(scope)) { | ||||
|                     var keys = data[scope]; | ||||
|                     for (var key in keys) { | ||||
|                         if (keys.hasOwnProperty(key)) { | ||||
|                             addHandler(scope,key,keys[key]); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         RED.actions.add("core:show-help", showKeyboardHelp); | ||||
|  | ||||
|     } | ||||
|     function parseKeySpecifier(key) { | ||||
|         var parts = key.toLowerCase().split("-"); | ||||
|         var modifiers = {}; | ||||
|         var keycode; | ||||
|         var blank = 0; | ||||
|         for (var i=0;i<parts.length;i++) { | ||||
|             switch(parts[i]) { | ||||
|                 case "ctrl": | ||||
|                 case "cmd": | ||||
|                     modifiers.ctrl = true; | ||||
|                     modifiers.meta = true; | ||||
|                     break; | ||||
|                 case "alt": | ||||
|                     modifiers.alt = true; | ||||
|                     break; | ||||
|                 case "shift": | ||||
|                     modifiers.shift = true; | ||||
|                     break; | ||||
|                 case "": | ||||
|                     blank++; | ||||
|                     keycode = keyMap["-"]; | ||||
|                     break; | ||||
|                 default: | ||||
|                     if (keyMap.hasOwnProperty(parts[i])) { | ||||
|                         keycode = keyMap[parts[i]]; | ||||
|                     } else if (parts[i].length > 1) { | ||||
|                         return null; | ||||
|                     } else { | ||||
|                         keycode = parts[i].toUpperCase().charCodeAt(0); | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         return [keycode,modifiers]; | ||||
|     } | ||||
|  | ||||
|     function resolveKeyEvent(evt) { | ||||
|         var slot = handlers; | ||||
|         var slot = partialState||handlers; | ||||
|         if (evt.ctrlKey || evt.metaKey) { | ||||
|             slot = slot.ctrl; | ||||
|         } | ||||
| @@ -28,9 +119,19 @@ RED.keyboard = (function() { | ||||
|         if (slot && evt.altKey) { | ||||
|             slot = slot.alt; | ||||
|         } | ||||
|         if (slot && slot[evt.keyCode]) { | ||||
|             var handler = slot[evt.keyCode]; | ||||
|             if (handler.scope && handler.scope !== "*") { | ||||
|         var keyCode = firefoxKeyCodeMap[evt.keyCode] || evt.keyCode; | ||||
|         if (slot && slot[keyCode]) { | ||||
|             var handler = slot[keyCode]; | ||||
|             if (!handler.scope) { | ||||
|                 if (partialState) { | ||||
|                     partialState = null; | ||||
|                     return resolveKeyEvent(evt); | ||||
|                 } else { | ||||
|                     partialState = handler; | ||||
|                     evt.preventDefault(); | ||||
|                     return null; | ||||
|                 } | ||||
|             } else if (handler.scope && handler.scope !== "*") { | ||||
|                 var target = evt.target; | ||||
|                 while (target.nodeName !== 'BODY' && target.id !== handler.scope) { | ||||
|                     target = target.parentElement; | ||||
| @@ -39,66 +140,123 @@ RED.keyboard = (function() { | ||||
|                     handler = null; | ||||
|                 } | ||||
|             } | ||||
|             partialState = null; | ||||
|             return handler; | ||||
|         } else if (partialState) { | ||||
|             partialState = null; | ||||
|             return resolveKeyEvent(evt); | ||||
|         } | ||||
|     } | ||||
|     d3.select(window).on("keydown",function() { | ||||
|         if (metaKeyCodes[d3.event.keyCode]) { | ||||
|             return; | ||||
|         } | ||||
|         var handler = resolveKeyEvent(d3.event); | ||||
|         if (handler && handler.ondown) { | ||||
|             handler.ondown(); | ||||
|         } | ||||
|     }); | ||||
|     d3.select(window).on("keyup",function() { | ||||
|         var handler = resolveKeyEvent(d3.event); | ||||
|         if (handler && handler.onup) { | ||||
|             handler.onup(); | ||||
|             if (typeof handler.ondown === "string") { | ||||
|                 RED.actions.invoke(handler.ondown); | ||||
|             } else { | ||||
|                 handler.ondown(); | ||||
|             } | ||||
|             d3.event.preventDefault(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     function addHandler(scope,key,modifiers,ondown,onup) { | ||||
|     function addHandler(scope,key,modifiers,ondown) { | ||||
|         var mod = modifiers; | ||||
|         var cbdown = ondown; | ||||
|         var cbup = onup; | ||||
|         if (typeof modifiers == "function") { | ||||
|         if (typeof modifiers == "function" || typeof modifiers === "string") { | ||||
|             mod = {}; | ||||
|             cbdown = modifiers; | ||||
|             cbup = ondown; | ||||
|         } | ||||
|         var keys = []; | ||||
|         var i=0; | ||||
|         if (typeof key === 'string') { | ||||
|             if (typeof cbdown === 'string') { | ||||
|                 actionToKeyMap[cbdown] = {scope:scope,key:key}; | ||||
|             } | ||||
|             var parts = key.split(" "); | ||||
|             for (i=0;i<parts.length;i++) { | ||||
|                 var parsedKey = parseKeySpecifier(parts[i]); | ||||
|                 if (parsedKey) { | ||||
|                     keys.push(parsedKey); | ||||
|                 } else { | ||||
|                     console.log("Unrecognised key specifier:",key) | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             keys.push([key,mod]) | ||||
|         } | ||||
|         var slot = handlers; | ||||
|         if (mod.ctrl) { | ||||
|             slot.ctrl = slot.ctrl||{}; | ||||
|             slot = slot.ctrl; | ||||
|         for (i=0;i<keys.length;i++) { | ||||
|             key = keys[i][0]; | ||||
|             mod = keys[i][1]; | ||||
|             if (mod.ctrl) { | ||||
|                 slot.ctrl = slot.ctrl||{}; | ||||
|                 slot = slot.ctrl; | ||||
|             } | ||||
|             if (mod.shift) { | ||||
|                 slot.shift = slot.shift||{}; | ||||
|                 slot = slot.shift; | ||||
|             } | ||||
|             if (mod.alt) { | ||||
|                 slot.alt = slot.alt||{}; | ||||
|                 slot = slot.alt; | ||||
|             } | ||||
|             slot[key] = slot[key] || {}; | ||||
|             slot = slot[key]; | ||||
|             //slot[key] = {scope: scope, ondown:cbdown}; | ||||
|         } | ||||
|         if (mod.shift) { | ||||
|             slot.shift = slot.shift||{}; | ||||
|             slot = slot.shift; | ||||
|         } | ||||
|         if (mod.alt) { | ||||
|             slot.alt = slot.alt||{}; | ||||
|             slot = slot.alt; | ||||
|         } | ||||
|         slot[key] = {scope: scope, ondown:cbdown, onup:cbup}; | ||||
|         slot.scope = scope; | ||||
|         slot.ondown = cbdown; | ||||
|     } | ||||
|  | ||||
|     function removeHandler(key,modifiers) { | ||||
|         var mod = modifiers || {}; | ||||
|         var keys = []; | ||||
|         var i=0; | ||||
|         if (typeof key === 'string') { | ||||
|             delete actionToKeyMap[key]; | ||||
|             var parts = key.split(" "); | ||||
|             for (i=0;i<parts.length;i++) { | ||||
|                 var parsedKey = parseKeySpecifier(parts[i]); | ||||
|                 if (parsedKey) { | ||||
|                     keys.push(parsedKey); | ||||
|                 } else { | ||||
|                     console.log("Unrecognised key specifier:",key) | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             keys.push([key,mod]) | ||||
|         } | ||||
|         var slot = handlers; | ||||
|         if (mod.ctrl) { | ||||
|             slot = slot.ctrl; | ||||
|         } | ||||
|         if (slot && mod.shift) { | ||||
|             slot = slot.shift; | ||||
|         } | ||||
|         if (slot && mod.alt) { | ||||
|             slot = slot.alt; | ||||
|         } | ||||
|         if (slot) { | ||||
|             delete slot[key]; | ||||
|         for (i=0;i<keys.length;i++) { | ||||
|             key = keys[i][0]; | ||||
|             mod = keys[i][1]; | ||||
|             if (mod.ctrl) { | ||||
|                 slot = slot.ctrl; | ||||
|             } | ||||
|             if (slot && mod.shift) { | ||||
|                 slot = slot.shift; | ||||
|             } | ||||
|             if (slot && mod.alt) { | ||||
|                 slot = slot.alt; | ||||
|             } | ||||
|             if (!slot[key]) { | ||||
|                 return; | ||||
|             } | ||||
|             slot = slot[key]; | ||||
|         } | ||||
|         delete slot.scope; | ||||
|         delete slot.ondown; | ||||
|     } | ||||
|  | ||||
|     var dialog = null; | ||||
|  | ||||
|     var cmdCtrlKey = '<span class="help-key">'+(isMac?'⌘':'Ctrl')+'</span>'; | ||||
|  | ||||
|     function showKeyboardHelp() { | ||||
|         if (!RED.settings.theme("menu.menu-item-keyboard-shortcuts",true)) { | ||||
|             return; | ||||
| @@ -107,33 +265,30 @@ RED.keyboard = (function() { | ||||
|             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>'+cmdCtrlKey+' + <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>'+cmdCtrlKey+' + <span class="help-key">Click</span></td><td>'+RED._("keyboard.addRemoveNode")+'</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>'+ | ||||
|                         '<tr><td><span class="help-key">Enter</span></td><td>'+RED._("keyboard.editSelected")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Delete</span> / <span class="help-key">Backspace</span></td><td>'+RED._("keyboard.deleteSelected")+'</td></tr>'+ | ||||
|                         '<tr><td> </td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key"> ← </span> <span class="help-key"> ↑ </span> <span class="help-key"> → </span> <span class="help-key"> ↓ </span></td><td>'+RED._("keyboard.nudgeNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key"> ← </span></td><td rowspan="4">'+RED._("keyboard.moveNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key"> ↑ </span></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key"> → </span></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key"> ↓ </span></td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">i</span></td><td>'+RED._("keyboard.importNode")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <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><span class="help-key">Ctrl/⌘</span> + <span class="help-key">.</span></td><td>'+RED._("keyboard.searchBox")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">Shift</span> + <span class="help-key">p</span></td><td>'+RED._("keyboard.managePalette")+'</td></tr>'+ | ||||
|                         '<tr><td></td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Delete</span></td><td rowspan="2">'+RED._("keyboard.deleteSelected")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Backspace</span></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>'+ | ||||
|                         '<tr><td><span class="help-key">Ctrl/⌘</span> + <span class="help-key">z</span></td><td>'+RED._("keyboard.undoChange")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">Space</span></td><td>'+RED._("keyboard.toggleSidebar")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">f</span></td><td>'+RED._("keyboard.searchBox")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">Shift</span> + <span class="help-key">p</span></td><td>'+RED._("keyboard.managePalette")+'</td></tr>'+ | ||||
|                         '<tr><td> </td><td></td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">←</span> <span class="help-key">↑</span> <span class="help-key">→</span> <span class="help-key">↓</span></td><td>'+RED._("keyboard.nudgeNode")+'</td></tr>'+ | ||||
|                         '<tr><td><span class="help-key">Shift</span> + <span class="help-key">←</span> <span class="help-key">↑</span> <span class="help-key">→</span> <span class="help-key">↓</span></td><td>'+RED._("keyboard.moveNode")+'</td></tr>'+ | ||||
|                         '<tr><td> </td><td></td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">c</span></td><td>'+RED._("keyboard.copyNode")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">x</span></td><td>'+RED._("keyboard.cutNode")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">v</span></td><td>'+RED._("keyboard.pasteNode")+'</td></tr>'+ | ||||
|                         '<tr><td>'+cmdCtrlKey+' + <span class="help-key">z</span></td><td>'+RED._("keyboard.undoChange")+'</td></tr>'+ | ||||
|                     '</table>'+ | ||||
|                 '</div>'+ | ||||
|                 '</div>') | ||||
| @@ -149,11 +304,24 @@ RED.keyboard = (function() { | ||||
|  | ||||
|         dialog.dialog("open"); | ||||
|     } | ||||
|     function formatKey(key) { | ||||
|         var formattedKey = isMac?key.replace(/ctrl-?/,"⌘"):key; | ||||
|         formattedKey = formattedKey.replace(/shift-?/,"⇧") | ||||
|         formattedKey = formattedKey.replace(/left/,"←") | ||||
|         formattedKey = formattedKey.replace(/up/,"↑") | ||||
|         formattedKey = formattedKey.replace(/right/,"→") | ||||
|         formattedKey = formattedKey.replace(/down/,"↓") | ||||
|         return '<span class="help-key-block"><span class="help-key">'+formattedKey.split(" ").join('</span> <span class="help-key">')+'</span></span>'; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         add: addHandler, | ||||
|         remove: removeHandler, | ||||
|         showHelp: showKeyboardHelp | ||||
|         getShortcut: function(actionName) { | ||||
|             return actionToKeyMap[actionName]; | ||||
|         }, | ||||
|         formatKey: formatKey | ||||
|     } | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -410,6 +410,9 @@ RED.library = (function() { | ||||
|  | ||||
|     return { | ||||
|         init: function() { | ||||
|  | ||||
|             RED.actions.add("core:library-export",exportFlow); | ||||
|  | ||||
|             RED.events.on("view:selection-changed",function(selection) { | ||||
|                 if (!selection.nodes) { | ||||
|                     RED.menu.setDisabled("menu-item-export",true); | ||||
|   | ||||
| @@ -51,11 +51,17 @@ RED.notify = (function() { | ||||
|  | ||||
|         n.update = (function() { | ||||
|             var nn = n; | ||||
|             return function(msg) { | ||||
|             return function(msg,timeout) { | ||||
|                 nn.innerHTML = msg; | ||||
|                 if (timeout !== undefined && timeout > 0) { | ||||
|                     window.clearTimeout(nn.timeoutid); | ||||
|                     nn.timeoutid = window.setTimeout(nn.close,timeout); | ||||
|                 } else { | ||||
|                     window.clearTimeout(nn.timeoutid); | ||||
|                 } | ||||
|             } | ||||
|         })(); | ||||
|          | ||||
|  | ||||
|         if (!fixed) { | ||||
|             $(n).click((function() { | ||||
|                 var nn = n; | ||||
|   | ||||
| @@ -304,10 +304,10 @@ RED.palette.editor = (function() { | ||||
|             filterInput.focus(); | ||||
|         },250); | ||||
|         RED.events.emit("palette-editor:open"); | ||||
|         RED.keyboard.add("*",/* ESCAPE */ 27,function(){hidePaletteEditor();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("*","escape",function(){hidePaletteEditor()}); | ||||
|     } | ||||
|     function hidePaletteEditor() { | ||||
|         RED.keyboard.remove("*"); | ||||
|         RED.keyboard.remove("escape"); | ||||
|         $("#main-container").removeClass("palette-expanded"); | ||||
|         $("#header-shade").hide(); | ||||
|         $("#editor-shade").hide(); | ||||
| @@ -424,8 +424,10 @@ RED.palette.editor = (function() { | ||||
|         RED.events.on("editor:close",function() { disabled = false; }); | ||||
|         RED.events.on("search:open",function() { disabled = true; }); | ||||
|         RED.events.on("search:close",function() { disabled = false; }); | ||||
|         RED.events.on("type-search:open",function() { disabled = true; }); | ||||
|         RED.events.on("type-search:close",function() { disabled = false; }); | ||||
|  | ||||
|         RED.keyboard.add("*", /* p */ 80,{shift:true,ctrl:true},function() {RED.palette.editor.show();d3.event.preventDefault();}); | ||||
|         RED.actions.add("core:manage-palette",RED.palette.editor.show); | ||||
|  | ||||
|         editorTabs = RED.tabs.create({ | ||||
|             id:"palette-editor-tabs", | ||||
| @@ -631,7 +633,7 @@ RED.palette.editor = (function() { | ||||
|  | ||||
|  | ||||
|         $('<span>').html(RED._("palette.editor.sort")+' ').appendTo(toolBar); | ||||
|         var sortGroup = $('<span class="button-group"></span> ').appendTo(toolBar); | ||||
|         var sortGroup = $('<span class="button-group"></span>').appendTo(toolBar); | ||||
|         var sortAZ = $('<a href="#" class="sidebar-header-button-toggle selected" data-i18n="palette.editor.sortAZ"></a>').appendTo(sortGroup); | ||||
|         var sortRecent = $('<a href="#" class="sidebar-header-button-toggle" data-i18n="palette.editor.sortRecent"></a>').appendTo(sortGroup); | ||||
|  | ||||
| @@ -754,7 +756,9 @@ RED.palette.editor = (function() { | ||||
|             refreshNodeModule(ns.module); | ||||
|             for (var i=0;i<filteredList.length;i++) { | ||||
|                 if (filteredList[i].info.id === ns.module) { | ||||
|                     filteredList[i].elements.installButton.hide(); | ||||
|                     var installButton = filteredList[i].elements.installButton; | ||||
|                     installButton.addClass('disabled'); | ||||
|                     installButton.html(RED._('palette.editor.installed')); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| @@ -768,7 +772,9 @@ RED.palette.editor = (function() { | ||||
|                     delete nodeEntries[ns.module]; | ||||
|                     for (var i=0;i<filteredList.length;i++) { | ||||
|                         if (filteredList[i].info.id === ns.module) { | ||||
|                             filteredList[i].elements.installButton.show(); | ||||
|                             var installButton = filteredList[i].elements.installButton; | ||||
|                             installButton.removeClass('disabled'); | ||||
|                             installButton.html(RED._('palette.editor.install')); | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|   | ||||
| @@ -45,6 +45,9 @@ RED.search = (function() { | ||||
|  | ||||
|  | ||||
|         var properties = ['id','type','name','label','info']; | ||||
|         if (n._def && n._def.defaults) { | ||||
|             properties = properties.concat(Object.keys(n._def.defaults)); | ||||
|         } | ||||
|         for (var i=0;i<properties.length;i++) { | ||||
|             if (n.hasOwnProperty(properties[i])) { | ||||
|                 var v = n[properties[i]]; | ||||
| @@ -238,8 +241,11 @@ RED.search = (function() { | ||||
|     } | ||||
|  | ||||
|     function show() { | ||||
|         if (disabled) { | ||||
|             return; | ||||
|         } | ||||
|         if (!visible) { | ||||
|             RED.keyboard.add("*",/* ESCAPE */ 27,function(){hide();d3.event.preventDefault();}); | ||||
|             RED.keyboard.add("*","escape",function(){hide()}); | ||||
|             $("#header-shade").show(); | ||||
|             $("#editor-shade").show(); | ||||
|             $("#palette-shade").show(); | ||||
| @@ -257,7 +263,7 @@ RED.search = (function() { | ||||
|     } | ||||
|     function hide() { | ||||
|         if (visible) { | ||||
|             RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|             RED.keyboard.remove("escape"); | ||||
|             visible = false; | ||||
|             $("#header-shade").hide(); | ||||
|             $("#editor-shade").hide(); | ||||
| @@ -274,11 +280,14 @@ RED.search = (function() { | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|         RED.keyboard.add("*",/* . */ 190,{ctrl:true},function(){if (!disabled) { show(); } d3.event.preventDefault();}); | ||||
|         RED.actions.add("core:search",show); | ||||
|  | ||||
|         RED.events.on("editor:open",function() { disabled = true; }); | ||||
|         RED.events.on("editor:close",function() { disabled = false; }); | ||||
|         RED.events.on("palette-editor:open",function() { disabled = true; }); | ||||
|         RED.events.on("palette-editor:close",function() { disabled = false; }); | ||||
|         RED.events.on("type-search:open",function() { disabled = true; }); | ||||
|         RED.events.on("type-search:close",function() { disabled = false; }); | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -202,12 +202,18 @@ RED.sidebar = (function() { | ||||
|     } | ||||
|  | ||||
|     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();}); | ||||
|         RED.actions.add("core:toggle-sidebar",function(state){ | ||||
|             if (state === undefined) { | ||||
|                 RED.menu.toggleSelected("menu-item-sidebar"); | ||||
|             } else { | ||||
|                 toggleSidebar(state); | ||||
|             } | ||||
|         }); | ||||
|         showSidebar(); | ||||
|         RED.sidebar.info.init(); | ||||
|         RED.sidebar.config.init(); | ||||
|         // hide info bar at start if screen rather narrow... | ||||
|         if ($(window).width() < 600) { toggleSidebar(); } | ||||
|         if ($(window).width() < 600) { RED.menu.setSelected("menu-item-sidebar",false); } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2013 IBM Corp. | ||||
|  * Copyright 2013, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -22,5 +22,6 @@ RED.state = { | ||||
|     EDITING: 5, | ||||
|     EXPORT: 6, | ||||
|     IMPORT: 7, | ||||
|     IMPORT_DRAGGING: 8 | ||||
|     IMPORT_DRAGGING: 8, | ||||
|     QUICK_JOINING: 9 | ||||
| } | ||||
|   | ||||
| @@ -306,50 +306,13 @@ RED.subflow = (function() { | ||||
|  | ||||
|         $("#workspace-subflow-delete").click(function(event) { | ||||
|             event.preventDefault(); | ||||
|             var removedNodes = []; | ||||
|             var removedLinks = []; | ||||
|             var startDirty = RED.nodes.dirty(); | ||||
|             var historyEvent = removeSubflow(RED.workspaces.active()); | ||||
|             historyEvent.t = 'delete'; | ||||
|             historyEvent.dirty = startDirty; | ||||
|  | ||||
|             var activeSubflow = getSubflow(); | ||||
|             RED.history.push(historyEvent); | ||||
|  | ||||
|             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); | ||||
| @@ -362,7 +325,48 @@ RED.subflow = (function() { | ||||
|         $("#chart").css({"margin-top": "0"}); | ||||
|     } | ||||
|  | ||||
|     function removeSubflow(id) { | ||||
|         var removedNodes = []; | ||||
|         var removedLinks = []; | ||||
|  | ||||
|         var activeSubflow = RED.nodes.subflow(id); | ||||
|  | ||||
|         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.workspaces.remove(activeSubflow); | ||||
|         RED.nodes.dirty(true); | ||||
|         RED.view.redraw(); | ||||
|  | ||||
|         return { | ||||
|             nodes:removedNodes, | ||||
|             links:removedLinks, | ||||
|             subflow: { | ||||
|                 subflow: activeSubflow | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function init() { | ||||
|         RED.events.on("workspace:change",function(event) { | ||||
|             var activeSubflow = RED.nodes.subflow(event.workspace); | ||||
| @@ -380,6 +384,8 @@ RED.subflow = (function() { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:create-subflow",createSubflow); | ||||
|         RED.actions.add("core:convert-to-subflow",convertToSubflow); | ||||
|     } | ||||
|  | ||||
|     function createSubflow() { | ||||
| @@ -617,6 +623,7 @@ RED.subflow = (function() { | ||||
|         init: init, | ||||
|         createSubflow: createSubflow, | ||||
|         convertToSubflow: convertToSubflow, | ||||
|         removeSubflow: removeSubflow, | ||||
|         refresh: refresh, | ||||
|         removeInput: removeSubflowInput, | ||||
|         removeOutput: removeSubflowOutput | ||||
|   | ||||
| @@ -236,10 +236,7 @@ RED.sidebar.config = (function() { | ||||
|             visible: false, | ||||
|             onchange: function() { refreshConfigNodeList(); } | ||||
|         }); | ||||
|  | ||||
|         RED.menu.setAction('menu-item-config-nodes',function() { | ||||
|             RED.sidebar.show('config'); | ||||
|         }) | ||||
|         RED.actions.add("core:show-config-tab",function() {RED.sidebar.show('config')}); | ||||
|  | ||||
|         $("#workspace-config-node-collapse-all").on("click", function(e) { | ||||
|             e.preventDefault(); | ||||
|   | ||||
| @@ -42,7 +42,7 @@ RED.sidebar.info = (function() { | ||||
|             content: content, | ||||
|             enableOnEdit: true | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:show-info-tab",show); | ||||
|     } | ||||
|  | ||||
|     function show() { | ||||
| @@ -67,13 +67,16 @@ RED.sidebar.info = (function() { | ||||
|     } | ||||
|  | ||||
|     function refresh(node) { | ||||
|         var table = '<table class="node-info"><tbody>'; | ||||
|         table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>'; | ||||
|         tips.stop(); | ||||
|         $(content).empty(); | ||||
|         var table = $('<table class="node-info"></table>'); | ||||
|         var tableBody = $('<tbody>').appendTo(table); | ||||
|         $('<tr class="blank"><td colspan="2">'+RED._("sidebar.info.node")+'</td></tr>').appendTo(tableBody); | ||||
|         if (node.type != "subflow" && node.name) { | ||||
|             table += '<tr><td>'+RED._("common.label.name")+'</td><td> <span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>'; | ||||
|             $('<tr><td>'+RED._("common.label.name")+'</td><td> <span class="bidiAware" dir="'+RED.text.bidi.resolveBaseTextDir(node.name)+'">'+node.name+'</span></td></tr>').appendTo(tableBody); | ||||
|         } | ||||
|         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>"; | ||||
|         $("<tr><td>"+RED._("sidebar.info.type")+"</td><td> "+node.type+"</td></tr>").appendTo(tableBody); | ||||
|         $("<tr><td>"+RED._("sidebar.info.id")+"</td><td> "+node.id+"</td></tr>").appendTo(tableBody); | ||||
|  | ||||
|         var m = /^subflow(:(.+))?$/.exec(node.type); | ||||
|         var subflowNode; | ||||
| @@ -84,7 +87,7 @@ RED.sidebar.info = (function() { | ||||
|                 subflowNode = node; | ||||
|             } | ||||
|  | ||||
|             table += '<tr class="blank"><td colspan="2">'+RED._("sidebar.info.subflow")+'</td></tr>'; | ||||
|             $('<tr class="blank"><td colspan="2">'+RED._("sidebar.info.subflow")+'</td></tr>').appendTo(tableBody); | ||||
|  | ||||
|             var userCount = 0; | ||||
|             var subflowType = "subflow:"+subflowNode.id; | ||||
| @@ -93,66 +96,38 @@ RED.sidebar.info = (function() { | ||||
|                     userCount++; | ||||
|                 } | ||||
|             }); | ||||
|             table += '<tr><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>'; | ||||
|             table += "<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>"; | ||||
|             $('<tr><td>'+RED._("common.label.name")+'</td><td><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.name)+'">'+subflowNode.name+'</span></td></tr>').appendTo(tableBody); | ||||
|             $("<tr><td>"+RED._("sidebar.info.instances")+"</td><td>"+userCount+"</td></tr>").appendTo(tableBody); | ||||
|         } | ||||
|  | ||||
|         if (!m && node.type != "subflow" && node.type != "comment") { | ||||
|             table += '<tr class="blank"><td colspan="2"><a href="#" class="node-info-property-header"><i style="width: 10px; text-align: center;" class="fa fa-caret-'+(propertiesExpanded?"down":"right")+'"></i> '+RED._("sidebar.info.properties")+'</a></td></tr>'; | ||||
|             $('<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>').appendTo(tableBody); | ||||
|             if (node._def) { | ||||
|                 for (var n in node._def.defaults) { | ||||
|                     if (n != "name" && node._def.defaults.hasOwnProperty(n)) { | ||||
|                         var val = node[n]; | ||||
|                         var type = typeof val; | ||||
|                         if (val === null || val === undefined) { | ||||
|                             val = '<span style="font-style: italic; color: #ccc;">'+RED._("sidebar.info.null")+'</span>'; | ||||
|                         } else if (type === "string") { | ||||
|                             if (val.length === 0) { | ||||
|                                 val = '<span style="font-style: italic; color: #ccc;">'+RED._("sidebar.info.blank")+'</span>'; | ||||
|                             } else { | ||||
|                                 if (val.length > 30) { | ||||
|                                     val = val.substring(0,30)+" ..."; | ||||
|                                 } | ||||
|                                 val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|                             } | ||||
|                         } else if (type === "number") { | ||||
|                             val = val.toString(); | ||||
|                         } else if ($.isArray(val)) { | ||||
|                             val = "[<br/>"; | ||||
|                             for (var i=0;i<Math.min(node[n].length,10);i++) { | ||||
|                                 var vv = JSON.stringify(node[n][i],jsonFilter," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|                                 val += " "+i+": "+vv+"<br/>"; | ||||
|                             } | ||||
|                             if (node[n].length > 10) { | ||||
|                                 val += " ... "+RED._("sidebar.info.arrayItems",{count:node[n].length})+"<br/>"; | ||||
|                             } | ||||
|                             val += "]"; | ||||
|                         } else { | ||||
|                             val = JSON.stringify(val,jsonFilter," "); | ||||
|                             val = val.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|                         } | ||||
|  | ||||
|                         table += '<tr class="node-info-property-row'+(propertiesExpanded?"":" hide")+'"><td>'+n+"</td><td>"+val+"</td></tr>"; | ||||
|                         var propRow = $('<tr class="node-info-property-row'+(propertiesExpanded?"":" hide")+'"><td>'+n+"</td><td></td></tr>").appendTo(tableBody); | ||||
|                         RED.utils.createObjectElement(val).appendTo(propRow.children()[1]); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         table += "</tbody></table><hr/>"; | ||||
|         $(table).appendTo(content); | ||||
|         $("<hr/>").appendTo(content); | ||||
|         if (!subflowNode && node.type != "comment") { | ||||
|             var helpText = $("script[data-help-name$='"+node.type+"']").html()||""; | ||||
|             table  += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(helpText)+'">'+helpText+'</span></div>'; | ||||
|             $('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(helpText)+'">'+helpText+'</span></div>').appendTo(content); | ||||
|         } | ||||
|         if (subflowNode) { | ||||
|             table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.info||"")+'">'+marked(subflowNode.info||"")+'</span></div>'; | ||||
|             $('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(subflowNode.info||"")+'">'+marked(subflowNode.info||"")+'</span></div>').appendTo(content); | ||||
|         } else if (node._def && node._def.info) { | ||||
|             var info = node._def.info; | ||||
|             var textInfo = (typeof info === "function" ? info.call(node) : info); | ||||
|             table += '<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(textInfo)+'">'+marked(textInfo)+'</span></div>'; | ||||
|             //table += '<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>'; | ||||
|             $('<div class="node-help"><span class="bidiAware" dir=\"'+RED.text.bidi.resolveBaseTextDir(textInfo)+'">'+marked(textInfo)+'</span></div>').appendTo(content); | ||||
|             //$('<div class="node-help">'+(typeof info === "function" ? info.call(node) : info)+'</div>'; | ||||
|         } | ||||
|  | ||||
|         $(content).html(table); | ||||
|  | ||||
|         $(".node-info-property-header").click(function(e) { | ||||
|             var icon = $(this).find("i"); | ||||
|             if (icon.hasClass("fa-caret-right")) { | ||||
| @@ -171,14 +146,98 @@ RED.sidebar.info = (function() { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     var tips = (function() { | ||||
|         var started = false; | ||||
|         var enabled = true; | ||||
|         var startDelay = 1000; | ||||
|         var cycleDelay = 10000; | ||||
|         var startTimeout; | ||||
|         var refreshTimeout; | ||||
|         var tipCount = -1; | ||||
|  | ||||
|         RED.actions.add("core:toggle-show-tips",function(state) { | ||||
|             if (state === undefined) { | ||||
|                 RED.menu.toggleSelected("menu-item-show-tips"); | ||||
|             } else { | ||||
|                 enabled = state; | ||||
|                 if (enabled) { | ||||
|                     if (started) { | ||||
|                         startTips(); | ||||
|                     } | ||||
|                 } else { | ||||
|                     stopTips(); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         function setTip() { | ||||
|             var r = Math.floor(Math.random() * tipCount); | ||||
|             var tip = RED._("infotips:info.tip"+r); | ||||
|  | ||||
|             var m; | ||||
|             while ((m=/({{(.*?)}})/.exec(tip))) { | ||||
|                 var shortcut = RED.keyboard.getShortcut(m[2]); | ||||
|                 if (shortcut) { | ||||
|                     tip = tip.replace(m[1],RED.keyboard.formatKey(shortcut.key)); | ||||
|                 } else { | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|             while ((m=/(\[(.*?)\])/.exec(tip))) { | ||||
|                 tip = tip.replace(m[1],RED.keyboard.formatKey(m[2])); | ||||
|             } | ||||
|             $('<div class="node-info-tip hide">'+tip+'</div>').appendTo(content).fadeIn(200); | ||||
|             if (startTimeout) { | ||||
|                 startTimeout = null; | ||||
|                 refreshTimeout = setInterval(cycleTips,cycleDelay); | ||||
|             } | ||||
|         } | ||||
|         function cycleTips() { | ||||
|             $(".node-info-tip").fadeOut(300,function() { | ||||
|                 $(this).remove(); | ||||
|                 setTip(); | ||||
|             }) | ||||
|         } | ||||
|         function startTips() { | ||||
|             started = true; | ||||
|             if (enabled) { | ||||
|                 if (!startTimeout && !refreshTimeout) { | ||||
|                     $(content).html(""); | ||||
|                     if (tipCount === -1) { | ||||
|                         do { | ||||
|                             tipCount++; | ||||
|                         } while(RED._("infotips:info.tip"+tipCount)!=="infotips:info.tip"+tipCount); | ||||
|                     } | ||||
|                     startTimeout = setTimeout(setTip,startDelay); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         function stopTips() { | ||||
|             started = false; | ||||
|             clearInterval(refreshTimeout); | ||||
|             clearTimeout(startTimeout); | ||||
|             refreshTimeout = null; | ||||
|             startTimeout = null; | ||||
|             $(".node-info-tip").remove(); | ||||
|         } | ||||
|         return { | ||||
|             start: startTips, | ||||
|             stop: stopTips | ||||
|         } | ||||
|     })(); | ||||
|  | ||||
|     function clear() { | ||||
|         $(content).html(""); | ||||
|         tips.start(); | ||||
|     } | ||||
|  | ||||
|     function set(html) { | ||||
|         tips.stop(); | ||||
|         $(content).html(html); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     RED.events.on("view:selection-changed",function(selection) { | ||||
|         if (selection.nodes) { | ||||
|             if (selection.nodes.length == 1) { | ||||
| @@ -202,7 +261,7 @@ RED.sidebar.info = (function() { | ||||
|     return { | ||||
|         init: init, | ||||
|         show: show, | ||||
|         refresh:refresh, | ||||
|         refresh: refresh, | ||||
|         clear: clear, | ||||
|         set: set | ||||
|     } | ||||
|   | ||||
| @@ -155,6 +155,8 @@ RED.tray = (function() { | ||||
|                     // Delay resetting the flag, so we don't close prematurely | ||||
|                     openingTray = false; | ||||
|                 },200); | ||||
|                 body.find(":focusable:first").focus(); | ||||
|  | ||||
|             },150); | ||||
|             el.css({right:0}); | ||||
|         },0); | ||||
|   | ||||
							
								
								
									
										304
									
								
								editor/js/ui/typeSearch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								editor/js/ui/typeSearch.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,304 @@ | ||||
| RED.typeSearch = (function() { | ||||
|  | ||||
|     var shade; | ||||
|  | ||||
|     var disabled = false; | ||||
|     var dialog = null; | ||||
|     var searchInput; | ||||
|     var searchResults; | ||||
|     var searchResultsDiv; | ||||
|     var selected = -1; | ||||
|     var visible = false; | ||||
|  | ||||
|     var activeFilter = ""; | ||||
|     var addCallback; | ||||
|  | ||||
|     var typesUsed = {}; | ||||
|  | ||||
|     function search(val) { | ||||
|         activeFilter = val.toLowerCase(); | ||||
|         var visible = searchResults.editableList('filter'); | ||||
|         setTimeout(function() { | ||||
|             selected = 0; | ||||
|             searchResults.children().removeClass('selected'); | ||||
|             searchResults.children(":visible:first").addClass('selected'); | ||||
|         },100); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function ensureSelectedIsVisible() { | ||||
|         var selectedEntry = searchResults.find("li.selected"); | ||||
|         if (selectedEntry.length === 1) { | ||||
|             var scrollWindow = searchResults.parent(); | ||||
|             var scrollHeight = scrollWindow.height(); | ||||
|             var scrollOffset = scrollWindow.scrollTop(); | ||||
|             var y = selectedEntry.position().top; | ||||
|             var h = selectedEntry.height(); | ||||
|             if (y+h > scrollHeight) { | ||||
|                 scrollWindow.animate({scrollTop: '-='+(scrollHeight-(y+h)-10)},50); | ||||
|             } else if (y<0) { | ||||
|                 scrollWindow.animate({scrollTop: '+='+(y-10)},50); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function createDialog() { | ||||
|         //shade = $('<div>',{class:"red-ui-type-search-shade"}).appendTo("#main-container"); | ||||
|         dialog = $("<div>",{id:"red-ui-type-search",class:"red-ui-search red-ui-type-search"}).appendTo("#main-container"); | ||||
|         var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog); | ||||
|         searchInput = $('<input type="text">').attr("placeholder",RED._("search.addNode")).appendTo(searchDiv).searchBox({ | ||||
|             delay: 50, | ||||
|             change: function() { | ||||
|                 search($(this).val()); | ||||
|             } | ||||
|         }); | ||||
|         searchInput.on('keydown',function(evt) { | ||||
|             var children = searchResults.children(":visible"); | ||||
|             if (children.length > 0) { | ||||
|                 if (evt.keyCode === 40) { | ||||
|                     // Down | ||||
|                     if (selected < children.length-1) { | ||||
|                         if (selected > -1) { | ||||
|                             $(children[selected]).removeClass('selected'); | ||||
|                         } | ||||
|                         selected++; | ||||
|                     } | ||||
|                     $(children[selected]).addClass('selected'); | ||||
|                     ensureSelectedIsVisible(); | ||||
|                     evt.preventDefault(); | ||||
|                 } else if (evt.keyCode === 38) { | ||||
|                     // Up | ||||
|                     if (selected > 0) { | ||||
|                         if (selected < children.length) { | ||||
|                             $(children[selected]).removeClass('selected'); | ||||
|                         } | ||||
|                         selected--; | ||||
|                     } | ||||
|                     $(children[selected]).addClass('selected'); | ||||
|                     ensureSelectedIsVisible(); | ||||
|                     evt.preventDefault(); | ||||
|                 } else if (evt.keyCode === 13) { | ||||
|                     // Enter | ||||
|                     var index = Math.max(0,selected); | ||||
|                     if (index < children.length) { | ||||
|                         // TODO: dips into editableList impl details | ||||
|                         confirm($(children[index]).find(".red-ui-editableList-item-content").data('data')); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         searchResultsDiv = $("<div>",{class:"red-ui-search-results-container"}).appendTo(dialog); | ||||
|         searchResults = $('<ol>',{id:"search-result-list", style:"position: absolute;top: 0;bottom: 0;left: 0;right: 0;"}).appendTo(searchResultsDiv).editableList({ | ||||
|             addButton: false, | ||||
|             filter: function(data) { | ||||
|                 if (activeFilter === "" ) { | ||||
|                     return true; | ||||
|                 } | ||||
|                 if (data.recent || data.common) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 return (activeFilter==="")||(data.index.indexOf(activeFilter) > -1); | ||||
|             }, | ||||
|             addItem: function(container,i,object) { | ||||
|                 var def = object.def; | ||||
|                 object.index = object.type.toLowerCase(); | ||||
|                 if (object.separator) { | ||||
|                     container.addClass("red-ui-search-result-separator") | ||||
|                 } | ||||
|                 var div = $('<a>',{href:'#',class:"red-ui-search-result"}).appendTo(container); | ||||
|  | ||||
|                 var nodeDiv = $('<div>',{class:"red-ui-search-result-node"}).appendTo(div); | ||||
|                 var colour = def.color; | ||||
|                 var icon_url = "arrow-in.png"; | ||||
|                 if (def.category === 'config') { | ||||
|                     icon_url = "cog.png"; | ||||
|                 } else { | ||||
|                     try { | ||||
|                         icon_url = (typeof def.icon === "function" ? def.icon.call({}) : def.icon); | ||||
|                     } catch(err) { | ||||
|                         console.log("Definition error: "+object.type+".icon",err); | ||||
|                     } | ||||
|                 } | ||||
|                 nodeDiv.css('backgroundColor',colour); | ||||
|  | ||||
|                 var iconContainer = $('<div/>',{class:"palette_icon_container"}).appendTo(nodeDiv); | ||||
|                 $('<div/>',{class:"palette_icon",style:"background-image: url(icons/"+icon_url+")"}).appendTo(iconContainer); | ||||
|  | ||||
|                 if (def.inputs > 0) { | ||||
|                     $('<div/>',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv); | ||||
|                 } | ||||
|                 if (def.outputs > 0) { | ||||
|                     $('<div/>',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv); | ||||
|                 } | ||||
|  | ||||
|                 var contentDiv = $('<div>',{class:"red-ui-search-result-description"}).appendTo(div); | ||||
|  | ||||
|                 var label = object.label; | ||||
|                 object.index += "|"+label.toLowerCase(); | ||||
|  | ||||
|                 $('<div>',{class:"red-ui-search-result-node-label"}).html(label).appendTo(contentDiv); | ||||
|  | ||||
|                 div.click(function(evt) { | ||||
|                     evt.preventDefault(); | ||||
|                     confirm(object); | ||||
|                 }); | ||||
|             }, | ||||
|             scrollOnAdd: false | ||||
|         }); | ||||
|  | ||||
|     } | ||||
|     function confirm(def) { | ||||
|         hide(); | ||||
|         typesUsed[def.type] = Date.now(); | ||||
|         addCallback(def.type); | ||||
|     } | ||||
|  | ||||
|     function handleMouseActivity(evt) { | ||||
|         if (visible) { | ||||
|             var t = $(evt.target); | ||||
|             while (t.prop('nodeName').toLowerCase() !== 'body') { | ||||
|                 if (t.attr('id') === 'red-ui-type-search') { | ||||
|                     return; | ||||
|                 } | ||||
|                 t = t.parent(); | ||||
|             } | ||||
|             hide(true); | ||||
|         } | ||||
|     } | ||||
|     function show(opts) { | ||||
|         if (!visible) { | ||||
|             RED.keyboard.add("*","escape",function(){hide()}); | ||||
|             if (dialog === null) { | ||||
|                 createDialog(); | ||||
|             } | ||||
|             visible = true; | ||||
|             setTimeout(function() { | ||||
|                 $(document).on('mousedown.type-search',handleMouseActivity); | ||||
|                 $(document).on('mouseup.type-search',handleMouseActivity); | ||||
|                 $(document).on('click.type-search',handleMouseActivity); | ||||
|             },200); | ||||
|         } else { | ||||
|             dialog.hide(); | ||||
|             searchResultsDiv.hide(); | ||||
|         } | ||||
|         refreshTypeList(); | ||||
|         addCallback = opts.add; | ||||
|         RED.events.emit("type-search:open"); | ||||
|         //shade.show(); | ||||
|         dialog.css({left:opts.x+"px",top:opts.y+"px"}).show(); | ||||
|         searchResultsDiv.slideDown(); | ||||
|         setTimeout(function() { | ||||
|             searchResultsDiv.find(".red-ui-editableList-container").scrollTop(0); | ||||
|             searchInput.focus(); | ||||
|         },100); | ||||
|     } | ||||
|     function hide(fast) { | ||||
|         if (visible) { | ||||
|             RED.keyboard.remove("escape"); | ||||
|             visible = false; | ||||
|             if (dialog !== null) { | ||||
|                 searchResultsDiv.slideUp(fast?50:200,function() { | ||||
|                     dialog.hide(); | ||||
|                     searchInput.searchBox('value',''); | ||||
|                 }); | ||||
|                 //shade.hide(); | ||||
|             } | ||||
|             RED.events.emit("type-search:close"); | ||||
|             RED.view.focus(); | ||||
|             $(document).off('mousedown.type-search'); | ||||
|             $(document).off('mouseup.type-search'); | ||||
|             $(document).off('click.type-search'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function getTypeLabel(type, def) { | ||||
|         var label = type; | ||||
|         if (typeof def.paletteLabel !== "undefined") { | ||||
|             try { | ||||
|                 label = (typeof def.paletteLabel === "function" ? def.paletteLabel.call(def) : def.paletteLabel)||""; | ||||
|                 label += " ("+type+")"; | ||||
|             } catch(err) { | ||||
|                 console.log("Definition error: "+type+".paletteLabel",err); | ||||
|             } | ||||
|         } | ||||
|         return label; | ||||
|     } | ||||
|  | ||||
|     function refreshTypeList() { | ||||
|         var i; | ||||
|         searchResults.editableList('empty'); | ||||
|         searchInput.searchBox('value',''); | ||||
|         selected = -1; | ||||
|         var common = [ | ||||
|             'debug','inject','function','change','switch' | ||||
|         ]; | ||||
|  | ||||
|         var recentlyUsed = Object.keys(typesUsed); | ||||
|         recentlyUsed.sort(function(a,b) { | ||||
|             return typesUsed[b]-typesUsed[a]; | ||||
|         }); | ||||
|         recentlyUsed = recentlyUsed.filter(function(t) { | ||||
|             return common.indexOf(t) === -1; | ||||
|         }); | ||||
|  | ||||
|         var items = []; | ||||
|         RED.nodes.registry.getNodeTypes().forEach(function(t) { | ||||
|             var def = RED.nodes.getType(t); | ||||
|             if (def.category !== 'config' && t !== 'unknown') { | ||||
|                 items.push({type:t,def: def, label:getTypeLabel(t,def)}); | ||||
|             } | ||||
|         }); | ||||
|         items.sort(function(a,b) { | ||||
|             var al = a.label.toLowerCase(); | ||||
|             var bl = b.label.toLowerCase(); | ||||
|             if (al < bl) { | ||||
|                 return -1; | ||||
|             } else if (al === bl) { | ||||
|                 return 0; | ||||
|             } else { | ||||
|                 return 1; | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         var commonCount = 0; | ||||
|         var item; | ||||
|         for(i=0;i<common.length;i++) { | ||||
|             item = { | ||||
|                 type: common[i], | ||||
|                 common: true, | ||||
|                 def: RED.nodes.getType(common[i]) | ||||
|             }; | ||||
|             item.label = getTypeLabel(item.type,item.def); | ||||
|             if (i === common.length-1) { | ||||
|                 item.separator = true; | ||||
|             } | ||||
|             searchResults.editableList('addItem', item); | ||||
|         } | ||||
|         for(i=0;i<Math.min(5,recentlyUsed.length);i++) { | ||||
|             item = { | ||||
|                 type:recentlyUsed[i], | ||||
|                 def: RED.nodes.getType(recentlyUsed[i]), | ||||
|                 recent: true | ||||
|             }; | ||||
|             item.label = getTypeLabel(item.type,item.def); | ||||
|             if (i === recentlyUsed.length-1) { | ||||
|                 item.separator = true; | ||||
|             } | ||||
|             searchResults.editableList('addItem', item); | ||||
|         } | ||||
|         for (i=0;i<items.length;i++) { | ||||
|             searchResults.editableList('addItem', items[i]); | ||||
|         } | ||||
|         setTimeout(function() { | ||||
|             selected = 0; | ||||
|             searchResults.children(":first").addClass('selected'); | ||||
|         },100); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         show: show, | ||||
|         hide: hide | ||||
|     }; | ||||
|  | ||||
| })(); | ||||
							
								
								
									
										366
									
								
								editor/js/ui/utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								editor/js/ui/utils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | ||||
| /** | ||||
|  * Copyright 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| RED.utils = (function() { | ||||
|  | ||||
|     function formatString(str) { | ||||
|         return str.replace(/\r?\n/g,"↵").replace(/\t/g,"→"); | ||||
|     } | ||||
|     function sanitize(m) { | ||||
|         return m.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|     } | ||||
|  | ||||
|     function buildMessageSummaryValue(value) { | ||||
|         var result; | ||||
|         if (Array.isArray(value)) { | ||||
|             result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('array['+value.length+']'); | ||||
|         } else if (value === null) { | ||||
|             result = $('<span class="debug-message-object-value debug-message-type-null">null</span>'); | ||||
|         } else if (typeof value === 'object') { | ||||
|             if (value.hasOwnProperty('type') && value.type === 'Buffer' && value.hasOwnProperty('data')) { | ||||
|                 result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('buffer['+value.data.length+']'); | ||||
|             } else if (value.hasOwnProperty('type') && value.type === 'array' && value.hasOwnProperty('data')) { | ||||
|                 result = $('<span class="debug-message-object-value debug-message-type-meta"></span>').html('array['+value.length+']'); | ||||
|             } else { | ||||
|                 result = $('<span class="debug-message-object-value debug-message-type-meta">object</span>'); | ||||
|             } | ||||
|         } else if (typeof value === 'string') { | ||||
|             var subvalue; | ||||
|             if (value.length > 30) { | ||||
|                 subvalue = sanitize(value.substring(0,30))+"…"; | ||||
|             } else { | ||||
|                 subvalue = sanitize(value); | ||||
|             } | ||||
|             result = $('<span class="debug-message-object-value debug-message-type-string"></span>').html('"'+formatString(subvalue)+'"'); | ||||
|         } else { | ||||
|             result = $('<span class="debug-message-object-value debug-message-type-other"></span>').text(""+value); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     function makeExpandable(el,onexpand) { | ||||
|         el.addClass("debug-message-expandable"); | ||||
|         el.click(function(e) { | ||||
|             var parent = $(this).parent(); | ||||
|             if (parent.hasClass('collapsed')) { | ||||
|                 if (onexpand && !parent.hasClass('built')) { | ||||
|                     onexpand(); | ||||
|                     parent.addClass('built'); | ||||
|                 } | ||||
|                 parent.removeClass('collapsed'); | ||||
|             } else { | ||||
|                 parent.addClass('collapsed'); | ||||
|             } | ||||
|             e.preventDefault(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function buildMessageElement(obj,key,typeHint,hideKey) { | ||||
|         var i; | ||||
|         var e; | ||||
|         var entryObj; | ||||
|         var header; | ||||
|         var headerHead; | ||||
|         var value; | ||||
|         var element = $('<span class="debug-message-element"></span>'); | ||||
|         if (!key) { | ||||
|             element.addClass("debug-message-top-level"); | ||||
|         } | ||||
|  | ||||
|         header = $('<span></span>').appendTo(element); | ||||
|  | ||||
|         if (key && !hideKey) { | ||||
|             $('<span class="debug-message-object-key"></span>').text(key).appendTo(header); | ||||
|             $('<span>: </span>').appendTo(header); | ||||
|         } | ||||
|         entryObj = $('<span class="debug-message-object-value"></span>').appendTo(header); | ||||
|  | ||||
|         var isArray = Array.isArray(obj); | ||||
|         var isArrayObject = false; | ||||
|         if (obj && typeof obj === 'object' && obj.hasOwnProperty('type') && obj.hasOwnProperty('data') && ((obj.__encoded__ && obj.type === 'array') || obj.type === 'Buffer')) { | ||||
|             isArray = true; | ||||
|             isArrayObject = true; | ||||
|         } | ||||
|  | ||||
|         if (obj === null || obj === undefined) { | ||||
|             $('<span class="debug-message-type-null">'+obj+'</span>').appendTo(entryObj); | ||||
|         } else if (typeof obj === 'string') { | ||||
|             if (/[\t\n\r]/.test(obj)) { | ||||
|                 element.addClass('collapsed'); | ||||
|                 $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').prependTo(header); | ||||
|                 makeExpandable(header, function() { | ||||
|                     $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html(typeHint||'string').appendTo(header); | ||||
|                     var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element); | ||||
|                     $('<pre class="debug-message-type-string"></pre>').text(obj).appendTo(row); | ||||
|                 }); | ||||
|             } | ||||
|             $('<span class="debug-message-type-string debug-message-object-header"></span>').html('"'+formatString(sanitize(obj))+'"').appendTo(entryObj); | ||||
|  | ||||
|  | ||||
|         } else if (typeof obj === 'number') { | ||||
|             e = $('<span class="debug-message-type-number"></span>').text(""+obj).appendTo(entryObj); | ||||
|             if (Number.isInteger(obj) && (obj >= 0)) { // if it's a +ve integer | ||||
|                 e.addClass("debug-message-type-number-toggle"); | ||||
|                 e.click(function(evt) { | ||||
|                     var format = $(this).data('format') || "date"; | ||||
|                     if (format === 'dec') { | ||||
|                         $(this).text(""+obj).data('format','date'); | ||||
|                     } else if ((format === 'date') && (obj.toString().length===13) && (obj<=2147483647000)) { | ||||
|                         $(this).text((new Date(obj)).toISOString()).data('format','hex'); | ||||
|                     } else if ((format === 'date') && (obj.toString().length===10) && (obj<=2147483647)) { | ||||
|                         $(this).text((new Date(obj*1000)).toISOString()).data('format','hex'); | ||||
|                     } else { | ||||
|                         $(this).text("0x"+(obj).toString(16)).data('format','dec'); | ||||
|                     } | ||||
|                     evt.preventDefault(); | ||||
|                 }); | ||||
|             } | ||||
|         } else if (isArray) { | ||||
|             element.addClass('collapsed'); | ||||
|  | ||||
|             var originalLength = obj.length; | ||||
|             if (typeHint) { | ||||
|                 var m = /\[(\d+)\]/.exec(typeHint); | ||||
|                 if (m) { | ||||
|                     originalLength = parseInt(m[1]); | ||||
|                 } | ||||
|             } | ||||
|             var data = obj; | ||||
|             var type = 'array'; | ||||
|             if (isArrayObject) { | ||||
|                 data = obj.data; | ||||
|                 if (originalLength === undefined) { | ||||
|                     originalLength = data.length; | ||||
|                 } | ||||
|                 type = obj.type.toLowerCase(); | ||||
|             } else if (/buffer/.test(typeHint)) { | ||||
|                 type = 'buffer'; | ||||
|             } | ||||
|             var fullLength = data.length; | ||||
|  | ||||
|             if (originalLength > 0) { | ||||
|                 $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').prependTo(header); | ||||
|                 var arrayRows = $('<div class="debug-message-array-rows"></div>').appendTo(element); | ||||
|                 element.addClass('debug-message-buffer-raw'); | ||||
|                 makeExpandable(header,function() { | ||||
|                     if (!key) { | ||||
|                         headerHead = $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html(typeHint||(type+'['+originalLength+']')).appendTo(header); | ||||
|                     } | ||||
|                     if (type === 'buffer') { | ||||
|                         var stringRow = $('<div class="debug-message-string-rows"></div>').appendTo(element); | ||||
|                         var sr = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(stringRow); | ||||
|                         var stringEncoding = ""; | ||||
|                         try { | ||||
|                             stringEncoding = String.fromCharCode.apply(null, new Uint16Array(data)) | ||||
|                         } catch(err) { | ||||
|                             console.log(err); | ||||
|                         } | ||||
|                         $('<pre class="debug-message-type-string"></pre>').text(stringEncoding).appendTo(sr); | ||||
|                         var bufferOpts = $('<span class="debug-message-buffer-opts"></span>').appendTo(headerHead); | ||||
|                         $('<a href="#"></a>').addClass('selected').html('raw').appendTo(bufferOpts).click(function(e) { | ||||
|                             if ($(this).text() === 'raw') { | ||||
|                                 $(this).text('string'); | ||||
|                                 element.addClass('debug-message-buffer-string').removeClass('debug-message-buffer-raw'); | ||||
|                             } else { | ||||
|                                 $(this).text('raw'); | ||||
|                                 element.removeClass('debug-message-buffer-string').addClass('debug-message-buffer-raw'); | ||||
|                             } | ||||
|                             e.preventDefault(); | ||||
|                             e.stopPropagation(); | ||||
|                         }) | ||||
|                     } | ||||
|                     var row; | ||||
|                     if (fullLength <= 10) { | ||||
|                         for (i=0;i<fullLength;i++) { | ||||
|                             row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(arrayRows); | ||||
|                             buildMessageElement(data[i],""+i,false).appendTo(row); | ||||
|                         } | ||||
|                     } else { | ||||
|                         for (i=0;i<fullLength;i+=10) { | ||||
|                             var minRange = i; | ||||
|                             row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(arrayRows); | ||||
|                             header = $('<span></span>').appendTo(row); | ||||
|                             $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').appendTo(header); | ||||
|                             makeExpandable(header, (function() { | ||||
|                                 var min = minRange; | ||||
|                                 var max = Math.min(fullLength-1,(minRange+9)); | ||||
|                                 var parent = row; | ||||
|                                 return function() { | ||||
|                                     for (var i=min;i<=max;i++) { | ||||
|                                         var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(parent); | ||||
|                                         buildMessageElement(data[i],""+i,false).appendTo(row); | ||||
|                                     } | ||||
|                                 } | ||||
|                             })()); | ||||
|                             $('<span class="debug-message-object-key"></span>').html("["+minRange+" … "+Math.min(fullLength-1,(minRange+9))+"]").appendTo(header); | ||||
|                         } | ||||
|                         if (fullLength < originalLength) { | ||||
|                              $('<div class="debug-message-object-entry collapsed"><span class="debug-message-object-key">['+fullLength+' … '+originalLength+']</span></div>').appendTo(arrayRows); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|             if (key) { | ||||
|                 headerHead = $('<span class="debug-message-type-meta f"></span>').html(typeHint||(type+'['+originalLength+']')).appendTo(entryObj); | ||||
|             } else { | ||||
|                 headerHead = $('<span class="debug-message-object-header"></span>').appendTo(entryObj); | ||||
|                 $('<span>[ </span>').appendTo(headerHead); | ||||
|                 var arrayLength = Math.min(originalLength,10); | ||||
|                 for (i=0;i<arrayLength;i++) { | ||||
|                     buildMessageSummaryValue(data[i]).appendTo(headerHead); | ||||
|                     if (i < arrayLength-1) { | ||||
|                         $('<span>, </span>').appendTo(headerHead); | ||||
|                     } | ||||
|                 } | ||||
|                 if (originalLength > arrayLength) { | ||||
|                     $('<span> …</span>').appendTo(headerHead); | ||||
|                 } | ||||
|                 if (arrayLength === 0) { | ||||
|                     $('<span class="debug-message-type-meta">empty</span>').appendTo(headerHead); | ||||
|                 } | ||||
|                 $('<span> ]</span>').appendTo(headerHead); | ||||
|             } | ||||
|  | ||||
|         } else if (typeof obj === 'object') { | ||||
|             element.addClass('collapsed'); | ||||
|             var keys = Object.keys(obj); | ||||
|             if (key || keys.length > 0) { | ||||
|                 $('<i class="fa fa-caret-right debug-message-object-handle"></i> ').prependTo(header); | ||||
|                 makeExpandable(header, function() { | ||||
|                     if (!key) { | ||||
|                         $('<span class="debug-message-type-meta debug-message-object-type-header"></span>').html('object').appendTo(header); | ||||
|                     } | ||||
|                     for (i=0;i<keys.length;i++) { | ||||
|                         var row = $('<div class="debug-message-object-entry collapsed"></div>').appendTo(element); | ||||
|                         buildMessageElement(obj[keys[i]],keys[i],false).appendTo(row); | ||||
|                     } | ||||
|                     if (keys.length === 0) { | ||||
|                         $('<div class="debug-message-object-entry debug-message-type-meta collapsed"></div>').text("empty").appendTo(element); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|             if (key) { | ||||
|                 $('<span class="debug-message-type-meta"></span>').html('object').appendTo(entryObj); | ||||
|             } else { | ||||
|                 headerHead = $('<span class="debug-message-object-header"></span>').appendTo(entryObj); | ||||
|                 $('<span>{ </span>').appendTo(headerHead); | ||||
|                 var keysLength = Math.min(keys.length,5); | ||||
|                 for (i=0;i<keysLength;i++) { | ||||
|                     $('<span class="debug-message-object-key"></span>').text(keys[i]).appendTo(headerHead); | ||||
|                     $('<span>: </span>').appendTo(headerHead); | ||||
|                     buildMessageSummaryValue(obj[keys[i]]).appendTo(headerHead); | ||||
|                     if (i < keysLength-1) { | ||||
|                         $('<span>, </span>').appendTo(headerHead); | ||||
|                     } | ||||
|                 } | ||||
|                 if (keys.length > keysLength) { | ||||
|                     $('<span> …</span>').appendTo(headerHead); | ||||
|                 } | ||||
|                 if (keysLength === 0) { | ||||
|                     $('<span class="debug-message-type-meta">empty</span>').appendTo(headerHead); | ||||
|                 } | ||||
|                 $('<span> }</span>').appendTo(headerHead); | ||||
|             } | ||||
|         } else { | ||||
|             $('<span class="debug-message-type-other"></span>').text(""+obj).appendTo(entryObj); | ||||
|         } | ||||
|         return element; | ||||
|     } | ||||
|  | ||||
|     function validatePropertyExpression(str) { | ||||
|         // This must be kept in sync with normalisePropertyExpression | ||||
|         // in red/runtime/util.js | ||||
|  | ||||
|         var length = str.length; | ||||
|         var start = 0; | ||||
|         var inString = false; | ||||
|         var inBox = false; | ||||
|         var quoteChar; | ||||
|         var v; | ||||
|         for (var i=0;i<length;i++) { | ||||
|             var c = str[i]; | ||||
|             if (!inString) { | ||||
|                 if (c === "'" || c === '"') { | ||||
|                     if (i != start) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     inString = true; | ||||
|                     quoteChar = c; | ||||
|                     start = i+1; | ||||
|                 } else if (c === '.') { | ||||
|                     if (i===0 || i===length-1) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     // Next char is a-z | ||||
|                     if (!/[a-z0-9]/i.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                 } else if (c === '[') { | ||||
|                     if (i === 0) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     if (i===length-1) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     // Next char is either a quote or a number | ||||
|                     if (!/["'\d]/.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inBox = true; | ||||
|                 } else if (c === ']') { | ||||
|                     if (!inBox) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     if (start != i) { | ||||
|                         v = str.substring(start,i); | ||||
|                         if (!/^\d+$/.test(v)) { | ||||
|                             return false; | ||||
|                         } | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inBox = false; | ||||
|                 } else if (c === ' ') { | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (c === quoteChar) { | ||||
|                     if (i-start === 0) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     // Next char must be a ] | ||||
|                     if (inBox && !/\]/.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) { | ||||
|                         return false; | ||||
|                     } | ||||
|                     start = i+1; | ||||
|                     inString = false; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         if (inBox || inString) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         createObjectElement: buildMessageElement, | ||||
|         validatePropertyExpression: validatePropertyExpression | ||||
|     } | ||||
| })(); | ||||
| @@ -244,10 +244,14 @@ RED.view = (function() { | ||||
|             node.el = dragGroup.append("svg:path").attr("class", "drag_line"); | ||||
|             drag_lines.push(node); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     function hideDragLines() { | ||||
|         while(drag_lines.length) { | ||||
|             (drag_lines.pop()).el.remove(); | ||||
|             var line = drag_lines.pop(); | ||||
|             if (line.el) { | ||||
|                 line.el.remove(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -263,6 +267,7 @@ RED.view = (function() { | ||||
|     } | ||||
|  | ||||
|     function init() { | ||||
|  | ||||
|         RED.events.on("workspace:change",function(event) { | ||||
|             var chart = $("#chart"); | ||||
|             if (event.old !== 0) { | ||||
| @@ -320,70 +325,12 @@ RED.view = (function() { | ||||
|             drop: function( event, ui ) { | ||||
|                 d3.event = event; | ||||
|                 var selected_tool = ui.draggable[0].type; | ||||
|                 var m = /^subflow:(.+)$/.exec(selected_tool); | ||||
|  | ||||
|                 if (activeSubflow && m) { | ||||
|                     var subflowId = m[1]; | ||||
|                     if (subflowId === activeSubflow.id) { | ||||
|                         RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddSubflowToItself")}),"error"); | ||||
|                         return; | ||||
|                     } | ||||
|                     if (RED.nodes.subflowContains(m[1],activeSubflow.id)) { | ||||
|                         RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddCircularReference")}),"error"); | ||||
|                         return; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 var nn = { id:RED.nodes.id(),z:RED.workspaces.active()}; | ||||
|  | ||||
|                 nn.type = selected_tool; | ||||
|                 nn._def = RED.nodes.getType(nn.type); | ||||
|  | ||||
|                 if (!m) { | ||||
|                     nn.inputs = nn._def.inputs || 0; | ||||
|                     nn.outputs = nn._def.outputs; | ||||
|  | ||||
|                     for (var d in nn._def.defaults) { | ||||
|                         if (nn._def.defaults.hasOwnProperty(d)) { | ||||
|                             if (nn._def.defaults[d].value !== undefined) { | ||||
|                                 nn[d] = JSON.parse(JSON.stringify(nn._def.defaults[d].value)); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (nn._def.onadd) { | ||||
|                         try { | ||||
|                             nn._def.onadd.call(nn); | ||||
|                         } catch(err) { | ||||
|                             console.log("onadd:",err); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     var subflow = RED.nodes.subflow(m[1]); | ||||
|                     nn.inputs = subflow.in.length; | ||||
|                     nn.outputs = subflow.out.length; | ||||
|                 } | ||||
|  | ||||
|                 nn.changed = true; | ||||
|  | ||||
|                 nn.w = node_width; | ||||
|                 nn.h = Math.max(node_height,(nn.outputs||0) * 15); | ||||
|  | ||||
|                 var historyEvent = { | ||||
|                     t:"add", | ||||
|                     nodes:[nn.id], | ||||
|                     dirty:RED.nodes.dirty() | ||||
|                 } | ||||
|                 if (activeSubflow) { | ||||
|                     var subflowRefresh = RED.subflow.refresh(true); | ||||
|                     if (subflowRefresh) { | ||||
|                         historyEvent.subflow = { | ||||
|                             id:activeSubflow.id, | ||||
|                             changed: activeSubflow.changed, | ||||
|                             instances: subflowRefresh.instances | ||||
|                         } | ||||
|                     } | ||||
|                 var result = addNode(selected_tool); | ||||
|                 if (!result) { | ||||
|                     return; | ||||
|                 } | ||||
|                 var historyEvent = result.historyEvent; | ||||
|                 var nn = result.node; | ||||
|  | ||||
|                 var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0)); | ||||
|                 var mousePos = d3.touches(this)[0]||d3.mouse(this); | ||||
| @@ -438,29 +385,125 @@ RED.view = (function() { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         RED.keyboard.add("workspace",/* backspace */ 8,function(){deleteSelection();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("workspace",/* delete */ 46,function(){deleteSelection();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("workspace",/* c */ 67,{ctrl:true},function(){copySelection();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("workspace",/* x */ 88,{ctrl:true},function(){copySelection();deleteSelection();d3.event.preventDefault();}); | ||||
|         RED.actions.add("core:copy",copySelection); | ||||
|         RED.actions.add("core:cut",function(){copySelection();deleteSelection();}); | ||||
|         RED.actions.add("core:paste",function(){importNodes(clipboard);}); | ||||
|         RED.actions.add("core:delete",deleteSelection); | ||||
|         RED.actions.add("core:edit",editSelection); | ||||
|         RED.actions.add("core:undo",RED.history.pop); | ||||
|         RED.actions.add("core:select-all",selectAll); | ||||
|         RED.actions.add("core:zoom-in",zoomIn); | ||||
|         RED.actions.add("core:zoom-out",zoomOut); | ||||
|         RED.actions.add("core:zoom-reset",zoomZero); | ||||
|  | ||||
|         RED.keyboard.add("workspace",/* z */ 90,{ctrl:true},function(){RED.history.pop();}); | ||||
|         RED.keyboard.add("workspace",/* a */ 65,{ctrl:true},function(){selectAll();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("*",/* = */ 187,{ctrl:true},function(){zoomIn();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("*",/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("*",/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();}); | ||||
|         RED.keyboard.add("workspace",/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();}); | ||||
|         RED.actions.add("core:toggle-show-grid",function(state) { | ||||
|             if (state === undefined) { | ||||
|                 RED.menu.toggleSelected("menu-item-view-show-grid"); | ||||
|             } else { | ||||
|                 toggleShowGrid(state); | ||||
|             } | ||||
|         }); | ||||
|         RED.actions.add("core:toggle-snap-grid",function(state) { | ||||
|             if (state === undefined) { | ||||
|                 RED.menu.toggleSelected("menu-item-view-snap-grid"); | ||||
|             } else { | ||||
|                 toggleSnapGrid(state); | ||||
|             } | ||||
|         }); | ||||
|         RED.actions.add("core:toggle-status",function(state) { | ||||
|             if (state === undefined) { | ||||
|                 RED.menu.toggleSelected("menu-item-status"); | ||||
|             } else { | ||||
|                 toggleStatus(state); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:move-selection-up", function() { moveSelection(0,-1);}); | ||||
|         RED.actions.add("core:step-selection-up", function() { moveSelection(0,-20);}); | ||||
|         RED.actions.add("core:move-selection-right", function() { moveSelection(1,0);}); | ||||
|         RED.actions.add("core:step-selection-right", function() { moveSelection(20,0);}); | ||||
|         RED.actions.add("core:move-selection-down", function() { moveSelection(0,1);}); | ||||
|         RED.actions.add("core:step-selection-down", function() { moveSelection(0,20);}); | ||||
|         RED.actions.add("core:move-selection-left", function() { moveSelection(-1,0);}); | ||||
|         RED.actions.add("core:step-selection-left", function() { moveSelection(-20,0);}); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     function addNode(type,x,y) { | ||||
|         var m = /^subflow:(.+)$/.exec(type); | ||||
|  | ||||
|         if (activeSubflow && m) { | ||||
|             var subflowId = m[1]; | ||||
|             if (subflowId === activeSubflow.id) { | ||||
|                 RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddSubflowToItself")}),"error"); | ||||
|                 return; | ||||
|             } | ||||
|             if (RED.nodes.subflowContains(m[1],activeSubflow.id)) { | ||||
|                 RED.notify(RED._("notification.error",{message: RED._("notification.errors.cannotAddCircularReference")}),"error"); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var nn = { id:RED.nodes.id(),z:RED.workspaces.active()}; | ||||
|  | ||||
|         nn.type = type; | ||||
|         nn._def = RED.nodes.getType(nn.type); | ||||
|  | ||||
|         if (!m) { | ||||
|             nn.inputs = nn._def.inputs || 0; | ||||
|             nn.outputs = nn._def.outputs; | ||||
|  | ||||
|             for (var d in nn._def.defaults) { | ||||
|                 if (nn._def.defaults.hasOwnProperty(d)) { | ||||
|                     if (nn._def.defaults[d].value !== undefined) { | ||||
|                         nn[d] = JSON.parse(JSON.stringify(nn._def.defaults[d].value)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (nn._def.onadd) { | ||||
|                 try { | ||||
|                     nn._def.onadd.call(nn); | ||||
|                 } catch(err) { | ||||
|                     console.log("onadd:",err); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             var subflow = RED.nodes.subflow(m[1]); | ||||
|             nn.inputs = subflow.in.length; | ||||
|             nn.outputs = subflow.out.length; | ||||
|         } | ||||
|  | ||||
|         nn.changed = true; | ||||
|  | ||||
|         nn.w = node_width; | ||||
|         nn.h = Math.max(node_height,(nn.outputs||0) * 15); | ||||
|  | ||||
|         var historyEvent = { | ||||
|             t:"add", | ||||
|             nodes:[nn.id], | ||||
|             dirty:RED.nodes.dirty() | ||||
|         } | ||||
|         if (activeSubflow) { | ||||
|             var subflowRefresh = RED.subflow.refresh(true); | ||||
|             if (subflowRefresh) { | ||||
|                 historyEvent.subflow = { | ||||
|                     id:activeSubflow.id, | ||||
|                     changed: activeSubflow.changed, | ||||
|                     instances: subflowRefresh.instances | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return { | ||||
|             node: nn, | ||||
|             historyEvent: historyEvent | ||||
|         } | ||||
|  | ||||
|         RED.keyboard.add("workspace",/* up    */ 38, function() { moveSelection(0,-1);d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* up    */ 38, {shift:true}, function() { moveSelection(0,-20); d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* down  */ 40, function() { moveSelection(0,1);d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* down  */ 40, {shift:true}, function() { moveSelection(0,20); d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* left  */ 37, function() { moveSelection(-1,0);d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* left  */ 37, {shift:true}, function() { moveSelection(-20,0); d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* right */ 39, function() { moveSelection(1,0);d3.event.preventDefault();},endKeyboardMove); | ||||
|         RED.keyboard.add("workspace",/* right */ 39, {shift:true}, function() { moveSelection(20,0); d3.event.preventDefault();},endKeyboardMove); | ||||
|     } | ||||
|  | ||||
|     function canvasMouseDown() { | ||||
|         var point; | ||||
|  | ||||
|         if (!mousedown_node && !mousedown_link) { | ||||
|             selected_link = null; | ||||
|             updateSelection(); | ||||
| @@ -470,19 +513,104 @@ RED.view = (function() { | ||||
|                 lasso.remove(); | ||||
|                 lasso = null; | ||||
|             } | ||||
|         } | ||||
|         if (mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) { | ||||
|             if (d3.event.metaKey || d3.event.ctrlKey) { | ||||
|                 point = d3.mouse(this); | ||||
|                 d3.event.stopPropagation(); | ||||
|                 var mainPos = $("#main-container").position(); | ||||
|  | ||||
|                 if (mouse_mode !== RED.state.QUICK_JOINING) { | ||||
|                     mouse_mode = RED.state.QUICK_JOINING; | ||||
|                     $(window).on('keyup',disableQuickJoinEventHandler); | ||||
|                 } | ||||
|  | ||||
|                 RED.typeSearch.show({ | ||||
|                     x:d3.event.clientX-mainPos.left-node_width/2, | ||||
|                     y:d3.event.clientY-mainPos.top-node_height/2, | ||||
|                     add: function(type) { | ||||
|                         var result = addNode(type); | ||||
|                         if (!result) { | ||||
|                             return; | ||||
|                         } | ||||
|                         var nn = result.node; | ||||
|                         var historyEvent = result.historyEvent; | ||||
|                         nn.x = point[0]; | ||||
|                         nn.y = point[1]; | ||||
|                         if (mouse_mode === RED.state.QUICK_JOINING) { | ||||
|                             if (drag_lines.length > 0) { | ||||
|                                 var drag_line = drag_lines[0]; | ||||
|                                 var src = null,dst,src_port; | ||||
|  | ||||
|                                 if (drag_line.portType === 0 && nn.inputs > 0) { | ||||
|                                     src = drag_line.node; | ||||
|                                     src_port = drag_line.port; | ||||
|                                     dst = nn; | ||||
|                                 } else if (drag_line.portType === 1 && nn.outputs > 0) { | ||||
|                                     src = nn; | ||||
|                                     dst = drag_line.node; | ||||
|                                     src_port = 0; | ||||
|                                 } | ||||
|                                 if (src !== null) { | ||||
|                                     var link = {source: src, sourcePort:src_port, target: dst}; | ||||
|                                     RED.nodes.addLink(link); | ||||
|                                     historyEvent.links = [link]; | ||||
|                                     hideDragLines(); | ||||
|                                     if (drag_line.portType === 0 && nn.outputs > 0) { | ||||
|                                         showDragLines([{node:nn,port:0,portType:0}]); | ||||
|                                     } else if (drag_line.portType === 1 && nn.inputs > 0) { | ||||
|                                         showDragLines([{node:nn,port:0,portType:1}]); | ||||
|                                     } else { | ||||
|                                         resetMouseVars(); | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     hideDragLines(); | ||||
|                                     resetMouseVars(); | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 if (nn.outputs > 0) { | ||||
|                                     showDragLines([{node:nn,port:0,portType:0}]); | ||||
|                                 } else if (nn.inputs > 0) { | ||||
|                                     showDragLines([{node:nn,port:0,portType:1}]); | ||||
|                                 } else { | ||||
|                                     resetMouseVars(); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|  | ||||
|                         RED.history.push(historyEvent); | ||||
|                         RED.nodes.add(nn); | ||||
|                         RED.editor.validateNode(nn); | ||||
|                         RED.nodes.dirty(true); | ||||
|                         // auto select dropped node - so info shows (if visible) | ||||
|                         clearSelection(); | ||||
|                         nn.selected = true; | ||||
|                         moving_set.push({n:nn}); | ||||
|                         updateActiveNodes(); | ||||
|                         updateSelection(); | ||||
|                         redraw(); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|                 updateActiveNodes(); | ||||
|                 updateSelection(); | ||||
|                 redraw(); | ||||
|             } | ||||
|         } | ||||
|         if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) { | ||||
|             if (!touchStartTime) { | ||||
|                 var point = d3.mouse(this); | ||||
|                 point = d3.mouse(this); | ||||
|                 lasso = vis.append("rect") | ||||
|                     .attr("ox",point[0]) | ||||
|                     .attr("oy",point[1]) | ||||
|                     .attr("rx",1) | ||||
|                     .attr("ry",1) | ||||
|                     .attr("x",point[0]) | ||||
|                     .attr("y",point[1]) | ||||
|                     .attr("width",0) | ||||
|                     .attr("height",0) | ||||
|                     .attr("class","lasso"); | ||||
|                 .attr("ox",point[0]) | ||||
|                 .attr("oy",point[1]) | ||||
|                 .attr("rx",1) | ||||
|                 .attr("ry",1) | ||||
|                 .attr("x",point[0]) | ||||
|                 .attr("y",point[1]) | ||||
|                 .attr("width",0) | ||||
|                 .attr("height",0) | ||||
|                 .attr("class","lasso"); | ||||
|                 d3.event.preventDefault(); | ||||
|             } | ||||
|         } | ||||
| @@ -530,12 +658,12 @@ RED.view = (function() { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && selected_link == null) { | ||||
|         if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && selected_link == null) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         var mousePos; | ||||
|         if (mouse_mode == RED.state.JOINING) { | ||||
|         if (mouse_mode == RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) { | ||||
|             // update drag line | ||||
|             if (drag_lines.length === 0) { | ||||
|                 if (d3.event.shiftKey) { | ||||
| @@ -576,12 +704,17 @@ RED.view = (function() { | ||||
|                             portType: (mousedown_port_type===0)?1:0 | ||||
|                         }) | ||||
|                     } | ||||
|                     showDragLines(links); | ||||
|                     mouse_mode = 0; | ||||
|                     updateActiveNodes(); | ||||
|                     redraw(); | ||||
|                     mouse_mode = RED.state.JOINING; | ||||
|                 } else { | ||||
|                     if (links.length === 0) { | ||||
|                         resetMouseVars(); | ||||
|                         redraw(); | ||||
|                     } else { | ||||
|                         showDragLines(links); | ||||
|                         mouse_mode = 0; | ||||
|                         updateActiveNodes(); | ||||
|                         redraw(); | ||||
|                         mouse_mode = RED.state.JOINING; | ||||
|                     } | ||||
|                 } else if (mousedown_node) { | ||||
|                     showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]); | ||||
|                 } | ||||
|                 selected_link = null; | ||||
| @@ -748,6 +881,9 @@ RED.view = (function() { | ||||
|     function canvasMouseUp() { | ||||
|         var i; | ||||
|         var historyEvent; | ||||
|         if (mouse_mode === RED.state.QUICK_JOINING) { | ||||
|             return; | ||||
|         } | ||||
|         if (mousedown_node && mouse_mode == RED.state.JOINING) { | ||||
|             var removedLinks = []; | ||||
|             for (i=0;i<drag_lines.length;i++) { | ||||
| @@ -843,7 +979,7 @@ RED.view = (function() { | ||||
|             } | ||||
|         } | ||||
|         if (mouse_mode == RED.state.IMPORT_DRAGGING) { | ||||
|             RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|             RED.keyboard.remove("escape"); | ||||
|             updateActiveNodes(); | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
| @@ -987,6 +1123,7 @@ RED.view = (function() { | ||||
|     } | ||||
|  | ||||
|     function endKeyboardMove() { | ||||
|         endMoveSet = false; | ||||
|         if (moving_set.length > 0) { | ||||
|             var ns = []; | ||||
|             for (var i=0;i<moving_set.length;i++) { | ||||
| @@ -1001,14 +1138,21 @@ RED.view = (function() { | ||||
|             RED.nodes.dirty(true); | ||||
|         } | ||||
|     } | ||||
|     var endMoveSet = false; | ||||
|     function moveSelection(dx,dy) { | ||||
|         if (moving_set.length > 0) { | ||||
|             if (!endMoveSet) { | ||||
|                 $(document).one('keyup',endKeyboardMove); | ||||
|                 endMoveSet = true; | ||||
|             } | ||||
|             var minX = 0; | ||||
|             var minY = 0; | ||||
|             var node; | ||||
|  | ||||
|             for (var i=0;i<moving_set.length;i++) { | ||||
|                 node = moving_set[i]; | ||||
|                 node.n.changed = true; | ||||
|                 node.n.dirty = true; | ||||
|                 if (node.ox == null && node.oy == null) { | ||||
|                     node.ox = node.n.x; | ||||
|                     node.oy = node.n.y; | ||||
| @@ -1031,6 +1175,16 @@ RED.view = (function() { | ||||
|             redraw(); | ||||
|         } | ||||
|     } | ||||
|     function editSelection() { | ||||
|         if (moving_set.length > 0) { | ||||
|             var node = moving_set[0].n; | ||||
|             if (node.type === "subflow") { | ||||
|                 RED.editor.editSubflow(activeSubflow); | ||||
|             } else { | ||||
|                 RED.editor.edit(node); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     function deleteSelection() { | ||||
|         if (moving_set.length > 0 || selected_link != null) { | ||||
|             var result; | ||||
| @@ -1165,22 +1319,45 @@ RED.view = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function disableQuickJoinEventHandler(evt) { | ||||
|         // Check for ctrl (all browsers), "Meta" (Chrome/FF), keyCode 91 (Safari) | ||||
|         if (evt.keyCode === 17 || evt.key === "Meta" || evt.keyCode === 91) { | ||||
|             resetMouseVars(); | ||||
|             hideDragLines(); | ||||
|             redraw(); | ||||
|             $(window).off('keyup',disableQuickJoinEventHandler); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function portMouseDown(d,portType,portIndex) { | ||||
|         //console.log(d,portType,portIndex); | ||||
|         // disable zoom | ||||
|         //vis.call(d3.behavior.zoom().on("zoom"), null); | ||||
|         mousedown_node = d; | ||||
|         mouse_mode = RED.state.JOINING; | ||||
|         mousedown_port_type = portType; | ||||
|         mousedown_port_index = portIndex || 0; | ||||
|         document.body.style.cursor = "crosshair"; | ||||
|         if (mouse_mode !== RED.state.QUICK_JOINING) { | ||||
|             mouse_mode = RED.state.JOINING; | ||||
|             document.body.style.cursor = "crosshair"; | ||||
|             if (d3.event.ctrlKey || d3.event.metaKey) { | ||||
|                 mouse_mode = RED.state.QUICK_JOINING; | ||||
|                 showDragLines([{node:mousedown_node,port:mousedown_port_index,portType:mousedown_port_type}]); | ||||
|                 $(window).on('keyup',disableQuickJoinEventHandler); | ||||
|             } | ||||
|         } | ||||
|         d3.event.stopPropagation(); | ||||
|         d3.event.preventDefault(); | ||||
|     } | ||||
|  | ||||
|     function portMouseUp(d,portType,portIndex) { | ||||
|         var i; | ||||
|         if (mouse_mode === RED.state.QUICK_JOINING) { | ||||
|             if (drag_lines[0].node===d) { | ||||
|                 return | ||||
|             } | ||||
|         } | ||||
|         document.body.style.cursor = ""; | ||||
|         if (mouse_mode == RED.state.JOINING && drag_lines.length > 0) { | ||||
|         if (mouse_mode == RED.state.JOINING || mouse_mode == RED.state.QUICK_JOINING) { | ||||
|             if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) { | ||||
|                 RED.nodes.eachNode(function(n) { | ||||
|                     if (n.z == RED.workspaces.active()) { | ||||
| @@ -1247,6 +1424,21 @@ RED.view = (function() { | ||||
|                 updateActiveNodes(); | ||||
|                 RED.nodes.dirty(true); | ||||
|             } | ||||
|             if (mouse_mode === RED.state.QUICK_JOINING) { | ||||
|                 if (addedLinks.length > 0) { | ||||
|                     hideDragLines(); | ||||
|                     if (portType === 1 && d.outputs > 0) { | ||||
|                         showDragLines([{node:d,port:0,portType:0}]); | ||||
|                     } else if (portType === 0 && d.inputs > 0) { | ||||
|                         showDragLines([{node:d,port:0,portType:1}]); | ||||
|                     } else { | ||||
|                         resetMouseVars(); | ||||
|                     } | ||||
|                 } | ||||
|                 redraw(); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             resetMouseVars(); | ||||
|             hideDragLines(); | ||||
|             selected_link = null; | ||||
| @@ -1276,7 +1468,7 @@ RED.view = (function() { | ||||
|         //var pos = [touch0.pageX,touch0.pageY]; | ||||
|         //RED.touch.radialMenu.show(d3.select(this),pos); | ||||
|         if (mouse_mode == RED.state.IMPORT_DRAGGING) { | ||||
|             RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|             RED.keyboard.remove("escape"); | ||||
|  | ||||
|             if (activeSpliceLink) { | ||||
|                 // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp | ||||
| @@ -1306,6 +1498,9 @@ RED.view = (function() { | ||||
|             resetMouseVars(); | ||||
|             d3.event.stopPropagation(); | ||||
|             return; | ||||
|         } else if (mouse_mode == RED.state.QUICK_JOINING) { | ||||
|             d3.event.stopPropagation(); | ||||
|             return; | ||||
|         } | ||||
|         mousedown_node = d; | ||||
|         var now = Date.now(); | ||||
| @@ -1623,14 +1818,14 @@ RED.view = (function() { | ||||
|                             nodeMouseUp.call(this,d); | ||||
|                         }) | ||||
|                         .on("mouseover",function(d) { | ||||
|                                 if (mouse_mode === 0) { | ||||
|                                     var node = d3.select(this); | ||||
|                                     node.classed("node_hovered",true); | ||||
|                                 } | ||||
|                             if (mouse_mode === 0) { | ||||
|                                 var node = d3.select(this); | ||||
|                                 node.classed("node_hovered",true); | ||||
|                             } | ||||
|                         }) | ||||
|                         .on("mouseout",function(d) { | ||||
|                                 var node = d3.select(this); | ||||
|                                 node.classed("node_hovered",false); | ||||
|                             var node = d3.select(this); | ||||
|                             node.classed("node_hovered",false); | ||||
|                         }); | ||||
|  | ||||
|                    //node.append("rect").attr("class", "node-gradient-top").attr("rx", 6).attr("ry", 6).attr("height",30).attr("stroke","none").attr("fill","url(#gradient-top)").style("pointer-events","none"); | ||||
| @@ -2270,8 +2465,8 @@ RED.view = (function() { | ||||
|                                            node.n._def.outputs > 0; | ||||
|                         } | ||||
|                     } | ||||
|                     RED.keyboard.add("*",/* ESCAPE */ 27,function(){ | ||||
|                             RED.keyboard.remove(/* ESCAPE */ 27); | ||||
|                     RED.keyboard.add("*","escape",function(){ | ||||
|                             RED.keyboard.remove("escape"); | ||||
|                             clearSelection(); | ||||
|                             RED.history.pop(); | ||||
|                             mouse_mode = 0; | ||||
| @@ -2316,6 +2511,24 @@ RED.view = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function toggleShowGrid(state) { | ||||
|         if (state) { | ||||
|             grid.style("visibility","visible"); | ||||
|         } else { | ||||
|             grid.style("visibility","hidden"); | ||||
|         } | ||||
|     } | ||||
|     function toggleSnapGrid(state) { | ||||
|         snapGrid = state; | ||||
|         redraw(); | ||||
|     } | ||||
|     function toggleStatus(s) { | ||||
|         showStatus = s; | ||||
|         RED.nodes.eachNode(function(n) { n.dirty = true;}); | ||||
|         //TODO: subscribe/unsubscribe here | ||||
|         redraw(); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         state:function(state) { | ||||
| @@ -2335,16 +2548,6 @@ RED.view = (function() { | ||||
|         }, | ||||
|         focus: focusView, | ||||
|         importNodes: importNodes, | ||||
|         status: function(s) { | ||||
|             if (s == null) { | ||||
|                 return showStatus; | ||||
|             } else { | ||||
|                 showStatus = s; | ||||
|                 RED.nodes.eachNode(function(n) { n.dirty = true;}); | ||||
|                 //TODO: subscribe/unsubscribe here | ||||
|                 redraw(); | ||||
|             } | ||||
|         }, | ||||
|         calculateTextWidth: calculateTextWidth, | ||||
|         select: function(selection) { | ||||
|             if (typeof selection !== "undefined") { | ||||
| @@ -2371,17 +2574,6 @@ RED.view = (function() { | ||||
|             } | ||||
|             return selection; | ||||
|         }, | ||||
|         toggleShowGrid: function(state) { | ||||
|             if (state) { | ||||
|                 grid.style("visibility","visible"); | ||||
|             } else { | ||||
|                 grid.style("visibility","hidden"); | ||||
|             } | ||||
|         }, | ||||
|         toggleSnapGrid: function(state) { | ||||
|             snapGrid = state; | ||||
|             redraw(); | ||||
|         }, | ||||
|         scale: function() { | ||||
|             return scaleFactor; | ||||
|         }, | ||||
| @@ -2405,7 +2597,6 @@ RED.view = (function() { | ||||
|                     node.highlighted = true; | ||||
|                     node.dirty = true; | ||||
|                     RED.workspaces.show(node.z); | ||||
|                     RED.view.redraw(); | ||||
|  | ||||
|                     var screenSize = [$("#chart").width(),$("#chart").height()]; | ||||
|                     var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()]; | ||||
| @@ -2419,17 +2610,23 @@ RED.view = (function() { | ||||
|                         },200); | ||||
|                     } | ||||
|  | ||||
|                     var flash = 22; | ||||
|                     var flashFunc = function() { | ||||
|                         flash--; | ||||
|                         node.highlighted = !node.highlighted; | ||||
|                         node.dirty = true; | ||||
|                         RED.view.redraw(); | ||||
|                         if (flash >= 0) { | ||||
|                             setTimeout(flashFunc,100); | ||||
|                     if (!node._flashing) { | ||||
|                         node._flashing = true; | ||||
|                         var flash = 22; | ||||
|                         var flashFunc = function() { | ||||
|                             flash--; | ||||
|                             node.dirty = true; | ||||
|                             if (flash >= 0) { | ||||
|                                 node.highlighted = !node.highlighted; | ||||
|                                 setTimeout(flashFunc,100); | ||||
|                             } else { | ||||
|                                 node.highlighted = false; | ||||
|                                 delete node._flashing; | ||||
|                             } | ||||
|                             RED.view.redraw(); | ||||
|                         } | ||||
|                         flashFunc(); | ||||
|                     } | ||||
|                     flashFunc(); | ||||
|                 } else if (node._def.category === 'config') { | ||||
|                     RED.sidebar.config.show(id); | ||||
|                 } | ||||
|   | ||||
| @@ -170,6 +170,9 @@ RED.workspaces = (function() { | ||||
|         createWorkspaceTabs(); | ||||
|         RED.events.on("sidebar:resize",workspace_tabs.resize); | ||||
|  | ||||
|         RED.actions.add("core:show-next-tab",workspace_tabs.nextTab); | ||||
|         RED.actions.add("core:show-previous-tab",workspace_tabs.previousTab); | ||||
|  | ||||
|         RED.menu.setAction('menu-item-workspace-delete',function() { | ||||
|             deleteWorkspace(RED.nodes.workspace(activeWorkspace)); | ||||
|         }); | ||||
| @@ -177,6 +180,14 @@ RED.workspaces = (function() { | ||||
|         $(window).resize(function() { | ||||
|             workspace_tabs.resize(); | ||||
|         }); | ||||
|  | ||||
|         RED.actions.add("core:add-flow",addWorkspace); | ||||
|         RED.actions.add("core:edit-flow",editWorkspace); | ||||
|         RED.actions.add("core:remove-flow",removeWorkspace); | ||||
|     } | ||||
|  | ||||
|     function editWorkspace(id) { | ||||
|         showRenameWorkspaceDialog(id||activeWorkspace); | ||||
|     } | ||||
|  | ||||
|     function removeWorkspace(ws) { | ||||
| @@ -201,9 +212,7 @@ RED.workspaces = (function() { | ||||
|         add: addWorkspace, | ||||
|         remove: removeWorkspace, | ||||
|         order: setWorkspaceOrder, | ||||
|         edit: function(id) { | ||||
|             showRenameWorkspaceDialog(id||activeWorkspace); | ||||
|         }, | ||||
|         edit: editWorkspace, | ||||
|         contains: function(id) { | ||||
|             return workspace_tabs.contains(id); | ||||
|         }, | ||||
|   | ||||
							
								
								
									
										8
									
								
								editor/sass/ace.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								editor/sass/ace.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| .ace_gutter { | ||||
|     border-top-left-radius: 4px; | ||||
|     border-bottom-left-radius: 4px; | ||||
| } | ||||
| .ace_scroller { | ||||
|     border-top-right-radius: 4px; | ||||
|     border-bottom-right-radius: 4px; | ||||
| } | ||||
| @@ -58,7 +58,7 @@ $workspace-button-color-focus-outline: rgba(85,150,230,0.2); | ||||
|  | ||||
| $typedInput-button-background: #efefef; | ||||
| $typedInput-button-background-hover: #ddd; | ||||
| $typedInput-button-background-active: #e3e3e3; | ||||
| $typedInput-button-background-active: #ddd; | ||||
|  | ||||
| $editor-button-color-primary: #eee; | ||||
| $editor-button-background-primary: #AD1625; | ||||
|   | ||||
							
								
								
									
										185
									
								
								editor/sass/debug.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								editor/sass/debug.scss
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,185 @@ | ||||
| /** | ||||
|  * Copyright 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| .debug-window { | ||||
|     padding:0; | ||||
|     margin:0; | ||||
|     background: #fff; | ||||
|     line-height: 20px; | ||||
| } | ||||
| .debug-window .debug-message-payload { | ||||
|     font-size: 14px; | ||||
| } | ||||
| .debug-content { | ||||
|     position: absolute; | ||||
|     top: 43px; | ||||
|     bottom: 0px; | ||||
|     left:0px; | ||||
|     right: 0px; | ||||
|     overflow-y: scroll; | ||||
| } | ||||
| .debug-filter-box { | ||||
|     position:absolute; | ||||
|     top: 42px; | ||||
|     left: 0px; | ||||
|     right: 0px; | ||||
|     background: #f9f9f9; | ||||
|     padding: 10px; | ||||
|     border-bottom: 1px solid #ddd; | ||||
|     box-shadow: 0 2px 6px rgba(0,0,0,0.1); | ||||
| } | ||||
| .debug-filter-row { | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .debug-message { | ||||
|     border-bottom: 1px solid #eee; | ||||
|     border-left: 8px solid #eee; | ||||
|     border-right: 8px solid #eee; | ||||
|     padding: 2px; | ||||
| } | ||||
| .debug-message-meta { | ||||
|     background: #fff; | ||||
|     font-size: 10px; | ||||
|     color: #777; | ||||
| } | ||||
| .debug-message-date { | ||||
|     padding: 1px 5px 1px 1px; | ||||
| } | ||||
| .debug-message-topic { | ||||
|     display: block; | ||||
|     color: #a66; | ||||
| } | ||||
| .debug-message-name { | ||||
|     padding: 1px 5px; | ||||
|     color: #777; | ||||
| } | ||||
| .debug-message-payload { | ||||
|     display: block; | ||||
|     padding: 2px; | ||||
|     background: #fff; | ||||
| } | ||||
| .debug-message-level-log { | ||||
|     border-left-color: #eee; | ||||
|     border-right-color: #eee; | ||||
| } | ||||
| .debug-message-level-30 { | ||||
|     border-left-color: #ffdf9d; | ||||
|     border-right-color: #ffdf9d; | ||||
| } | ||||
| .debug-message-level-20 { | ||||
|     border-left-color: #f99; | ||||
|     border-right-color: #f99; | ||||
| } | ||||
| .debug-message-object-entry { | ||||
|     padding-left: 15px; | ||||
|     padding-bottom: 3px; | ||||
|     padding-top: 3px; | ||||
| } | ||||
| .debug-message-element { | ||||
|     color: #333; | ||||
|     font-family: Menlo, monospace; | ||||
|     font-size: 12px !important; | ||||
|     line-height: 1.3em; | ||||
| } | ||||
| .debug-message-object-key { | ||||
|     color: #792e90; | ||||
| } | ||||
| .debug-message-object-value { | ||||
|  | ||||
| } | ||||
| .debug-message-object-handle { | ||||
|     color: #666; | ||||
|     font-size: 1em; | ||||
|     width: 1em; | ||||
|     text-align: center; | ||||
|     transition: transform 0.1s ease-in-out; | ||||
|     transform: rotate(90deg); | ||||
| } | ||||
| .debug-message-element:not(.debug-message-top-level)>.debug-message-expandable>.debug-message-object-handle { | ||||
|     margin-left: -1em; | ||||
| } | ||||
| .debug-message-object-entry>.debug-message-expandable>.debug-message-object-handle { | ||||
|     margin-left: -1em; | ||||
| } | ||||
| .debug-message-object-entry.collapsed>span>.debug-message-object-handle { | ||||
|     transform: rotate(0deg); | ||||
| } | ||||
| .debug-message-element.collapsed>span>.debug-message-object-handle { | ||||
|     transform: rotate(0deg); | ||||
| } | ||||
|  | ||||
| .debug-message-object-entry.collapsed > .debug-message-object-entry { | ||||
|     display:none; | ||||
| } | ||||
|  | ||||
| .debug-message-element.collapsed .debug-message-object-entry { | ||||
|     display:none; | ||||
| } | ||||
| .debug-message-element:not(.collapsed)>.debug-message-expandable>.debug-message-object-value>.debug-message-object-header { | ||||
|     display:none; | ||||
| } | ||||
| .debug-message-element.collapsed .debug-message-buffer-opts { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| .debug-message-element.collapsed .debug-message-object-type-header { | ||||
|     display:none; | ||||
| } | ||||
| .debug-message-object-entry pre { | ||||
|     font-family: Menlo, monospace; | ||||
|     font-size: 12px; | ||||
|     line-height: 1.4em; | ||||
|     margin: 0 0 0 -1em; | ||||
| } | ||||
|  | ||||
| .debug-message-type-other { color: #2033d6; } | ||||
| .debug-message-type-string { color: #b72828; } | ||||
| .debug-message-type-null { color: #666; font-style: italic;} | ||||
| .debug-message-type-meta { color: #666; font-style: italic;} | ||||
| .debug-message-type-number { color: #2033d6; }; | ||||
| .debug-message-type-number-toggle { cursor: pointer;} | ||||
|  | ||||
| .debug-message-expandable { | ||||
|     cursor: pointer; | ||||
| } | ||||
| .debug-message-expandable:hover { | ||||
|     background: #fefefe; | ||||
| } | ||||
| .debug-message-expandable:hover .debug-message-object-handle { | ||||
|     color: #b72828 !important; | ||||
| } | ||||
|  | ||||
| .debug-message-buffer-opts a { | ||||
|     font-size: 9px; | ||||
|     color: #bbb; | ||||
|     border: 1px solid #bbb; | ||||
|     border-radius: 2px; | ||||
|     padding: 2px 5px; | ||||
|     margin-left: 5px; | ||||
| } | ||||
| .debug-message-buffer-opts a:hover { | ||||
|     text-decoration: none; | ||||
|     color: #999; | ||||
|     border: 1px solid #999; | ||||
|     background: #f3f3f3; | ||||
| } | ||||
| .debug-message-buffer-raw > .debug-message-string-rows { | ||||
|     display: none; | ||||
| } | ||||
| .debug-message-buffer-string > .debug-message-array-rows { | ||||
|     display: none; | ||||
| } | ||||
| @@ -21,30 +21,67 @@ | ||||
|     .red-ui-editableList-container { | ||||
|         border-radius:1px; | ||||
|         padding:0; | ||||
|         background: #f9f9f9; | ||||
|     } | ||||
|     ol { | ||||
|     #node-dialog-view-diff-diff { | ||||
|         position: absolute; | ||||
|         top:10px; | ||||
|         top:80px; | ||||
|         bottom:10px; | ||||
|         left:10px; | ||||
|         right:10px; | ||||
|         li { | ||||
|             background: #f9f9f9; | ||||
|             padding: 0px; | ||||
|             border: none; | ||||
|             min-height: 0; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     .red-ui-editableList-item-content { | ||||
|         padding: 5px; | ||||
|         // padding-bottom: 5px; | ||||
|     } | ||||
| } | ||||
| #node-dialog-view-diff-headers { | ||||
|     position: absolute; | ||||
|     left:17px; | ||||
|     right:32px; | ||||
|     top: 55px; | ||||
|     height: 25px; | ||||
|     .node-diff-node-entry-cell:not(:first-child) { | ||||
|         background: #f9f9f9; | ||||
|         text-align: center; | ||||
|         border-top: 1px solid $secondary-border-color; | ||||
|         border-color:$secondary-border-color; | ||||
|     } | ||||
|     .node-diff-node-entry-cell:last-child { | ||||
|         border-right: 1px solid $secondary-border-color; | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-toolbar { | ||||
|     position:absolute; | ||||
|     top:0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     height: 43px; | ||||
|     box-sizing: border-box; | ||||
|     color: #666; | ||||
|     text-align: right; | ||||
|     padding: 8px 10px; | ||||
|     background: #f3f3f3; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     white-space: nowrap; | ||||
| } | ||||
| .node-diff-tab { | ||||
|     border: 1px solid $secondary-border-color; | ||||
|     border-radius: 3px; | ||||
|     background: #fff; | ||||
|     border: 1px solid #ddd; | ||||
|     border-radius: 1px; | ||||
|     overflow: hidden; | ||||
|  | ||||
|     &.collapsed { | ||||
|         .node-diff-tab-title > .node-diff-chevron { | ||||
|         .node-diff-tab-title .node-diff-chevron { | ||||
|             transform: rotate(-90deg); | ||||
|         } | ||||
|         .node-diff-node-entry { | ||||
| @@ -53,21 +90,26 @@ | ||||
|     } | ||||
| } | ||||
| .node-diff-tab-stats { | ||||
|     position: absolute; | ||||
|     left: 50%; | ||||
|     font-size: 0.9em; | ||||
| } | ||||
|  | ||||
| .node-diff-chevron { | ||||
|     display: inline-block; | ||||
|     width: 15px; | ||||
|     text-align: center; | ||||
|     margin: 3px 5px 3px 5px; | ||||
|     margin-left: 3px; | ||||
|     transition: transform 0.1s ease-in-out; | ||||
|  | ||||
| } | ||||
| .node-diff-node-entry { | ||||
|     padding: 0 0 0 5px; | ||||
|     margin-left: 20px; | ||||
|     font-size: 0.9em; | ||||
|  | ||||
|     &:first-child { | ||||
|         border-top: 1px solid #eee; | ||||
|     } | ||||
|     &:not(:last-child) { | ||||
|         border-bottom: 1px solid $secondary-border-color; | ||||
|         border-bottom: 1px solid #eee; | ||||
|     } | ||||
|  | ||||
|     &.collapsed { | ||||
| @@ -78,25 +120,80 @@ | ||||
|             display: none; | ||||
|         } | ||||
|     } | ||||
|     &:not(.collapsed) { | ||||
|         .node-diff-node-entry-cell:not(:first-child) { | ||||
|             //display: none; | ||||
|         } | ||||
|         .node-diff-node-entry-cell:first-child { | ||||
|             //width: 100% | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     table { | ||||
|          border-collapse: collapse; | ||||
|          width: 100%; | ||||
|          table-layout:fixed; | ||||
|         border-collapse: collapse; | ||||
|         table-layout:fixed; | ||||
|          | ||||
|         // Fix for table-layout: fixed on safari: | ||||
|         max-width: none; | ||||
|         width: auto; | ||||
|         min-width: 100%; | ||||
|     } | ||||
|     td, th { | ||||
|         border: 1px solid $secondary-border-color; | ||||
|         padding: 3px 5px; | ||||
|         padding: 0 0 0 3px; | ||||
|         text-align: left; | ||||
|         overflow-x: auto; | ||||
|     } | ||||
|  | ||||
|     td:nth-child(1) { | ||||
|         width: 150px; | ||||
|     tr { | ||||
|         vertical-align: top; | ||||
|         &:first-child td { | ||||
|             white-space:nowrap; | ||||
|             overflow:hidden; | ||||
|         } | ||||
|     } | ||||
|     td:first-child { | ||||
|         width: 140px; | ||||
|     } | ||||
|     td:not(:first-child) { | ||||
|         width: calc(50% - 150px); | ||||
|         width: calc( 100% - 140px); | ||||
|     } | ||||
|     td  { | ||||
|         .node-diff-status { | ||||
|             margin-left: 0; | ||||
|         } | ||||
|     } | ||||
|     tr:not(.node-diff-property-header) { | ||||
|         .node-diff-status { | ||||
|             width: 12px; | ||||
|             margin-left: 0; | ||||
|             margin-top: 0; | ||||
|             margin-bottom: 0; | ||||
|             margin-right: 5px; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .node-diff-three-way { | ||||
|     .node-diff-node-entry-cell { | ||||
|         width: calc((100% - 220px) / 2); | ||||
|         &:first-child { | ||||
|             width: 220px; | ||||
|         } | ||||
|     } | ||||
|     td:not(:first-child) { | ||||
|         width: calc( (100% - 140px) / 2); | ||||
|     } | ||||
|  | ||||
|     .node-diff-node-entry { | ||||
|         .node-diff-node-entry-cell { | ||||
|             width: calc((100% + 20px - 220px) / 2); | ||||
|             &:first-child { | ||||
|                 width: 200px; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-column { | ||||
|     display:inline-block; | ||||
|     height:100%; | ||||
| @@ -108,18 +205,26 @@ | ||||
|         border-right: 1px solid $secondary-border-color | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-tab-title { | ||||
|     padding: 3px 3px 3px 0; | ||||
|     background: #f6f6f6; | ||||
|     cursor: pointer; | ||||
|     padding: 0; | ||||
|     // background: #f6f6f6; | ||||
| } | ||||
| .node-diff-tab-title-meta { | ||||
|     vertical-align: middle; | ||||
|     display: inline-block; | ||||
|     padding-top: 2px; | ||||
| } | ||||
| .node-diff-node-entry-header { | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .node-diff-node-entry-node { | ||||
|     vertical-align: middle; | ||||
|     display: inline-block; | ||||
|     margin: 5px; | ||||
|     width: 24px; | ||||
|     height: 20px; | ||||
|     width: 18px; | ||||
|     height: 15px; | ||||
|     background: #ddd; | ||||
|     border-radius: 2px; | ||||
|     border: 1px solid #999; | ||||
| @@ -128,25 +233,89 @@ | ||||
|     background-size: contain; | ||||
|     position: relative; | ||||
|  | ||||
|     .palette-icon { | ||||
|         width: 16px; | ||||
|     .palette_icon { | ||||
|         background-position: 49% 50%; | ||||
|         width: 15px; | ||||
|     } | ||||
|     .palette_icon_container { | ||||
|         width: 24px; | ||||
|         width: 18px; | ||||
|     } | ||||
| } | ||||
| .node-diff-tab-empty { | ||||
|     .node-diff-chevron i { | ||||
|         display: none; | ||||
|     } | ||||
|     .node-diff-tab-title { | ||||
|         cursor: default; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-deleted { | ||||
|     //background: #fadddd; | ||||
|     cursor: default !important; | ||||
|     .node-diff-status { | ||||
|         color: #f80000; | ||||
|     } | ||||
|     .node-diff-node-entry-node { | ||||
|         opacity: 0.5; | ||||
|     } | ||||
|     .node-diff-node-description { | ||||
|         opacity: 0.5; | ||||
|         text-decoration: line-through; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-added { | ||||
|     //background: #eefaee; | ||||
|     cursor: default !important; | ||||
|     .node-diff-status { | ||||
|         color: #009900; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-moved { | ||||
|     //background: #eefaee; | ||||
|     .node-diff-status { | ||||
|         color: #3f81b3; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-node-changed { | ||||
|     //background: #fff2ca; | ||||
|     .node-diff-status { | ||||
|         color: #f89406; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-unchanged { | ||||
|     //background: #fff2ca; | ||||
|     .node-diff-status { | ||||
|         color: #bbb; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-conflict { | ||||
|     .node-diff-status { | ||||
|         color: #9b45ce; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-entry-title { | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
|     .node-diff-status { | ||||
|         margin-left: 15px; | ||||
|     } | ||||
| } | ||||
| .node-diff-node-entry-properties { | ||||
|     margin-left: 30px; | ||||
|     margin-right: 8px; | ||||
|     margin-bottom:8px; | ||||
|     margin: 5px ; | ||||
|     color: #666; | ||||
| } | ||||
| .node-diff-status { | ||||
|     display: inline-block; | ||||
|     height: 20px; | ||||
|     margin-left: 5px; | ||||
|     vertical-align: top; | ||||
|     margin-top: 6px; | ||||
|     margin-bottom: 6px; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .node-diff-node-description { | ||||
|     color: $form-text-color; | ||||
|     margin-left: 5px; | ||||
|     margin-right: 5px; | ||||
|     padding-top: 5px; | ||||
|     display: inline-block; | ||||
| @@ -156,10 +325,192 @@ | ||||
|         clear: both; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-node-meta { | ||||
|     float: right; | ||||
|     //font-size: 0.9em; | ||||
|     color: #999; | ||||
|     margin-top: 7px; | ||||
|     margin-right: 10px; | ||||
| } | ||||
|  | ||||
| .node-diff-count { color: #999} | ||||
| .node-diff-added { color: #009900} | ||||
| .node-diff-deleted { color: #f80000} | ||||
| .node-diff-changed { color: #f89406} | ||||
| .node-diff-conflicted { color: purple} | ||||
|  | ||||
|  | ||||
| .node-diff-node-entry-cell { | ||||
|     display: inline-block; | ||||
|     vertical-align: top; | ||||
|     box-sizing: border-box; | ||||
|     width: calc( (100% - 20px) / 2); | ||||
|     height: 32px; | ||||
|     border-left: 1px solid #eee; | ||||
|     padding-top: 2px; | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
| } | ||||
| .node-diff-empty { | ||||
|     background: #f3f3f3; | ||||
|     background: repeating-linear-gradient( | ||||
|         20deg, | ||||
|         #fff, #fff 5px, | ||||
|         #f6f6f6 5px, | ||||
|         #f6f6f6 10px | ||||
|         ); | ||||
| } | ||||
| .node-diff-node-entry-cell:first-child { | ||||
|     border-left: none; | ||||
| } | ||||
| .node-diff-property-cell-label { | ||||
|     margin-left: 20px; | ||||
|     vertical-align: top; | ||||
|     box-sizing: border-box; | ||||
|     padding-left: 8px; | ||||
|     width: 120px; | ||||
| } | ||||
| .node-diff-property-wires { | ||||
|     display: inline-block; | ||||
|     .node-diff-node-entry-node { | ||||
|         width: 18px; | ||||
|         height: 15px; | ||||
|     } | ||||
|     .palette_icon_container { | ||||
|         width: 18px; | ||||
|     } | ||||
|     .palette_icon { | ||||
|         width: 15px; | ||||
|     } | ||||
|     ul,li,ol { | ||||
|         background: none !important; | ||||
|     } | ||||
|     ul { | ||||
|         vertical-align: middle; | ||||
|         display: inline-block; | ||||
|         margin-left: 5px; | ||||
|     } | ||||
|     li { | ||||
|         list-style-type: none !important; | ||||
|     } | ||||
|     ol { | ||||
|         font-size: 0.9em; | ||||
|         margin: 0; | ||||
|         & > span { | ||||
|             vertical-align: middle; | ||||
|             display: inline-block; | ||||
|             width: 30px; | ||||
|             text-align: center; | ||||
|         } | ||||
|         & > li:not(:last-child) { | ||||
|             border-bottom: 1px solid #999; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| .node-diff-node-props .node-diff-node-entry-cell:first-child { | ||||
|     padding: 6px 0px; | ||||
|     span:not(.node-diff-chevron) { | ||||
|         margin-left: 5px; | ||||
|     } | ||||
|  | ||||
| } | ||||
| .node-diff-property-cell { | ||||
|     // vertical-align: top; | ||||
|     // display:inline-block; | ||||
|     // | ||||
|     // box-sizing: border-box; | ||||
|     // padding: 1px 5px; | ||||
|     //min-height: 30px; | ||||
|  | ||||
|     &.node-diff-node-changed { | ||||
|         background: #fff2e1 !important; | ||||
|     } | ||||
|     &.node-diff-node-conflict { | ||||
|         background: #ffdad4 !important; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-selectbox { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     right:0; | ||||
|     bottom:0; | ||||
|     width: 35px; | ||||
|     text-align: center; | ||||
|     border-left: 1px solid #eee; | ||||
|     margin:0; | ||||
|     input { | ||||
|         margin-top: 8px; | ||||
|     } | ||||
|  | ||||
|     &:hover { | ||||
|         background: #f3f3f3; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .node-diff-node-entry-conflict.node-diff-select-remote { | ||||
|     .node-diff-node-remote { | ||||
|         background: #e7ffe3; | ||||
|         label { | ||||
|             border-left-color: #b8daad; | ||||
|         } | ||||
|     } | ||||
|     .node-diff-node-local { | ||||
|         background: #ffe1e1; | ||||
|         label { | ||||
|             border-left-color: #e4bcbc; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .node-diff-node-entry-conflict.node-diff-select-local { | ||||
|     .node-diff-node-local { | ||||
|         background: #e7ffe3; | ||||
|         label { | ||||
|             border-left-color: #b8daad; | ||||
|         } | ||||
|     } | ||||
|     .node-diff-node-remote { | ||||
|         background: #ffe1e1; | ||||
|         label { | ||||
|             border-left-color: #e4bcbc; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| #node-dialog-confirm-deploy { | ||||
|     .node-dialog-confirm-row { | ||||
|         text-align: left; padding-top: 10px; | ||||
|     } | ||||
|     ul { | ||||
|         font-size: 0.9em; | ||||
|         width: 400px; | ||||
|         margin: 10px auto; | ||||
|         text-align: left; | ||||
|     } | ||||
|     .node-dialog-confirm-conflict-row { | ||||
|         img { | ||||
|             vertical-align:middle; | ||||
|             height: 30px; | ||||
|             margin-right: 10px; | ||||
|         } | ||||
|         i { | ||||
|              vertical-align:middle; | ||||
|              text-align: center; | ||||
|              font-size: 30px; | ||||
|              width: 30px; | ||||
|              margin-right: 10px; | ||||
|         } | ||||
|         div { | ||||
|             vertical-align: middle; | ||||
|             width: calc(100% - 60px); | ||||
|             display:inline-block; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #node-diff-toolbar-resolved-conflicts .node-diff-status { | ||||
|     margin:0; | ||||
| } | ||||
|   | ||||
| @@ -168,12 +168,7 @@ | ||||
|     color: $workspace-button-color; | ||||
| } | ||||
| #palette-shade, #editor-shade, #header-shade, #sidebar-shade  { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     bottom:0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     background: $shade-color; | ||||
|     @include shade; | ||||
|     z-index: 2; | ||||
| } | ||||
| #sidebar-shade  { | ||||
|   | ||||
| @@ -170,6 +170,11 @@ | ||||
|     stroke-dasharray:8, 3; | ||||
| } | ||||
|  | ||||
|  | ||||
| .node_quickadd * { | ||||
|     stroke-dasharray: 12,3; | ||||
| } | ||||
|  | ||||
| .node_status_label { | ||||
|     @include disable-selection; | ||||
|     stroke-width: 0; | ||||
| @@ -183,6 +188,13 @@ | ||||
|     stroke: $port-selected-color; | ||||
|     fill:  $port-selected-color; | ||||
| } | ||||
|  | ||||
| .port_quick_link { | ||||
|     stroke: $port-selected-color; | ||||
|     fill:  $port-selected-color; | ||||
| } | ||||
|  | ||||
|  | ||||
| .subflowport { | ||||
|     stroke-dasharray: 5,5; | ||||
|     fill: #eee; | ||||
|   | ||||
| @@ -37,4 +37,6 @@ | ||||
|     font-family: Courier, monospace; | ||||
|     box-shadow: #999 1px 1px 1px; | ||||
| } | ||||
|  | ||||
| .help-key-block { | ||||
|     white-space: nowrap; | ||||
| } | ||||
|   | ||||
| @@ -70,7 +70,6 @@ | ||||
|     &.selected:not(.disabled) { | ||||
|         color: $workspace-button-color-selected !important; | ||||
|         background: $workspace-button-background-active; | ||||
|         cursor: default; | ||||
|     } | ||||
|     .button-group &:not(:first-child) { | ||||
|         border-left: none; | ||||
| @@ -86,6 +85,12 @@ | ||||
|         outline: 1px solid $workspace-button-color-focus-outline; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .button-group:not(:last-child) { | ||||
|     margin-right: 10px; | ||||
| } | ||||
|  | ||||
|  | ||||
| @mixin workspace-button-toggle { | ||||
|     @include workspace-button; | ||||
|     color: $workspace-button-toggle-color !important; | ||||
| @@ -97,6 +102,7 @@ | ||||
|         border-bottom-width: 2px; | ||||
|         border-bottom-color: $form-input-border-selected-color; | ||||
|         margin-bottom: 0; | ||||
|         cursor: default; | ||||
|     } | ||||
|     &.disabled { | ||||
|         color: $workspace-button-toggle-color-disabled !important; | ||||
| @@ -115,6 +121,12 @@ | ||||
|     height: 25px; | ||||
|     line-height: 23px; | ||||
|     padding: 0 10px; | ||||
|  | ||||
|  | ||||
|     .button-group:not(:last-child) { | ||||
|         margin-right: 5px; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @mixin component-footer-button { | ||||
| @@ -128,9 +140,28 @@ | ||||
|         padding: 0 5px; | ||||
|     } | ||||
| } | ||||
| @mixin component-footer-button-toggle { | ||||
|     @include workspace-button-toggle; | ||||
|     font-size: 11px; | ||||
|     line-height: 17px; | ||||
|     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); | ||||
|  | ||||
| } | ||||
|  | ||||
| @mixin shade { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     bottom: 0; | ||||
|     right: 0; | ||||
|     background: $shade-color; | ||||
| } | ||||
|   | ||||
| @@ -72,10 +72,6 @@ | ||||
|         padding: 8px 10px; | ||||
|         border-bottom: 1px solid $primary-border-color; | ||||
|         text-align: right; | ||||
|  | ||||
|         .button-group { | ||||
|             margin-right: 10px; | ||||
|         } | ||||
|     } | ||||
|     .palette-module-button-group { | ||||
|         position: absolute; | ||||
| @@ -86,12 +82,7 @@ | ||||
|         } | ||||
|     } | ||||
|     .palette-module-shade { | ||||
|         position: absolute; | ||||
|         top: 0; | ||||
|         bottom:0; | ||||
|         left:0; | ||||
|         right:0; | ||||
|         background: $shade-color; | ||||
|         @include shade; | ||||
|         text-align: center; | ||||
|         padding-top: 20px; | ||||
|     } | ||||
|   | ||||
| @@ -29,6 +29,77 @@ | ||||
|     ol { | ||||
|     } | ||||
| } | ||||
| .red-ui-type-search-shade { | ||||
|     @include shade; | ||||
|     z-index: 20; | ||||
|     position: fixed; | ||||
|     background: rgba(255,255,255,0.05); | ||||
| } | ||||
| .red-ui-type-search { | ||||
|     box-shadow: 0 1px 6px -3px black; | ||||
|     background: none; | ||||
|     width: 300px; | ||||
|     margin-left: 0px; | ||||
|     //height: 75px; | ||||
|     border: none; | ||||
|     .red-ui-search-container { | ||||
|         border-top-left-radius: 5px; | ||||
|         border-top-right-radius: 5px; | ||||
|         border: 1px dashed #aaa; | ||||
|         border-bottom: none; | ||||
|         padding: 0; | ||||
|     } | ||||
|     .red-ui-search-results-container { | ||||
|         display: none; | ||||
|         height: 150px; | ||||
|         .red-ui-editableList-container { | ||||
|             border: 1px dashed #aaa; | ||||
|             border-top: 1px solid #ccc; | ||||
|         } | ||||
|     } | ||||
|     .red-ui-search-result { | ||||
|         padding: 2px 2px 2px 5px; | ||||
|         font-size: 13px; | ||||
|         border-left-width: 3px; | ||||
|         border-right-width: 3px; | ||||
|     } | ||||
|     .red-ui-search-result-separator { | ||||
|         border-bottom: 3px solid #ddd; | ||||
|     } | ||||
|     .red-ui-search-result-node { | ||||
|         position: relative; | ||||
|         width: 18px; | ||||
|         height: 15px; | ||||
|         margin-top: 1px; | ||||
|     } | ||||
|     .red-ui-search-result-node-port { | ||||
|         position: absolute; | ||||
|         border-radius: 2px; | ||||
|         border: 1px solid #999; | ||||
|         width: 6px; | ||||
|         height: 7px; | ||||
|         top:4px; | ||||
|         left:-4px; | ||||
|         background: #eee; | ||||
|         box-sizing: border-box; | ||||
|     } | ||||
|     .red-ui-search-result-node-output{ | ||||
|         left: 16px; | ||||
|     } | ||||
|     .palette_icon_container { | ||||
|         width: 18px; | ||||
|     } | ||||
|     .palette_icon { | ||||
|         width: 15px; | ||||
|     } | ||||
|     .red-ui-search-result-description { | ||||
|         margin-left:28px; | ||||
|     } | ||||
|     .red-ui-search-result-node-label { | ||||
|         color: #999; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .red-ui-search-container { | ||||
|     padding: 3px; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
| @@ -37,19 +108,21 @@ | ||||
|     position:relative; | ||||
|     height: 300px; | ||||
|     padding: 5px; | ||||
|     background: $background-color; | ||||
|     background: #f9f9f9; | ||||
|  | ||||
|     .red-ui-editableList-container { | ||||
|         background: white; | ||||
|         border-radius: 2px; | ||||
|         padding: 0; | ||||
|         background: $background-color; | ||||
|         background: #f9f9f9; | ||||
|         li { | ||||
|             padding: 0; | ||||
|  | ||||
|             &.selected .red-ui-search-result { | ||||
|                     border-color: $primary-border-color; | ||||
|             &.selected { | ||||
|                 background: #efefef; | ||||
|                 .red-ui-search-result { | ||||
|                     border-left-color:#999; | ||||
|                     border-right-color:#999; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -58,11 +131,19 @@ | ||||
|     display: block; | ||||
|     cursor: pointer; | ||||
|     color: $form-text-color; | ||||
|     border: 2px solid white; | ||||
|     border-left: 3px solid #fff; | ||||
|     border-right: 3px solid #fff; | ||||
|     &:hover { | ||||
|         text-decoration: none; | ||||
|         border-color: $primary-border-color; | ||||
|         color: $form-text-color; | ||||
|         background: #efefef; | ||||
|         border-left-color:#efefef; | ||||
|         border-right-color:#efefef; | ||||
|     } | ||||
|     &:after { | ||||
|         content: ""; | ||||
|         display: table; | ||||
|         clear: both; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -78,6 +159,11 @@ | ||||
|     background-repeat: no-repeat; | ||||
|     background-size: contain; | ||||
|     position: relative; | ||||
|  | ||||
|     .palette_icon_container { | ||||
|         border-right: none; | ||||
|     } | ||||
|  | ||||
| } | ||||
| .red-ui-search-result-description { | ||||
|     margin-left: 40px; | ||||
|   | ||||
| @@ -71,6 +71,7 @@ | ||||
|     padding: 8px 10px; | ||||
|     background: #f3f3f3; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     white-space: nowrap; | ||||
| } | ||||
|  | ||||
| #sidebar-footer { | ||||
| @@ -80,6 +81,9 @@ | ||||
| .sidebar-footer-button { | ||||
|     @include component-footer-button; | ||||
| } | ||||
| .sidebar-footer-button-toggle { | ||||
|     @include component-footer-button-toggle; | ||||
| } | ||||
| .sidebar-header-button { | ||||
|     @include workspace-button; | ||||
|     font-size: 13px; | ||||
| @@ -97,10 +101,5 @@ | ||||
| } | ||||
|  | ||||
| .sidebar-shade { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     bottom:0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     background: $shade-color; | ||||
|     @include shade; | ||||
| } | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
|  | ||||
| @import "jquery"; | ||||
| @import "bootstrap"; | ||||
| @import "ace"; | ||||
|  | ||||
| @import "dropdownMenu"; | ||||
|  | ||||
| @@ -53,6 +54,7 @@ | ||||
|  | ||||
| @import "keyboard"; | ||||
|  | ||||
| @import "debug"; | ||||
|  | ||||
| body { | ||||
|     font-size: 14px; | ||||
| @@ -119,3 +121,7 @@ pre code { | ||||
|   background-color: transparent; | ||||
|   border: 0; | ||||
| } | ||||
|  | ||||
| .hide { | ||||
|     display: none; | ||||
| } | ||||
|   | ||||
| @@ -35,7 +35,7 @@ table.node-info tr.blank td { | ||||
|     padding-left: 0px; | ||||
| } | ||||
| table.node-info td:first-child{ | ||||
|     color: #000; | ||||
|     color: #666; | ||||
|     vertical-align: top; | ||||
|     width: 90px; | ||||
|     padding: 3px; | ||||
| @@ -81,3 +81,18 @@ div.node-info { | ||||
|         margin: 8px auto; | ||||
|     } | ||||
| } | ||||
| .node-info-tip { | ||||
|     position: absolute; | ||||
|     top: 40%; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     padding: 20px; | ||||
|     font-size: 16px; | ||||
|     text-align: center; | ||||
|     line-height: 1.9em; | ||||
|     color : #bbb; | ||||
|     background-color: #fff; | ||||
|     @include disable-selection; | ||||
|     cursor: default; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -49,7 +49,10 @@ | ||||
|         border-color: $form-input-focus-color !important; | ||||
|     } | ||||
|  | ||||
|     a { | ||||
|     button { | ||||
|         text-align: left; | ||||
|         border: none; | ||||
|         position: absolute; | ||||
|         box-sizing: border-box; | ||||
|         border-top-left-radius: 4px; | ||||
|         border-bottom-left-radius: 4px; | ||||
| @@ -66,6 +69,9 @@ | ||||
|             margin-right:4px; | ||||
|             margin-top: 1px; | ||||
|             vertical-align: middle; | ||||
|             &.fa-ellipsis-h { | ||||
|                 top: -1px; | ||||
|             } | ||||
|         } | ||||
|         &.disabled { | ||||
|             cursor: default; | ||||
| @@ -82,44 +88,65 @@ | ||||
|         &:not(.disabled):hover { | ||||
|             text-decoration: none; | ||||
|             background: $typedInput-button-background-hover; | ||||
|  | ||||
|             span { | ||||
|                 background: $typedInput-button-background-hover; | ||||
|             } | ||||
|         } | ||||
|         &:focus { | ||||
|             text-decoration: none; | ||||
|             outline: none; | ||||
|             box-shadow: inset 0 0 0 1px $form-input-focus-color; | ||||
|         } | ||||
|         &:not(.disabled):active { | ||||
|             background: $typedInput-button-background-active; | ||||
|             text-decoration: none; | ||||
|         } | ||||
|         &.red-ui-typedInput-full-width { | ||||
|             width: 100%; | ||||
|             border-top-right-radius: 4px; | ||||
|             border-bottom-right-radius: 4px; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     a.red-ui-typedInput-option-trigger { | ||||
|     button.red-ui-typedInput-option-expand { | ||||
|         border-top-right-radius: 4px; | ||||
|         border-bottom-right-radius: 4px; | ||||
|         border-top-left-radius: 0; | ||||
|         border-bottom-left-radius: 0; | ||||
|         right: 0; | ||||
|     } | ||||
|  | ||||
|     button.red-ui-typedInput-option-trigger { | ||||
|         border-top-left-radius: 0px; | ||||
|         border-bottom-left-radius: 0px; | ||||
|         border-top-right-radius: 4px; | ||||
|         border-bottom-right-radius: 4px; | ||||
|         padding: 0 5px 0 0; | ||||
|         padding: 0 0 0 0; | ||||
|         position:absolute; | ||||
|         left:0; | ||||
|         top:0; | ||||
|         bottom:0; | ||||
|         right:0; | ||||
|         width: calc( 100% ); | ||||
|  | ||||
|         i { | ||||
|             position:absolute; | ||||
|             right: 4px; | ||||
|             top: 7px; | ||||
|         } | ||||
|         span { | ||||
|         .red-ui-typedInput-option-label { | ||||
|             background:#fff; | ||||
|             position:absolute; | ||||
|             left:0; | ||||
|             right:23px; | ||||
|             top: 0; | ||||
|             padding: 0 5px 0 5px; | ||||
|         } | ||||
|         .red-ui-typedInput-option-caret { | ||||
|             top: 0; | ||||
|             position: absolute; | ||||
|             right: 0; | ||||
|             width: 17px; | ||||
|         } | ||||
|         &:focus { | ||||
|             box-shadow: none; | ||||
|         } | ||||
|         &:focus .red-ui-typedInput-option-caret { | ||||
|             box-shadow: inset 0 0 0 1px $form-input-focus-color; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .red-ui-typedInput-options { | ||||
| @@ -139,12 +166,12 @@ | ||||
|         } | ||||
|         &:focus { | ||||
|             text-decoration: none; | ||||
|             background: $typedInput-button-background-active; | ||||
|             outline: none; | ||||
|         } | ||||
|         &:active { | ||||
|             background: $typedInput-button-background-active; | ||||
|             text-decoration: none; | ||||
|             background: $typedInput-button-background-active; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -87,19 +87,28 @@ | ||||
|  | ||||
| <div id="node-dialog-confirm-deploy" class="hide"> | ||||
|     <form class="form-horizontal"> | ||||
|         <div id="node-dialog-confirm-deploy-config" style="text-align: left; padding-top: 30px;" data-i18n="[prepend]deploy.confirm.improperlyConfigured;[append]deploy.confirm.confirm"> | ||||
|             <ul style="font-size: 0.9em; width: 400px; margin: 10px auto; text-align: left;" id="node-dialog-confirm-deploy-invalid-list"></ul> | ||||
|         <div id="node-dialog-confirm-deploy-config" class="node-dialog-confirm-row" data-i18n="[prepend]deploy.confirm.improperlyConfigured;[append]deploy.confirm.confirm"> | ||||
|             <ul id="node-dialog-confirm-deploy-invalid-list"></ul> | ||||
|         </div> | ||||
|         <div id="node-dialog-confirm-deploy-unknown" style="text-align: left; padding-top: 10px;" data-i18n="[prepend]deploy.confirm.unknown;[append]deploy.confirm.confirm"> | ||||
|             <ul style="font-size: 0.9em; width: 400px; margin: 10px auto; text-align: left;" id="node-dialog-confirm-deploy-unknown-list"></ul> | ||||
|         <div id="node-dialog-confirm-deploy-unknown" class="node-dialog-confirm-row" data-i18n="[prepend]deploy.confirm.unknown;[append]deploy.confirm.confirm"> | ||||
|             <ul id="node-dialog-confirm-deploy-unknown-list"></ul> | ||||
|         </div> | ||||
|         <div id="node-dialog-confirm-deploy-conflict" style="text-align: left; padding-top: 10px;" data-i18n="[prepend]deploy.confirm.conflict;[append]deploy.confirm.confirm"> | ||||
|         <div id="node-dialog-confirm-deploy-conflict" class="node-dialog-confirm-row"> | ||||
|             <div style="margin-left: 40px; margin-bottom: 10px;"> | ||||
|                 <span data-i18n="deploy.confirm.conflict"></span> | ||||
|             </div> | ||||
|             <div id="node-dialog-confirm-deploy-conflict-checking" class="node-dialog-confirm-conflict-row"> | ||||
|                 <img src="red/images/spin.svg"/><div data-i18n="deploy.confirm.conflictChecking"></div> | ||||
|             </div> | ||||
|             <div id="node-dialog-confirm-deploy-conflict-auto-merge" class="node-dialog-confirm-conflict-row"> | ||||
|                 <i style="color: #3a3;" class="fa fa-check"></i><div data-i18n="deploy.confirm.conflictAutoMerge"></div> | ||||
|             </div> | ||||
|             <div id="node-dialog-confirm-deploy-conflict-manual-merge" class="node-dialog-confirm-conflict-row"> | ||||
|                 <i style="color: #999;" class="fa fa-exclamation"></i><div data-i18n="deploy.confirm.conflictManualMerge"></div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </form> | ||||
| </div> | ||||
| <div id="node-dialog-view-diff" class="hide"> | ||||
|     <ol id="node-dialog-view-diff-diff"></ol> | ||||
| </div> | ||||
|  | ||||
| <div id="node-dialog-library-save-confirm" class="hide"> | ||||
|     <form class="form-horizontal"> | ||||
| @@ -159,10 +168,25 @@ | ||||
|     <div class="form-row form-tips" id="subflow-dialog-user-count"></div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="_expression"> | ||||
|     <div class="form-row node-text-editor-row"> | ||||
|         <div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-expression"></div> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-expression-func" data-i18n="expressionEditor.functions"></label> | ||||
|         <select id="node-input-expression-func"></select> | ||||
|         <button id="node-input-expression-func-insert" class="editor-button" data-i18n="expressionEditor.insert"></button> | ||||
|         <div style="min-height: 200px;" id="node-input-expression-help"></div> | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
|  | ||||
| <script src="vendor/vendor.js"></script> | ||||
| <script src="vendor/jsonata/jsonata.min.js"></script> | ||||
| <script src="vendor/ace/ace.js"></script> | ||||
| <script src="vendor/ace/ext-language_tools.js"></script> | ||||
| <script src="{{ asset.red }}"></script> | ||||
| <script src="{{ asset.main }}"></script> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								editor/vendor/font-awesome/fonts/FontAwesome.otf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								editor/vendor/font-awesome/fonts/FontAwesome.otf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										3350
									
								
								editor/vendor/font-awesome/fonts/fontawesome-webfont.svg
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3350
									
								
								editor/vendor/font-awesome/fonts/fontawesome-webfont.svg
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 434 KiB | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										147
									
								
								editor/vendor/jsonata/formatter.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								editor/vendor/jsonata/formatter.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| (function() { | ||||
|     function indentLine(str,length) { | ||||
|         if (length <= 0) { | ||||
|             return str; | ||||
|         } | ||||
|         var i = (new Array(length)).join(" "); | ||||
|         str = str.replace(/^\s*/,i); | ||||
|         return str; | ||||
|     } | ||||
|     function formatExpression(str) { | ||||
|         var length = str.length; | ||||
|         var start = 0; | ||||
|         var inString = false; | ||||
|         var inBox = false; | ||||
|         var quoteChar; | ||||
|         var list = []; | ||||
|         var stack = []; | ||||
|         var frame; | ||||
|         var v; | ||||
|         var matchingBrackets = { | ||||
|             "(":")", | ||||
|             "[":"]", | ||||
|             "{":"}" | ||||
|         } | ||||
|         for (var i=0;i<length;i++) { | ||||
|             var c = str[i]; | ||||
|             if (!inString) { | ||||
|                 if (c === "'" || c === '"') { | ||||
|                     inString = true; | ||||
|                     quoteChar = c; | ||||
|                     frame = {type:"string",pos:i}; | ||||
|                     list.push(frame); | ||||
|                     stack.push(frame); | ||||
|                 } else if (c === ";") { | ||||
|                     frame = {type:";",pos:i}; | ||||
|                     list.push(frame); | ||||
|                 } else if (c === ",") { | ||||
|                     frame = {type:",",pos:i}; | ||||
|                     list.push(frame); | ||||
|                 } else if (/[\(\[\{]/.test(c)) { | ||||
|                     frame = {type:"open-block",char:c,pos:i}; | ||||
|                     list.push(frame); | ||||
|                     stack.push(frame); | ||||
|                 } else if (/[\}\)\]]/.test(c)) { | ||||
|                     var oldFrame = stack.pop(); | ||||
|                     if (matchingBrackets[oldFrame.char] !== c) { | ||||
|                         //console.log("Stack frame mismatch",c,"at",i,"expected",matchingBrackets[oldFrame.char],"from",oldFrame.pos); | ||||
|                         return str; | ||||
|                     } | ||||
|                     //console.log("Closing",c,"at",i,"compare",oldFrame.type,oldFrame.pos); | ||||
|                     oldFrame.width = i-oldFrame.pos; | ||||
|                     frame = {type:"close-block",pos:i,char:c,width:oldFrame.width} | ||||
|                     list.push(frame); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (c === quoteChar) { | ||||
|                     // Next char must be a ] | ||||
|                     inString = false; | ||||
|                     stack.pop(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         // console.log(stack); | ||||
|         var result = str; | ||||
|         var indent = 0; | ||||
|         var offset = 0; | ||||
|         var pre,post,indented; | ||||
|         var longStack = []; | ||||
|         list.forEach(function(f) { | ||||
|             if (f.type === ";" || f.type === ",") { | ||||
|                 if (longStack[longStack.length-1]) { | ||||
|                     pre = result.substring(0,offset+f.pos+1); | ||||
|                     post = result.substring(offset+f.pos+1); | ||||
|                     indented = indentLine(post,indent); | ||||
|                     result = pre+"\n"+indented; | ||||
|                     offset += indented.length-post.length+1; | ||||
|                 } | ||||
|             } else if (f.type === "open-block") { | ||||
|                 if (f.width > 30) { | ||||
|                     longStack.push(true); | ||||
|                     indent += 4; | ||||
|                     pre = result.substring(0,offset+f.pos+1); | ||||
|                     post = result.substring(offset+f.pos+1); | ||||
|                     indented = indentLine(post,indent); | ||||
|                     result = pre+"\n"+indented; | ||||
|                     offset += indented.length-post.length+1; | ||||
|                 } else { | ||||
|                     longStack.push(false); | ||||
|                 } | ||||
|             } else if (f.type === "close-block") { | ||||
|                 if (f.width > 30) { | ||||
|                     indent -= 4; | ||||
|                     pre = result.substring(0,offset+f.pos); | ||||
|                     post = result.substring(offset+f.pos); | ||||
|                     indented = indentLine(post,indent); | ||||
|                     result = pre+"\n"+indented; | ||||
|                     offset += indented.length-post.length+1; | ||||
|                 } | ||||
|                 longStack.pop(); | ||||
|             } | ||||
|         }) | ||||
|         //console.log(result); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     jsonata.format = formatExpression; | ||||
|     jsonata.functions = | ||||
|     { | ||||
|         '$append':{ args:['array','array'] }, | ||||
|         '$average':{ args:['value'] }, | ||||
|         '$boolean':{ args:['value'] }, | ||||
|         '$count':{ args:['array'] }, | ||||
|         '$exists':{ args:['value'] }, | ||||
|         '$join':{ args:['array','separator'] }, | ||||
|         '$keys':{ args:['object'] }, | ||||
|         '$length':{ args:['string'] }, | ||||
|         '$lookup':{ args:['object','key'] }, | ||||
|         '$lowercase':{ args:['string'] }, | ||||
|         '$map':{ args:[] }, | ||||
|         '$max':{ args:['array'] }, | ||||
|         '$min':{ args:['array'] }, | ||||
|         '$not':{ args:['value'] }, | ||||
|         '$number':{ args:['value'] }, | ||||
|         '$reduce':{ args:[] }, | ||||
|         '$split':{ args:['string','separator','limit'] }, | ||||
|         '$spread':{ args:['object'] }, | ||||
|         '$string':{ args:['value'] }, | ||||
|         '$substring':{ args:['string','start','length'] }, | ||||
|         '$substringAfter':{ args:['string','chars'] }, | ||||
|         '$substringBefore':{ args:['string','chars'] }, | ||||
|         '$sum':{ args:['array'] }, | ||||
|         '$uppercase':{ args:['string'] } | ||||
|     } | ||||
|     jsonata.getFunctionSnippet = function(fn) { | ||||
|         var snippetText = ""; | ||||
|         if (jsonata.functions.hasOwnProperty(fn)) { | ||||
|             var def = jsonata.functions[fn]; | ||||
|             snippetText = "\\"+fn+"("; | ||||
|             if (def.args) { | ||||
|                 snippetText += def.args.map(function(a,i) { return "${"+(i+1)+":"+a+"}"}).join(", "); | ||||
|             } | ||||
|             snippetText += ")\n" | ||||
|         } | ||||
|         return snippetText; | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										134
									
								
								editor/vendor/jsonata/mode-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								editor/vendor/jsonata/mode-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules","ace/worker/worker_client","ace/mode/text"], function(require, exports, module) { | ||||
|  | ||||
|     "use strict"; | ||||
|  | ||||
|     var oop = require("../lib/oop"); | ||||
|     var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; | ||||
|  | ||||
|     var WorkerClient = require("../worker/worker_client").WorkerClient; | ||||
|     var jsonataFunctions = Object.keys(jsonata.functions); | ||||
|     // sort in length order (long->short) otherwise substringAfter gets matched | ||||
|     // as substring etc. | ||||
|     jsonataFunctions.sort(function(A,B) { | ||||
|         return B.length-A.length; | ||||
|     }); | ||||
|     jsonataFunctions = jsonataFunctions.join("|").replace(/\$/g,"\\$"); | ||||
|  | ||||
|     var JSONataHighlightRules = function() { | ||||
|  | ||||
|         var keywordMapper = this.createKeywordMapper({ | ||||
|             "keyword.operator": | ||||
|                 "and|or|in", | ||||
|             "constant.language": | ||||
|                 "null|Infinity|NaN|undefined", | ||||
|             "constant.language.boolean": | ||||
|                 "true|false", | ||||
|             "storage.type": | ||||
|                 "function" | ||||
|         }, "identifier"); | ||||
|         this.$rules = { | ||||
|             "start" : [ | ||||
|                 { | ||||
|                     token : "string", | ||||
|                     regex : "'(?=.)", | ||||
|                     next  : "qstring" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "string", | ||||
|                     regex : '"(?=.)', | ||||
|                     next  : "qqstring" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "constant.numeric", // hex | ||||
|                     regex : /0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/ | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "constant.numeric", // float | ||||
|                     regex : /[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/ | ||||
|                 }, | ||||
|                 {   token: "keyword", | ||||
|                     regex: /λ/ | ||||
|                 }, | ||||
|                 { | ||||
|                     token: "keyword", | ||||
|                     regex: jsonataFunctions | ||||
|                 }, | ||||
|                 { | ||||
|                     token : keywordMapper, | ||||
|                     regex : "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "punctuation.operator", | ||||
|                     regex : /[.](?![.])/ | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "keyword.operator", | ||||
|                     regex : /\|\||<=|>=|\.\.|\*\*|!=|:=|[=<>`!$%&*+\-~\/^]/, | ||||
|                     next  : "start" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "punctuation.operator", | ||||
|                     regex : /[?:,;.]/, | ||||
|                     next  : "start" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "paren.lparen", | ||||
|                     regex : /[\[({]/, | ||||
|                     next  : "start" | ||||
|                 }, | ||||
|                 { | ||||
|                     token : "paren.rparen", | ||||
|                     regex : /[\])}]/ | ||||
|                 } | ||||
|             ], | ||||
|             "qqstring" : [ | ||||
|                 { | ||||
|                     token : "string", | ||||
|                     regex : '"|$', | ||||
|                     next  : "start" | ||||
|                 }, { | ||||
|                     defaultToken: "string" | ||||
|                 } | ||||
|             ], | ||||
|             "qstring" : [ | ||||
|                 { | ||||
|                     token : "string", | ||||
|                     regex : "'|$", | ||||
|                     next  : "start" | ||||
|                 }, { | ||||
|                     defaultToken: "string" | ||||
|                 } | ||||
|             ] | ||||
|         }; | ||||
|     }; | ||||
|  | ||||
|     oop.inherits(JSONataHighlightRules, TextHighlightRules); | ||||
|  | ||||
|     var TextMode = require("./text").Mode; | ||||
|     var Mode = function() { | ||||
|         this.HighlightRules = JSONataHighlightRules; | ||||
|     }; | ||||
|     oop.inherits(Mode, TextMode); | ||||
|  | ||||
|  | ||||
|     (function() { | ||||
|         this.createWorker = function(session) { | ||||
|             var worker = new WorkerClient(["ace"], "ace/mode/jsonata_worker", "JSONataWorker"); | ||||
|             worker.attachToDocument(session.getDocument()); | ||||
|  | ||||
|             worker.on("annotate", function(e) { | ||||
|                 session.setAnnotations(e.data); | ||||
|             }); | ||||
|  | ||||
|             worker.on("terminate", function() { | ||||
|                 session.clearAnnotations(); | ||||
|             }); | ||||
|  | ||||
|             return worker; | ||||
|         }; | ||||
|         this.$id = "ace/mode/jsonata"; | ||||
|     }).call(Mode.prototype); | ||||
|  | ||||
|     exports.Mode = Mode; | ||||
|  | ||||
| }); | ||||
							
								
								
									
										11
									
								
								editor/vendor/jsonata/snippets-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								editor/vendor/jsonata/snippets-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| define("ace/snippets/jsonata",["require","exports","module"], function(require, exports, module) { | ||||
| "use strict"; | ||||
| var snippetText = ""; | ||||
| for (var fn in jsonata.functions) { | ||||
| 	if (jsonata.functions.hasOwnProperty(fn)) { | ||||
| 		snippetText += "# "+fn+"\nsnippet "+fn+"\n\t"+jsonata.getFunctionSnippet(fn)+"\n" | ||||
| 	} | ||||
| } | ||||
| exports.snippetText = snippetText; | ||||
| exports.scope = "jsonata"; | ||||
| }); | ||||
							
								
								
									
										4236
									
								
								editor/vendor/jsonata/worker-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4236
									
								
								editor/vendor/jsonata/worker-jsonata.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -185,7 +185,7 @@ | ||||
|                         return false; | ||||
|                     } | ||||
|                 } else if (ptype === 'flow' || ptype === 'global' ) { | ||||
|                     return /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i.test(v); | ||||
|                     return RED.utils.validatePropertyExpression(v); | ||||
|                 } else if (ptype === 'num') { | ||||
|                     return /^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v); | ||||
|                 } | ||||
|   | ||||
| @@ -41,12 +41,14 @@ | ||||
|     <p>The button to the right of the node will toggle its output on and off so you can de-clutter the debug window.</p> | ||||
|     <p>If the payload is an object or buffer it will be stringified first for display and indicate that by saying "(Object)" or "(Buffer)".</p> | ||||
|     <p>Selecting any particular message will highlight (in red) the debug node that reported it. This is useful if you wire up multiple debug nodes.</p> | ||||
|     <p>Optionally can show the complete <code>msg</code> object, and send messages to the console log.</p> | ||||
|     <p>Optionally can show the complete <code>msg</code> object, and send messages to the console log (⇶).</p> | ||||
|     <p>In addition any calls to node.warn or node.error will appear here.</p> | ||||
| </script> | ||||
| <script src="debug/view/debug-utils.js"></script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|  | ||||
| (function() { | ||||
|     var subWindow = null; | ||||
|     RED.nodes.registerType('debug',{ | ||||
|         category: 'output', | ||||
|         defaults: { | ||||
| @@ -56,10 +58,12 @@ | ||||
|             complete: {value:"false", required:true} | ||||
|         }, | ||||
|         label: function() { | ||||
|             var suffix = ""; | ||||
|             if (this.console === true || this.console === "true") { suffix = " ⇶"; } | ||||
|             if (this.complete === true || this.complete === "true") { | ||||
|                 return this.name||"msg"; | ||||
|                 return (this.name||"msg") + suffix; | ||||
|             } else { | ||||
|                 return this.name || "msg." + ((!this.complete || this.complete === "false") ? "payload" : this.complete); | ||||
|                 return (this.name || "msg." + ((!this.complete || this.complete === "false") ? "payload" : this.complete)) + suffix; | ||||
|             } | ||||
|         }, | ||||
|         labelStyle: function() { | ||||
| @@ -98,33 +102,52 @@ | ||||
|             } | ||||
|         }, | ||||
|         onpaletteadd: function() { | ||||
|             var content = $("<div>").css({"position":"relative","height":"100%"}); | ||||
|             var toolbar = $('<div class="sidebar-header">'+ | ||||
|                 '<span class="button-group">'+ | ||||
|                     '<a class="sidebar-header-button-toggle selected" id="debug-tab-filter-all" href="#"><span data-i18n="node-red:debug.sidebar.filterAll"></span></a>'+ | ||||
|                     '<a class="sidebar-header-button-toggle" id="debug-tab-filter-current" href="#"><span data-i18n="node-red:debug.sidebar.filterCurrent"></span></a> '+ | ||||
|                 '</span>'+ | ||||
|                 '<span><a id="debug-tab-clear" title="clear log" class="sidebar-header-button" href="#"><i class="fa fa-trash"></i></a></span></div>').appendTo(content); | ||||
|  | ||||
|             var messages = $('<div id="debug-content"/>').appendTo(content); | ||||
|             content.i18n(); | ||||
|             var options = { | ||||
|                 messageMouseEnter: function(sourceId) { | ||||
|                     if (sourceId) { | ||||
|                         var n = RED.nodes.node(sourceId); | ||||
|                         if (n) { | ||||
|                             n.highlighted = true; | ||||
|                             n.dirty = true; | ||||
|                         } | ||||
|                         RED.view.redraw(); | ||||
|                     } | ||||
|                 }, | ||||
|                 messageMouseLeave: function(sourceId) { | ||||
|                     if (sourceId) { | ||||
|                         var n = RED.nodes.node(sourceId); | ||||
|                         if (n) { | ||||
|                             n.highlighted = false; | ||||
|                             n.dirty = true; | ||||
|                         } | ||||
|                         RED.view.redraw(); | ||||
|                     } | ||||
|                 }, | ||||
|                 messageSourceClick: function(sourceId) { | ||||
|                     RED.view.reveal(sourceId); | ||||
|                 }, | ||||
|                 clear: function() { | ||||
|                     RED.nodes.eachNode(function(node) { | ||||
|                         node.highlighted = false; | ||||
|                         node.dirty = true; | ||||
|                     }); | ||||
|                     RED.view.redraw(); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var uiComponents = RED.debug.init(options); | ||||
|  | ||||
|             RED.sidebar.addTab({ | ||||
|                 id: "debug", | ||||
|                 label: this._("debug.sidebar.label"), | ||||
|                 name: this._("debug.sidebar.name"), | ||||
|                 content: content, | ||||
|                 content: uiComponents.content, | ||||
|                 toolbar: uiComponents.footer, | ||||
|                 enableOnEdit: true | ||||
|             }); | ||||
|             RED.actions.add("core:show-debug-tab",function() { RED.sidebar.show('debug')}); | ||||
|  | ||||
|             function getTimestamp() { | ||||
|                 var d = new Date(); | ||||
|                 return d.toLocaleString(); | ||||
|             } | ||||
|  | ||||
|             var sbc = document.getElementById("debug-content"); | ||||
|             var filter = false; | ||||
|             var messageCount = 0; | ||||
|             var that = this; | ||||
|             RED._debug = function(msg) { | ||||
|                 that.handleDebugMessage("",{ | ||||
| @@ -132,125 +155,76 @@ | ||||
|                     msg:msg | ||||
|                 }); | ||||
|             } | ||||
|             function sanitize(m) { | ||||
|                 return m.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|             } | ||||
|  | ||||
|             var refreshMessageList = function() { | ||||
|                 $(".debug-message").each(function() { | ||||
|                     $(this).toggleClass('hide',filter&&!$(this).hasClass('debug-message-flow-'+RED.workspaces.active())); | ||||
|                 }); | ||||
|             this.refreshMessageList = function() { | ||||
|                 RED.debug.refreshMessageList(RED.workspaces.active()); | ||||
|                 if (subWindow) { | ||||
|                     try { | ||||
|                         subWindow.postMessage({event:"workspaceChange",activeWorkspace:RED.workspaces.active()},"*") | ||||
|                     } catch(err) { | ||||
|                         console.log(err); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             this.handleDebugMessage = function(t,o) { | ||||
|                 var msg = document.createElement("div"); | ||||
|  | ||||
|                 var sourceNode = RED.nodes.node(o.id) || RED.nodes.node(o.z); | ||||
|                 if (sourceNode) { | ||||
|                     o._source = {id:sourceNode.id,z:sourceNode.z}; | ||||
|  | ||||
|                 msg.onmouseover = function() { | ||||
|                     msg.style.borderRightColor = "#999"; | ||||
|                     var n = RED.nodes.node(o.id) || RED.nodes.node(o.z); | ||||
|                     if (n) { | ||||
|                         n.highlighted = true; | ||||
|                         n.dirty = true; | ||||
|                     } | ||||
|                     RED.view.redraw(); | ||||
|                 }; | ||||
|                 msg.onmouseout = function() { | ||||
|                     msg.style.borderRightColor = ""; | ||||
|                     var n = RED.nodes.node(o.id) || RED.nodes.node(o.z); | ||||
|                     if (n) { | ||||
|                         n.highlighted = false; | ||||
|                         n.dirty = true; | ||||
|                     } | ||||
|                     RED.view.redraw(); | ||||
|                 }; | ||||
|                 msg.onclick = function() { | ||||
|                     var node = RED.nodes.node(o.id) || RED.nodes.node(o.z); | ||||
|                     if (node) { | ||||
|                         RED.view.reveal(node.id); | ||||
|                     } | ||||
|  | ||||
|                 }; | ||||
|                 //console.log(o); | ||||
|                 var name = sanitize(((o.name?o.name:o.id)||"").toString()); | ||||
|                 var topic = sanitize((o.topic||"").toString()); | ||||
|                 var property = sanitize(o.property?o.property:''); | ||||
|                 var payload = sanitize((o.msg||"").toString()); | ||||
|                 var format = sanitize((o.format||"").toString()); | ||||
|  | ||||
|                 msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'') + | ||||
|                                 ((sourceNode&&sourceNode.z)?((" debug-message-flow-"+sourceNode.z+((filter&&(RED.workspaces.active()!==sourceNode.z))?" hide":""))):""); | ||||
|                 msg.innerHTML = '<span class="debug-message-date">'+ | ||||
|                                 getTimestamp()+'</span>'+ | ||||
|                                 (name?'<span class="debug-message-name">'+name:'')+ | ||||
|                                 '</span>'; | ||||
|                 // NOTE: relying on function error to have a "type" that all other msgs don't | ||||
|                 if (o.hasOwnProperty("type") && (o.type === "function")) { | ||||
|                     var errorLvlType = 'error'; | ||||
|                     var errorLvl = 20; | ||||
|                     if (o.hasOwnProperty("level") && o.level === 30) { | ||||
|                         errorLvl = 30; | ||||
|                         errorLvlType = 'warn'; | ||||
|                     } | ||||
|                     msg.className = 'debug-message debug-message-level-' + errorLvl; | ||||
|                     msg.innerHTML += '<span class="debug-message-topic">function : (' + errorLvlType + ')</span>'; | ||||
|                 } else { | ||||
|                     msg.innerHTML += '<span class="debug-message-topic">'+ | ||||
|                                     (o.topic?topic+' : ':'')+ | ||||
|                                     (o.property?'msg.'+property:'msg')+" : "+format+ | ||||
|  | ||||
|                                     '</span>'; | ||||
|                 } | ||||
|                 msg.innerHTML += '<span class="debug-message-payload">'+ payload+ '</span>'; | ||||
|                 var atBottom = (sbc.scrollHeight-messages.height()-sbc.scrollTop) < 5; | ||||
|                 messageCount++; | ||||
|                 messages.append(msg); | ||||
|  | ||||
|                 if (messageCount > 200) { | ||||
|                     $("#debug-content .debug-message:first").remove(); | ||||
|                     messageCount--; | ||||
|                 } | ||||
|                 if (atBottom) { | ||||
|                     $(sbc).scrollTop(sbc.scrollHeight); | ||||
|                 RED.debug.handleDebugMessage(o); | ||||
|                 if (subWindow) { | ||||
|                     try { | ||||
|                         subWindow.postMessage({event:"message",msg:o},"*") | ||||
|                     } catch(err) { | ||||
|                         console.log(err); | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|             RED.comms.subscribe("debug",this.handleDebugMessage); | ||||
|  | ||||
|             $("#debug-tab-clear").click(function() { | ||||
|                 $(".debug-message").remove(); | ||||
|                 messageCount = 0; | ||||
|                 RED.nodes.eachNode(function(node) { | ||||
|                     node.highlighted = false; | ||||
|                     node.dirty = true; | ||||
|                 }); | ||||
|                 RED.view.redraw(); | ||||
|             }); | ||||
|             $('#debug-tab-filter-all').on("click",function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 if (filter) { | ||||
|                     $(this).addClass('selected'); | ||||
|                     $('#debug-tab-filter-current').removeClass('selected'); | ||||
|                     filter = !filter; | ||||
|                     refreshMessageList(); | ||||
|                 } | ||||
|             }); | ||||
|             $('#debug-tab-filter-current').on("click",function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 if (!filter) { | ||||
|                     $(this).addClass('selected'); | ||||
|                     $('#debug-tab-filter-all').removeClass('selected'); | ||||
|                     filter = !filter; | ||||
|                     refreshMessageList(); | ||||
|                 } | ||||
|             }); | ||||
|             RED.events.on("workspace:change", refreshMessageList); | ||||
|             RED.events.on("workspace:change", this.refreshMessageList); | ||||
|  | ||||
|             $("#debug-tab-open").click(function(e) { | ||||
|                 e.preventDefault(); | ||||
|                 subWindow = window.open(document.location.toString().replace(/[?#].*$/,"")+"debug/view/view.html"+document.location.search,"nodeREDDebugView","menubar=no,location=no,toolbar=no,chrome,height=500,width=600"); | ||||
|                 subWindow.onload = function() { | ||||
|                     subWindow.postMessage({event:"workspaceChange",activeWorkspace:RED.workspaces.active()},"*"); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             $(window).unload(function() { | ||||
|                 if (subWindow) { | ||||
|                     try { | ||||
|                         subWindow.close() | ||||
|                     } catch(err) { | ||||
|                         console.log(err); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             this.handleWindowMessage = function(evt) { | ||||
|                 var msg = evt.data; | ||||
|                 if (msg.event === "mouseEnter") { | ||||
|                     options.messageMouseEnter(msg.id); | ||||
|                 } else if (msg.event === "mouseLeave") { | ||||
|                     options.messageMouseLeave(msg.id); | ||||
|                 } else if (msg.event === "mouseClick") { | ||||
|                     options.messageSourceClick(msg.id); | ||||
|                 } else if (msg.event === "clear") { | ||||
|                     options.clear(); | ||||
|                 } | ||||
|             } | ||||
|             window.addEventListener('message',this.handleWindowMessage); | ||||
|         }, | ||||
|         onpaletteremove: function() { | ||||
|             RED.comms.unsubscribe("debug",this.handleDebugMessage); | ||||
|             RED.sidebar.removeTab("debug"); | ||||
|             RED.events.off("workspace:change", refreshMessageList); | ||||
|             RED.events.off("workspace:change", this.refreshMessageList); | ||||
|             window.removeEventListener("message",this.handleWindowMessage); | ||||
|             RED.actions.remove("core:show-debug"); | ||||
|  | ||||
|             delete RED._debug; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
| @@ -282,58 +256,5 @@ | ||||
|         } | ||||
|  | ||||
|     }); | ||||
| })(); | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
|     #debug-content { | ||||
|         position: absolute; | ||||
|         top: 43px; | ||||
|         bottom: 0px; | ||||
|         left:0px; | ||||
|         right: 0px; | ||||
|         overflow-y: scroll; | ||||
|     } | ||||
|     .debug-message { | ||||
|         cursor: pointer; | ||||
|         border-bottom: 1px solid #eee; | ||||
|         border-left: 8px solid #eee; | ||||
|         border-right: 8px solid #eee; | ||||
|         padding: 2px; | ||||
|     } | ||||
|     .debug-message-date { | ||||
|         background: #fff; | ||||
|         font-size: 9px; | ||||
|         color: #aaa; | ||||
|         padding: 1px 5px 1px 1px; | ||||
|     } | ||||
|     .debug-message-topic { | ||||
|         display: block; | ||||
|         background: #fff; | ||||
|         padding: 1px; | ||||
|         font-size: 10px; | ||||
|         color: #a66; | ||||
|     } | ||||
|     .debug-message-name { | ||||
|         background: #fff; | ||||
|         padding: 1px 5px; | ||||
|         font-size: 9px; | ||||
|         color: #aac; | ||||
|     } | ||||
|     .debug-message-payload { | ||||
|         display: block; | ||||
|         padding: 2px; | ||||
|         background: #fff; | ||||
|     } | ||||
|     .debug-message-level-log { | ||||
|         border-left-color: #eee; | ||||
|         border-right-color: #eee; | ||||
|     } | ||||
|     .debug-message-level-30 { | ||||
|         border-left-color: #ffdf9d; | ||||
|         border-right-color: #ffdf9d; | ||||
|     } | ||||
|     .debug-message-level-20 { | ||||
|         border-left-color: #f99; | ||||
|         border-right-color: #f99; | ||||
|     } | ||||
| </style> | ||||
|   | ||||
| @@ -18,6 +18,8 @@ module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var util = require("util"); | ||||
|     var events = require("events"); | ||||
|     var path = require("path"); | ||||
|     var safeJSONStringify = require("json-stringify-safe"); | ||||
|     var debuglength = RED.settings.debugMaxLength||1000; | ||||
|     var useColors = false; | ||||
|     // util.inspect.styles.boolean = "red"; | ||||
| @@ -79,30 +81,62 @@ module.exports = function(RED) { | ||||
|             msg.format = "error"; | ||||
|             msg.msg = msg.msg.toString(); | ||||
|         } else if (msg.msg instanceof Buffer) { | ||||
|             msg.format = "buffer ["+msg.msg.length+"]"; | ||||
|             msg.format = "buffer["+msg.msg.length+"]"; | ||||
|             msg.msg = msg.msg.toString('hex'); | ||||
|             if (msg.msg.length > debuglength) { | ||||
|                 msg.msg = msg.msg.substring(0,debuglength); | ||||
|             } | ||||
|         } else if (msg.msg && typeof msg.msg === 'object') { | ||||
|             var seen = []; | ||||
|             var seenAts = []; | ||||
|             try { | ||||
|                 msg.format = msg.msg.constructor.name || "Object"; | ||||
|             } catch(err) { | ||||
|                 msg.format = "Object"; | ||||
|             } | ||||
|             var isArray = util.isArray(msg.msg); | ||||
|             if (isArray) { | ||||
|                 msg.format = "array ["+msg.msg.length+"]"; | ||||
|             } | ||||
|             if (isArray || (msg.format === "Object")) { | ||||
|                 msg.msg = JSON.stringify(msg.msg, function(key, value) { | ||||
|                     if (typeof value === 'object' && value !== null) { | ||||
|                         if (seen.indexOf(value) !== -1) { return "[circular]"; } | ||||
|                         seen.push(value); | ||||
|                     } | ||||
|                     return value; | ||||
|                 }," "); | ||||
|             if (/error/i.test(msg.format)) { | ||||
|                 msg.msg = JSON.stringify({ | ||||
|                     name: msg.msg.name, | ||||
|                     message: msg.msg.message | ||||
|                 }); | ||||
|             } else { | ||||
|                 try { msg.msg = msg.msg.toString(); } | ||||
|                 catch(e) { msg.msg = "[Type not printable]"; } | ||||
|                 var isArray = util.isArray(msg.msg); | ||||
|                 if (isArray) { | ||||
|                     msg.format = "array["+msg.msg.length+"]"; | ||||
|                     if (msg.msg.length > debuglength) { | ||||
|                         msg.msg = msg.msg.slice(0,debuglength); | ||||
|                     } | ||||
|                 } | ||||
|                 if (isArray || (msg.format === "Object")) { | ||||
|                     msg.msg = safeJSONStringify(msg.msg, function(key, value) { | ||||
|                         if (key[0] === '_' && key !== "_msgid") { | ||||
|                             return undefined; | ||||
|                         } | ||||
|                         if (key === '_req' || key === '_res') { | ||||
|                             return "[internal]" | ||||
|                         } | ||||
|                         if (value instanceof Error) { | ||||
|                             return value.toString() | ||||
|                         } | ||||
|                         if (util.isArray(value) && value.length > debuglength) { | ||||
|                             value = { | ||||
|                                 __encoded__: true, | ||||
|                                 type: "array", | ||||
|                                 data: value.slice(0,debuglength), | ||||
|                                 length: value.length | ||||
|                             } | ||||
|                         } | ||||
|                         if (typeof value === 'string') { | ||||
|                             if (value.length > debuglength) { | ||||
|                                 return value.substring(0,debuglength)+"..."; | ||||
|                             } | ||||
|                         } | ||||
|                         return value; | ||||
|                     }," "); | ||||
|                 } else { | ||||
|                     try { msg.msg = msg.msg.toString(); } | ||||
|                     catch(e) { msg.msg = "[Type not printable]"; } | ||||
|                 } | ||||
|             } | ||||
|             seen = null; | ||||
|         } else if (typeof msg.msg === "boolean") { | ||||
| @@ -118,13 +152,14 @@ module.exports = function(RED) { | ||||
|             msg.format = (msg.msg === null)?"null":"undefined"; | ||||
|             msg.msg = "(undefined)"; | ||||
|         } else { | ||||
|             msg.format = "string ["+msg.msg.length+"]"; | ||||
|             msg.msg = msg.msg; | ||||
|         } | ||||
|  | ||||
|         if (msg.msg.length > debuglength) { | ||||
|             msg.msg = msg.msg.substr(0,debuglength) +" ...."; | ||||
|             msg.format = "string["+msg.msg.length+"]"; | ||||
|             if (msg.msg.length > debuglength) { | ||||
|                 msg.msg = msg.msg.substring(0,debuglength)+"..."; | ||||
|             } | ||||
|         } | ||||
|         // if (msg.msg.length > debuglength) { | ||||
|         //     msg.msg = msg.msg.substr(0,debuglength) +" ...."; | ||||
|         // } | ||||
|         RED.comms.publish("debug",msg); | ||||
|     } | ||||
|  | ||||
| @@ -153,4 +188,12 @@ module.exports = function(RED) { | ||||
|             res.sendStatus(404); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     RED.httpAdmin.get("/debug/view/*",RED.auth.needsPermission("debug.read"),function(req,res) { | ||||
|         var options = { | ||||
|             root: __dirname + '/lib/debug/', | ||||
|             dotfiles: 'deny' | ||||
|         }; | ||||
|         res.sendFile(req.params[0], options); | ||||
|     }); | ||||
| }; | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|     (on the 3rd output).</p> | ||||
|     <p>The optional append gets added to the command after <code>msg.payload</code> - so you can do | ||||
|     things like pipe the result to another command.</p> | ||||
|     <p>Parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p> | ||||
|     <p>Commands or parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p> | ||||
|     <p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p> | ||||
|     <p>The blue status icon will be visible while the node is active.</p> | ||||
|     <p>If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p> | ||||
|   | ||||
| @@ -49,6 +49,7 @@ module.exports = function(RED) { | ||||
|                 // slice whole line by spaces (trying to honour quotes); | ||||
|                 arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g); | ||||
|                 var cmd = arg.shift(); | ||||
|                 if (/^".*"$/.test(cmd)) { cmd = cmd.slice(1,-1); } | ||||
|                 /* istanbul ignore else  */ | ||||
|                 if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); } | ||||
|                 child = spawn(cmd,arg); | ||||
|   | ||||
| @@ -36,6 +36,7 @@ | ||||
|                 <option value="javascript">Javascript</option> | ||||
|                 <option value="css">CSS</option> | ||||
|                 <option value="markdown">Markdown</option> | ||||
|                 <option value="yaml">YAML</option> | ||||
|                 <option value="text">none</option> | ||||
|             </select> | ||||
|         </div> | ||||
| @@ -66,6 +67,7 @@ | ||||
| }</pre> | ||||
|     <p>The resulting property will be: | ||||
|     <pre>Hello Fred. Today is Monday</pre> | ||||
|     <p>It is possible to use property from flow context or global context. Just use <code>{{flow.name}}</code> or <code>{{global.name}}</code>. | ||||
|     <p>By default, mustache will escape any HTML entities in the values it substitutes. | ||||
|        To prevent this, use <code>{{{triple}}}</code> braces. | ||||
| </script> | ||||
|   | ||||
| @@ -18,6 +18,39 @@ module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var mustache = require("mustache"); | ||||
|  | ||||
|     /** | ||||
|      * Custom Mustache Context capable to resolve message property and node  | ||||
|      * flow and global context  | ||||
|      */ | ||||
|     function NodeContext(msg, nodeContext) { | ||||
|         this.msgContext = new mustache.Context(msg); | ||||
|         this.nodeContext = nodeContext; | ||||
|     } | ||||
|  | ||||
|     NodeContext.prototype = new mustache.Context(); | ||||
|  | ||||
|     NodeContext.prototype.lookup = function (name) { | ||||
|         // try message first: | ||||
|         var value = this.msgContext.lookup(name); | ||||
|         if (value !== undefined) { | ||||
|             return value; | ||||
|         } | ||||
|  | ||||
|         // try node context: | ||||
|         var dot = name.indexOf("."); | ||||
|         if (dot > 0) { | ||||
|             var contextName = name.substr(0, dot); | ||||
|             var variableName = name.substr(dot + 1); | ||||
|  | ||||
|             if (contextName === "flow" && this.nodeContext.flow) { | ||||
|                 return this.nodeContext.flow.get(variableName); | ||||
|             } | ||||
|             else if (contextName === "global" && this.nodeContext.global) { | ||||
|                 return this.nodeContext.global.get(variableName); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function TemplateNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.name = n.name; | ||||
| @@ -31,7 +64,7 @@ module.exports = function(RED) { | ||||
|             try { | ||||
|                 var value; | ||||
|                 if (node.syntax === "mustache") { | ||||
|                     value = mustache.render(node.template,msg); | ||||
|                     value = mustache.render(node.template, new NodeContext(msg, node.context())); | ||||
|                 } else { | ||||
|                     value = node.template; | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										285
									
								
								nodes/core/core/lib/debug/debug-utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								nodes/core/core/lib/debug/debug-utils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| /** | ||||
|  * Copyright 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| if (!RED) { | ||||
|     var RED = {} | ||||
| } | ||||
| RED.debug = (function() { | ||||
|     var config; | ||||
|     var messageList; | ||||
|     var messageTable; | ||||
|     var filter = false; | ||||
|     var view = 'list'; | ||||
|     var messages = []; | ||||
|     var messagesByNode = {}; | ||||
|     var sbc; | ||||
|     var activeWorkspace; | ||||
|  | ||||
|     function init(_config) { | ||||
|         config = _config; | ||||
|  | ||||
|         var content = $("<div>").css({"position":"relative","height":"100%"}); | ||||
|         var toolbar = $('<div class="sidebar-header">'+ | ||||
|             '<span class="button-group"><a id="debug-tab-filter" class="sidebar-header-button" href="#"><i class="fa fa-filter"></i></a></span>'+ | ||||
|             '<span class="button-group"><a id="debug-tab-clear" title="clear log" class="sidebar-header-button" href="#"><i class="fa fa-trash"></i></a></span></div>').appendTo(content); | ||||
|  | ||||
|  | ||||
|         var footerToolbar = $('<div>'+ | ||||
|             // '<span class="button-group">'+ | ||||
|             //     '<a class="sidebar-footer-button-toggle text-button selected" id="debug-tab-view-list" href="#"><span data-i18n="">list</span></a>'+ | ||||
|             //     '<a class="sidebar-footer-button-toggle text-button" id="debug-tab-view-table" href="#"><span data-i18n="">table</span></a> '+ | ||||
|             // '</span>'+ | ||||
|             '<span class="button-group"><a id="debug-tab-open" title="open in new window" class="sidebar-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' + | ||||
|             '</div>'); | ||||
|  | ||||
|         messageList = $('<div class="debug-content debug-content-list"/>').appendTo(content); | ||||
|         sbc = messageList[0]; | ||||
|         messageTable = $('<div class="debug-content  debug-content-table hide"/>').appendTo(content); | ||||
|  | ||||
|         var filterDialog = $('<div class="debug-filter-box hide">'+ | ||||
|             '<div class="debug-filter-row">'+ | ||||
|             '<span class="button-group">'+ | ||||
|                 '<a class="sidebar-header-button-toggle selected" id="debug-tab-filter-all" href="#"><span data-i18n="node-red:debug.sidebar.filterAll">all flows</span></a>'+ | ||||
|                 '<a class="sidebar-header-button-toggle" id="debug-tab-filter-current" href="#"><span data-i18n="node-red:debug.sidebar.filterCurrent">current flow</span></a> '+ | ||||
|             '</span>'+ | ||||
|             '</div>'+ | ||||
|         '</div>').appendTo(content); | ||||
|  | ||||
|         try { | ||||
|             content.i18n(); | ||||
|         } catch(err) { | ||||
|             console.log("TODO: i18n library support"); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         filterDialog.find('#debug-tab-filter-all').on("click",function(e) { | ||||
|             e.preventDefault(); | ||||
|             if (filter) { | ||||
|                 $(this).addClass('selected'); | ||||
|                 $('#debug-tab-filter-current').removeClass('selected'); | ||||
|                 filter = !filter; | ||||
|                 refreshMessageList(); | ||||
|             } | ||||
|         }); | ||||
|         filterDialog.find('#debug-tab-filter-current').on("click",function(e) { | ||||
|             e.preventDefault(); | ||||
|             if (!filter) { | ||||
|                 $(this).addClass('selected'); | ||||
|                 $('#debug-tab-filter-all').removeClass('selected'); | ||||
|                 filter = !filter; | ||||
|                 refreshMessageList(); | ||||
|             } | ||||
|         }); | ||||
|         // $('#debug-tab-view-list').on("click",function(e) { | ||||
|         //     e.preventDefault(); | ||||
|         //     if (!$(this).hasClass('selected')) { | ||||
|         //         $(this).addClass('selected'); | ||||
|         //         $('#debug-tab-view-table').removeClass('selected'); | ||||
|         //         showMessageList(); | ||||
|         //     } | ||||
|         // }); | ||||
|         // $('#debug-tab-view-table').on("click",function(e) { | ||||
|         //     e.preventDefault(); | ||||
|         //     if (!$(this).hasClass('selected')) { | ||||
|         //         $(this).addClass('selected'); | ||||
|         //         $('#debug-tab-view-list').removeClass('selected'); | ||||
|         //         showMessageTable(); | ||||
|         //     } | ||||
|         // }); | ||||
|  | ||||
|  | ||||
|         toolbar.find('#debug-tab-filter').on("click",function(e) { | ||||
|             e.preventDefault(); | ||||
|             if ($(this).hasClass('selected')) { | ||||
|                 $(this).removeClass('selected'); | ||||
|                 filterDialog.slideUp(200); | ||||
|             } else { | ||||
|                 $(this).addClass('selected'); | ||||
|                 filterDialog.slideDown(200); | ||||
|             } | ||||
|         }) | ||||
|  | ||||
|         toolbar.find("#debug-tab-clear").click(function(e) { | ||||
|             e.preventDefault(); | ||||
|             $(".debug-message").remove(); | ||||
|             messageCount = 0; | ||||
|             config.clear(); | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         return { | ||||
|             content: content, | ||||
|             footer: footerToolbar | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function getTimestamp() { | ||||
|         var d = new Date(); | ||||
|         return d.toLocaleString(); | ||||
|     } | ||||
|  | ||||
|     function sanitize(m) { | ||||
|         return m.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | ||||
|     } | ||||
|  | ||||
|     function refreshMessageList(_activeWorkspace) { | ||||
|         if (_activeWorkspace) { | ||||
|             activeWorkspace = _activeWorkspace; | ||||
|         } | ||||
|         $(".debug-message").each(function() { | ||||
|             $(this).toggleClass('hide',filter&&!$(this).hasClass('debug-message-flow-'+activeWorkspace)); | ||||
|         }); | ||||
|     } | ||||
|     function refreshMessageTable() { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function showMessageList() { | ||||
|         view = 'list'; | ||||
|         messageTable.hide(); | ||||
|         messageTable.empty(); | ||||
|  | ||||
|         messages.forEach(function(m) { | ||||
|             messageList.append(m.el); | ||||
|         }) | ||||
|         messageList.show(); | ||||
|     } | ||||
|     function showMessageTable() { | ||||
|         view = 'table'; | ||||
|         messageList.hide(); | ||||
|         messageList.empty(); | ||||
|  | ||||
|         Object.keys(messagesByNode).forEach(function(id) { | ||||
|             var m = messagesByNode[id]; | ||||
|             var msg = m.el; | ||||
|             var sourceNode = m.source; | ||||
|             if (sourceNode) { | ||||
|                 var wrapper = $("<div>",{id:"debug-message-source-"+sourceNode.id.replace(/\./g,"_")}).appendTo(messageTable); | ||||
|                 wrapper.append(msg); | ||||
|             } | ||||
|         }); | ||||
|         messageTable.show(); | ||||
|     } | ||||
|     function formatString(str) { | ||||
|         return str.replace(/\n/g,"↵").replace(/\t/g,"→"); | ||||
|     } | ||||
|  | ||||
|     function handleDebugMessage(o) { | ||||
|         var msg = document.createElement("div"); | ||||
|  | ||||
|         var sourceNode = o._source; | ||||
|  | ||||
|         msg.onmouseenter = function() { | ||||
|             msg.style.borderRightColor = "#999"; | ||||
|             if (o._source) { | ||||
|                 config.messageMouseEnter(o._source.id); | ||||
|             } | ||||
|         }; | ||||
|         msg.onmouseleave = function() { | ||||
|             msg.style.borderRightColor = ""; | ||||
|             if (o._source) { | ||||
|                 config.messageMouseLeave(o._source.id); | ||||
|             } | ||||
|         }; | ||||
|         var name = sanitize(((o.name?o.name:o.id)||"").toString()); | ||||
|         var topic = sanitize((o.topic||"").toString()); | ||||
|         var property = sanitize(o.property?o.property:''); | ||||
|         var payload = o.msg; | ||||
|         var format = sanitize((o.format||"").toString()); | ||||
|         msg.className = 'debug-message'+(o.level?(' debug-message-level-'+o.level):'') + | ||||
|         ((sourceNode&&sourceNode.z)?((" debug-message-flow-"+sourceNode.z+((filter&&(activeWorkspace!==sourceNode.z))?" hide":""))):""); | ||||
|         var metaRow = $('<div class="debug-message-meta"></div>').appendTo(msg); | ||||
|         $('<span class="debug-message-date">'+ getTimestamp()+'</span>').appendTo(metaRow); | ||||
|         if (sourceNode) { | ||||
|             $('<a>',{href:"#",class:"debug-message-name"}).html('node: '+sourceNode.id) | ||||
|             .appendTo(metaRow) | ||||
|             .click(function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 config.messageSourceClick(sourceNode.id); | ||||
|             }); | ||||
|         } else if (name) { | ||||
|             $('<span class="debug-message-name">'+name+'</span>').appendTo(metaRow); | ||||
|         } | ||||
|         // NOTE: relying on function error to have a "type" that all other msgs don't | ||||
|         if (o.hasOwnProperty("type") && (o.type === "function")) { | ||||
|             var errorLvlType = 'error'; | ||||
|             var errorLvl = 20; | ||||
|             if (o.hasOwnProperty("level") && o.level === 30) { | ||||
|                 errorLvl = 30; | ||||
|                 errorLvlType = 'warn'; | ||||
|             } | ||||
|             $(msg).addClass('debug-message-level-' + errorLvl); | ||||
|             $('<span class="debug-message-topic">function : (' + errorLvlType + ')</span>').appendTo(metaRow); | ||||
|         } else { | ||||
|             $('<span class="debug-message-topic">'+ | ||||
|                 (o.topic?topic+' : ':'')+ | ||||
|                 (o.property?'msg.'+property:'msg')+" : "+format+ | ||||
|                 '</span>').appendTo(metaRow); | ||||
|         } | ||||
|         if (format === 'Object' || /^array/.test(format) || format === 'boolean' || format === 'number'||/error/i.test(format) ) { | ||||
|             payload = JSON.parse(payload); | ||||
|         } else if (format === 'null') { | ||||
|             payload = null; | ||||
|         } else if (format === 'undefined') { | ||||
|             payload = undefined; | ||||
|         } else if (/^buffer/.test(format)) { | ||||
|             var buffer = payload; | ||||
|             payload = []; | ||||
|             for (var c = 0; c < buffer.length; c += 2) { | ||||
|                 payload.push(parseInt(buffer.substr(c, 2), 16)); | ||||
|             } | ||||
|         } | ||||
|         var el = $('<span class="debug-message-payload"></span>').appendTo(msg); | ||||
|         RED.utils.createObjectElement(payload,/*true*/null,format).appendTo(el); | ||||
|         var atBottom = (sbc.scrollHeight-messageList.height()-sbc.scrollTop) < 5; | ||||
|         var m = { | ||||
|             el: msg | ||||
|         }; | ||||
|         messages.push(m); | ||||
|         if (sourceNode) { | ||||
|             m.source = sourceNode; | ||||
|             messagesByNode[sourceNode.id] = m; | ||||
|         } | ||||
|         if (view == "list") { | ||||
|             messageList.append(msg); | ||||
|         } else { | ||||
|             if (sourceNode) { | ||||
|                 var wrapper = $("#debug-message-source-"+sourceNode.id.replace(/\./g,"_")); | ||||
|                 if (wrapper.length === 0 ) { | ||||
|                     wrapper = $("<div>",{id:"debug-message-source-"+sourceNode.id.replace(/\./g,"_")}).appendTo(messageTable); | ||||
|                 } | ||||
|                 wrapper.empty(); | ||||
|                 wrapper.append(msg); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (messages.length === 100) { | ||||
|             m = messages.shift(); | ||||
|             if (view === "list") { | ||||
|                 m.el.remove(); | ||||
|             } | ||||
|         } | ||||
|         if (atBottom) { | ||||
|             messageList.scrollTop(sbc.scrollHeight); | ||||
|         } | ||||
|     } | ||||
|     return { | ||||
|         init: init, | ||||
|         refreshMessageList:refreshMessageList, | ||||
|         handleDebugMessage: handleDebugMessage | ||||
|     } | ||||
| })(); | ||||
							
								
								
									
										28
									
								
								nodes/core/core/lib/debug/debug.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								nodes/core/core/lib/debug/debug.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| $(function() { | ||||
|     var options = { | ||||
|         messageMouseEnter: function(sourceId) { | ||||
|             window.opener.postMessage({event:"mouseEnter",id:sourceId},'*'); | ||||
|         }, | ||||
|         messageMouseLeave: function(sourceId) { | ||||
|             window.opener.postMessage({event:"mouseLeave",id:sourceId},'*'); | ||||
|         }, | ||||
|         messageSourceClick: function(sourceId) { | ||||
|             window.opener.postMessage({event:"mouseClick",id:sourceId},'*'); | ||||
|         }, | ||||
|         clear: function() { | ||||
|             window.opener.postMessage({event:"clear"},'*'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     var uiComponents = RED.debug.init(options); | ||||
|  | ||||
|     $(".debug-window").append(uiComponents.content); | ||||
|  | ||||
|     window.addEventListener('message',function(evt) { | ||||
|         if (evt.data.event === "message") { | ||||
|             RED.debug.handleDebugMessage(evt.data.msg); | ||||
|         } else if (evt.data.event === "workspaceChange") { | ||||
|             RED.debug.refreshMessageList(evt.data.activeWorkspace); | ||||
|         } | ||||
|     },false); | ||||
| }); | ||||
							
								
								
									
										13
									
								
								nodes/core/core/lib/debug/view.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								nodes/core/core/lib/debug/view.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <html> | ||||
| <head> | ||||
|     <link rel="stylesheet" href="../../red/style.min.css"> | ||||
|     <link rel="stylesheet" href="../../vendor/font-awesome/css/font-awesome.min.css"> | ||||
|     <title>Node-RED Debug Tools</title> | ||||
| </head> | ||||
| <body class="debug-window"> | ||||
| </body> | ||||
| <script src="../../vendor/vendor.js"></script> | ||||
| <script src="../../red/red.js"></script> | ||||
| <script src="debug-utils.js"></script> | ||||
| <script src="debug.js"></script> | ||||
| </html> | ||||
| @@ -90,6 +90,7 @@ | ||||
|         <li><code>payload</code> is the body of the response</li> | ||||
|         <li><code>statusCode</code> is the status code of the response, or the error code if the request could not be completed</li> | ||||
|         <li><code>headers</code> is an object containing the response headers</li> | ||||
|         <li><code>responseUrl</code> is the url of the server that responds</li> | ||||
|     </ul> | ||||
|     <p><b>Note</b>: If you need to configure a proxy please add <b>http_proxy=...</b> to your environment variables and restart Node-RED.</p> | ||||
| </script> | ||||
|   | ||||
| @@ -159,6 +159,7 @@ module.exports = function(RED) { | ||||
|                 (node.ret === "bin") ? res.setEncoding('binary') : res.setEncoding('utf8'); | ||||
|                 msg.statusCode = res.statusCode; | ||||
|                 msg.headers = res.headers; | ||||
|                 msg.responseUrl = res.responseUrl; | ||||
|                 msg.payload = ""; | ||||
|                 // msg.url = url;   // revert when warning above finally removed | ||||
|                 res.on('data',function(chunk) { | ||||
|   | ||||
| @@ -502,6 +502,9 @@ | ||||
|             "null":"is null", | ||||
|             "nnull":"is not null", | ||||
|             "else":"otherwise" | ||||
|         }, | ||||
|         "errors": { | ||||
|             "invalid-expr": "Invalid expression: __error__" | ||||
|         } | ||||
|     }, | ||||
|     "change": { | ||||
| @@ -614,6 +617,13 @@ | ||||
|             "dropped-error": "Failed to convert payload" | ||||
|         } | ||||
|     }, | ||||
|     "yaml": { | ||||
|         "errors": { | ||||
|             "dropped-object": "Ignored non-object payload", | ||||
|             "dropped": "Ignored unsupported payload type", | ||||
|             "dropped-error": "Failed to convert payload" | ||||
|         } | ||||
|     }, | ||||
|     "xml": { | ||||
|         "label": { | ||||
|             "represent": "Represent XML tag attributes as a property named", | ||||
|   | ||||
| @@ -64,7 +64,7 @@ | ||||
|             var node = this; | ||||
|             var previousValueType = {value:"prev",label:this._("inject.previous"),hasValue:false}; | ||||
|  | ||||
|             $("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global']}); | ||||
|             $("#node-input-property").typedInput({default:this.propertyType||'msg',types:['msg','flow','global','jsonata']}); | ||||
|             var operators = [ | ||||
|                 {v:"eq",t:"=="}, | ||||
|                 {v:"neq",t:"!="}, | ||||
| @@ -115,7 +115,10 @@ | ||||
|  | ||||
|             $("#node-input-rule-container").css('min-height','250px').css('min-width','450px').editableList({ | ||||
|                 addItem: function(container,i,opt) { | ||||
|                     var rule = opt; | ||||
|                     if (!opt.hasOwnProperty('r')) { | ||||
|                         opt.r = {}; | ||||
|                     } | ||||
|                     var rule = opt.r; | ||||
|                     if (!rule.hasOwnProperty('t')) { | ||||
|                         rule.t = 'eq'; | ||||
|                     } | ||||
| @@ -126,10 +129,10 @@ | ||||
|                     for (var d in operators) { | ||||
|                         selectField.append($("<option></option>").val(operators[d].v).text(operators[d].t)); | ||||
|                     } | ||||
|                     var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num',previousValueType]}); | ||||
|                     var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num',previousValueType]}); | ||||
|                     var valueField = $('<input/>',{class:"node-input-rule-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'str',types:['msg','flow','global','str','num','jsonata',previousValueType]}); | ||||
|                     var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]}); | ||||
|                     var btwnAndLabel = $('<span/>',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3); | ||||
|                     var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num',previousValueType]}); | ||||
|                     var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]}); | ||||
|                     var finalspan = $('<span/>',{style:"float: right;margin-top: 6px;"}).appendTo(row); | ||||
|                     finalspan.append(' → <span class="node-input-rule-index">'+(i+1)+'</span> '); | ||||
|                     var caseSensitive = $('<input/>',{id:"node-input-rule-case-"+i,class:"node-input-rule-case",type:"checkbox",style:"width:auto;vertical-align:top"}).appendTo(row2); | ||||
| @@ -177,6 +180,12 @@ | ||||
|                     selectField.change(); | ||||
|                 }, | ||||
|                 removeItem: function(opt) { | ||||
|                     if (opt.hasOwnProperty('i')) { | ||||
|                         var removedList = $("#node-input-rule-container").data('removedList')||[]; | ||||
|                         removedList.push(opt.i); | ||||
|                         $("#node-input-rule-container").data('removedList',removedList); | ||||
|                     } | ||||
|  | ||||
|                     var rules = $("#node-input-rule-container").editableList('items'); | ||||
|                     rules.each(function(i) { $(this).find(".node-input-rule-index").html(i+1); }); | ||||
|                 }, | ||||
| @@ -191,15 +200,21 @@ | ||||
|  | ||||
|             for (var i=0;i<this.rules.length;i++) { | ||||
|                 var rule = this.rules[i]; | ||||
|                 $("#node-input-rule-container").editableList('addItem',rule); | ||||
|                 $("#node-input-rule-container").editableList('addItem',{r:rule,i:i}); | ||||
|             } | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             var rules = $("#node-input-rule-container").editableList('items'); | ||||
|             var ruleset; | ||||
|             var node = this; | ||||
|             node.rules= []; | ||||
|             node.rules = []; | ||||
|             var changedOutputs = {}; | ||||
|             var removedList = $("#node-input-rule-container").data('removedList')||[]; | ||||
|             removedList.forEach(function(i) { | ||||
|                 changedOutputs[i] = -1; | ||||
|             }); | ||||
|             rules.each(function(i) { | ||||
|                 var ruleData = $(this).data('data'); | ||||
|                 var rule = $(this); | ||||
|                 var type = rule.find("select").val(); | ||||
|                 var r = {t:type}; | ||||
| @@ -217,8 +232,14 @@ | ||||
|                         r.case = rule.find(".node-input-rule-case").prop("checked"); | ||||
|                     } | ||||
|                 } | ||||
|                 if (ruleData.hasOwnProperty('i')) { | ||||
|                     if (ruleData.i !== i) { | ||||
|                         changedOutputs[ruleData.i] = i; | ||||
|                     } | ||||
|                 } | ||||
|                 node.rules.push(r); | ||||
|             }); | ||||
|             this._outputs = changedOutputs; | ||||
|             this.outputs = node.rules.length; | ||||
|             this.propertyType = $("#node-input-property").typedInput('type'); | ||||
|         }, | ||||
|   | ||||
| @@ -16,6 +16,9 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|  | ||||
|     var jsonata = require('jsonata'); | ||||
|  | ||||
|     var operators = { | ||||
|         'eq': function(a, b) { return a == b; }, | ||||
|         'neq': function(a, b) { return a != b; }, | ||||
| @@ -38,9 +41,20 @@ module.exports = function(RED) { | ||||
|         this.rules = n.rules || []; | ||||
|         this.property = n.property; | ||||
|         this.propertyType = n.propertyType || "msg"; | ||||
|  | ||||
|         if (this.propertyType === 'jsonata') { | ||||
|             try { | ||||
|                 this.property = jsonata(this.property); | ||||
|             } catch(err) { | ||||
|                 this.error(RED._("switch.errors.invalid-expr",{error:err.message})); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.checkall = n.checkall || "true"; | ||||
|         this.previousValue = null; | ||||
|         var node = this; | ||||
|         var valid = true; | ||||
|         for (var i=0; i<this.rules.length; i+=1) { | ||||
|             var rule = this.rules[i]; | ||||
|             if (!rule.vt) { | ||||
| @@ -54,6 +68,13 @@ module.exports = function(RED) { | ||||
|                 if (!isNaN(Number(rule.v))) { | ||||
|                     rule.v = Number(rule.v); | ||||
|                 } | ||||
|             } else if (rule.vt === "jsonata") { | ||||
|                 try { | ||||
|                     rule.v = jsonata(rule.v); | ||||
|                 } catch(err) { | ||||
|                     this.error(RED._("switch.errors.invalid-expr",{error:err.message})); | ||||
|                     valid = false; | ||||
|                 } | ||||
|             } | ||||
|             if (typeof rule.v2 !== 'undefined') { | ||||
|                 if (!rule.v2t) { | ||||
| @@ -65,14 +86,30 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 if (rule.v2t === 'num') { | ||||
|                     rule.v2 = Number(rule.v2); | ||||
|                 } else if (rule.v2t === 'jsonata') { | ||||
|                     try { | ||||
|                         rule.v2 = jsonata(rule.v2); | ||||
|                     } catch(err) { | ||||
|                         this.error(RED._("switch.errors.invalid-expr",{error:err.message})); | ||||
|                         valid = false; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!valid) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this.on('input', function (msg) { | ||||
|             var onward = []; | ||||
|             try { | ||||
|                 var prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg); | ||||
|                 var prop; | ||||
|                 if (node.propertyType === 'jsonata') { | ||||
|                     prop = node.property.evaluate({msg:msg}); | ||||
|                 } else { | ||||
|                     prop = RED.util.evaluateNodeProperty(node.property,node.propertyType,node,msg); | ||||
|                 } | ||||
|                 var elseflag = true; | ||||
|                 for (var i=0; i<node.rules.length; i+=1) { | ||||
|                     var rule = node.rules[i]; | ||||
| @@ -80,12 +117,26 @@ module.exports = function(RED) { | ||||
|                     var v1,v2; | ||||
|                     if (rule.vt === 'prev') { | ||||
|                         v1 = node.previousValue; | ||||
|                     } else if (rule.vt === 'jsonata') { | ||||
|                         try { | ||||
|                             v1 = rule.v.evaluate({msg:msg}); | ||||
|                         } catch(err) { | ||||
|                             node.error(RED._("switch.errors.invalid-expr",{error:err.message})); | ||||
|                             return; | ||||
|                         } | ||||
|                     } else { | ||||
|                         v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg); | ||||
|                     } | ||||
|                     v2 = rule.v2; | ||||
|                     if (rule.v2t === 'prev') { | ||||
|                         v2 = node.previousValue; | ||||
|                     } else if (rule.v2t === 'jsonata') { | ||||
|                         try { | ||||
|                             v2 = rule.v2.evaluate({msg:msg}); | ||||
|                         } catch(err) { | ||||
|                             node.error(RED._("switch.errors.invalid-expr",{error:err.message})); | ||||
|                             return; | ||||
|                         } | ||||
|                     } else if (typeof v2 !== 'undefined') { | ||||
|                         v2 = RED.util.evaluateNodeProperty(rule.v2,rule.v2t,node,msg); | ||||
|                     } | ||||
|   | ||||
| @@ -141,22 +141,22 @@ | ||||
|                         selectField.append($("<option></option>").val(selectOptions[i].v).text(selectOptions[i].l)); | ||||
|                     } | ||||
|  | ||||
|                     var propertyName = $('<input/>',{style:"width:250px",class:"node-input-rule-property-name",type:"text"}) | ||||
|                     var propertyName = $('<input/>',{class:"node-input-rule-property-name",type:"text"}) | ||||
|                         .appendTo(row1) | ||||
|                         .typedInput({types:['msg','flow','global']}); | ||||
|  | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(to) | ||||
|                         .appendTo(row2); | ||||
|                     var propertyValue = $('<input/>',{style:"width:250px",class:"node-input-rule-property-value",type:"text"}) | ||||
|                     var propertyValue = $('<input/>',{class:"node-input-rule-property-value",type:"text"}) | ||||
|                         .appendTo(row2) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','date']}); | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json','date','jsonata']}); | ||||
|  | ||||
|                     var row3_1 = $('<div/>').appendTo(row3); | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(search) | ||||
|                         .appendTo(row3_1); | ||||
|                     var fromValue = $('<input/>',{style:"width:250px",class:"node-input-rule-property-search-value",type:"text"}) | ||||
|                     var fromValue = $('<input/>',{class:"node-input-rule-property-search-value",type:"text"}) | ||||
|                         .appendTo(row3_1) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','re','num','bool']}); | ||||
|  | ||||
| @@ -164,14 +164,14 @@ | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(replace) | ||||
|                         .appendTo(row3_2); | ||||
|                     var toValue = $('<input/>',{style:"width:250px",class:"node-input-rule-property-replace-value",type:"text"}) | ||||
|                     var toValue = $('<input/>',{class:"node-input-rule-property-replace-value",type:"text"}) | ||||
|                         .appendTo(row3_2) | ||||
|                         .typedInput({default:'str',types:['msg','flow','global','str','num','bool','json']}); | ||||
|  | ||||
|                     $('<div/>',{style:"display:inline-block;text-align:right; width:120px; padding-right:10px; box-sizing:border-box;"}) | ||||
|                         .text(to) | ||||
|                         .appendTo(row4); | ||||
|                     var moveValue = $('<input/>',{style:"width:250px",class:"node-input-rule-property-move-value",type:"text"}) | ||||
|                     var moveValue = $('<input/>',{class:"node-input-rule-property-move-value",type:"text"}) | ||||
|                         .appendTo(row4) | ||||
|                         .typedInput({default:'msg',types:['msg','flow','global']}); | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var jsonata = require("jsonata"); | ||||
|  | ||||
|     function ChangeNode(n) { | ||||
|         RED.nodes.createNode(this, n); | ||||
| @@ -85,6 +86,13 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|             } else if (rule.tot === 'bool') { | ||||
|                 rule.to = /^true$/i.test(rule.to); | ||||
|             } else if (rule.tot === 'jsonata') { | ||||
|                 try { | ||||
|                     rule.to = jsonata(rule.to); | ||||
|                 } catch(e) { | ||||
|                     valid = false; | ||||
|                     this.error(RED._("change.errors.invalid-from",{error:e.message})); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -107,6 +115,8 @@ module.exports = function(RED) { | ||||
|                     value = node.context().global.get(rule.to); | ||||
|                 } else if (rule.tot === 'date') { | ||||
|                     value = Date.now(); | ||||
|                 } else if (rule.tot === 'jsonata') { | ||||
|                     value = rule.to.evaluate({msg:msg}); | ||||
|                 } | ||||
|                 if (rule.t === 'change') { | ||||
|                     if (rule.fromt === 'msg' || rule.fromt === 'flow' || rule.fromt === 'global') { | ||||
|   | ||||
| @@ -16,7 +16,6 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var util = require("util"); | ||||
|  | ||||
|     function JSONNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
| @@ -36,9 +35,7 @@ module.exports = function(RED) { | ||||
|                             msg.payload = JSON.stringify(msg.payload); | ||||
|                             node.send(msg); | ||||
|                         } | ||||
|                         catch(e) { | ||||
|                             node.error(RED._("json.errors.dropped-error")); | ||||
|                         } | ||||
|                         catch(e) { node.error(RED._("json.errors.dropped-error")); } | ||||
|                     } | ||||
|                     else { node.warn(RED._("json.errors.dropped-object")); } | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										32
									
								
								nodes/core/parsers/70-YAML.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								nodes/core/parsers/70-YAML.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
|  | ||||
| <script type="text/x-red" data-template-name="yaml"> | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-help-name="yaml"> | ||||
|     <p>A function that parses the <code>msg.payload</code> to convert a YAML string to/from a javascript object. Places the result back into the payload.</p> | ||||
|     <p>If the input is a YAML string it tries to parse it to a javascript object.</p> | ||||
|     <p>If the input is a javascript object it creates a YAML string.</p> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('yaml',{ | ||||
|         category: 'function', | ||||
|         color:"#DEBD5C", | ||||
|         defaults: { | ||||
|             name: {value:""} | ||||
|         }, | ||||
|         inputs:1, | ||||
|         outputs:1, | ||||
|         icon: "parser-yaml.png", | ||||
|         label: function() { | ||||
|             return this.name||"yaml"; | ||||
|         }, | ||||
|         labelStyle: function() { | ||||
|             return this.name?"node_label_italic":""; | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
							
								
								
									
										35
									
								
								nodes/core/parsers/70-YAML.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								nodes/core/parsers/70-YAML.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
|  | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|     var yaml = require('js-yaml'); | ||||
|     function YAMLNode(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         var node = this; | ||||
|         this.on("input", function(msg) { | ||||
|             if (msg.hasOwnProperty("payload")) { | ||||
|                 if (typeof msg.payload === "string") { | ||||
|                     try { | ||||
|                         msg.payload = yaml.load(msg.payload); | ||||
|                         node.send(msg); | ||||
|                     } | ||||
|                     catch(e) { node.error(e.message,msg); } | ||||
|                 } | ||||
|                 else if (typeof msg.payload === "object") { | ||||
|                     if (!Buffer.isBuffer(msg.payload)) { | ||||
|                         try { | ||||
|                             msg.payload = yaml.dump(msg.payload); | ||||
|                             node.send(msg); | ||||
|                         } | ||||
|                         catch(e) { | ||||
|                             node.error(RED._("yaml.errors.dropped-error")); | ||||
|                         } | ||||
|                     } | ||||
|                     else { node.warn(RED._("yaml.errors.dropped-object")); } | ||||
|                 } | ||||
|                 else { node.warn(RED._("yaml.errors.dropped")); } | ||||
|             } | ||||
|             else { node.send(msg); } // If no payload - just pass it on. | ||||
|         }); | ||||
|     } | ||||
|     RED.nodes.registerType("yaml",YAMLNode); | ||||
| }; | ||||
| @@ -45,8 +45,8 @@ module.exports = function(RED) { | ||||
|                 data = new Buffer(data); | ||||
|                 if (this.overwriteFile === "true") { | ||||
|                     // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while | ||||
|                     fs.writeFile(filename, data, "binary", function (err) { | ||||
|                     //fs.writeFile(filename, data, {encoding:"binary"}, function (err) { | ||||
|                     //fs.writeFile(filename, data, "binary", function (err) { | ||||
|                     fs.writeFile(filename, data, {encoding:"binary"}, function (err) { | ||||
|                         if (err) { | ||||
|                             if ((err.code === "ENOENT") && node.createDir) { | ||||
|                                 fs.ensureFile(filename, function (err) { | ||||
| @@ -71,8 +71,8 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 else { | ||||
|                     // using "binary" not {encoding:"binary"} to be 0.8 compatible for a while longer | ||||
|                     fs.appendFile(filename, data, "binary", function (err) { | ||||
|                     //fs.appendFile(filename, data, {encoding:"binary"}, function (err) { | ||||
|                     //fs.appendFile(filename, data, "binary", function (err) { | ||||
|                     fs.appendFile(filename, data, {encoding:"binary"}, function (err) { | ||||
|                         if (err) { | ||||
|                             if ((err.code === "ENOENT") && node.createDir) { | ||||
|                                 fs.ensureFile(filename, function (err) { | ||||
|   | ||||
							
								
								
									
										70
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								package.json
									
									
									
									
									
								
							| @@ -26,35 +26,38 @@ | ||||
|         "editor", "messaging", "iot", "ibm", "flow" | ||||
|     ], | ||||
|     "dependencies": { | ||||
|         "basic-auth": "1.0.4", | ||||
|         "bcryptjs": "2.3.0", | ||||
|         "basic-auth": "1.1.0", | ||||
|         "bcryptjs": "2.4.0", | ||||
|         "body-parser": "1.15.2", | ||||
|         "cheerio":"0.22.0", | ||||
|         "clone": "2.0.0", | ||||
|         "clone": "2.1.0", | ||||
|         "cookie-parser": "1.4.3", | ||||
|         "cors":"2.8.1", | ||||
|         "cron":"1.1.1", | ||||
|         "cron":"1.2.1", | ||||
|         "express": "4.14.0", | ||||
|         "follow-redirects":"0.2.0", | ||||
|         "fs-extra": "0.30.0", | ||||
|         "follow-redirects":"1.2.1", | ||||
|         "fs-extra": "1.0.0", | ||||
|         "fs.notify":"0.0.4", | ||||
|         "i18next":"1.10.6", | ||||
|         "is-utf8":"0.2.1", | ||||
|         "js-yaml": "3.7.0", | ||||
|         "json-stringify-safe":"5.0.1", | ||||
|         "jsonata":"1.0.10", | ||||
|         "media-typer": "0.3.0", | ||||
|         "mqtt": "1.14.1", | ||||
|         "mustache": "2.2.1", | ||||
|         "mqtt": "1.*", | ||||
|         "mustache": "2.3.0", | ||||
|         "nopt": "3.0.6", | ||||
|         "oauth2orize":"1.5.0", | ||||
|         "oauth2orize":"1.6.0", | ||||
|         "on-headers":"1.0.1", | ||||
|         "passport":"0.3.2", | ||||
|         "passport-http-bearer":"1.0.1", | ||||
|         "passport-oauth2-client-password":"0.1.2", | ||||
|         "raw-body":"2.1.7", | ||||
|         "semver": "5.3.0", | ||||
|         "sentiment":"1.0.6", | ||||
|         "uglify-js":"2.7.3", | ||||
|         "sentiment":"2.1.0", | ||||
|         "uglify-js":"2.7.5", | ||||
|         "when": "3.7.7", | ||||
|         "ws": "0.8.1", | ||||
|         "ws": "1.1.1", | ||||
|         "xml2js":"0.4.17", | ||||
|         "node-red-node-feedparser":"0.1.*", | ||||
|         "node-red-node-email":"0.1.*", | ||||
| @@ -62,31 +65,30 @@ | ||||
|         "node-red-node-rbe":"0.1.*" | ||||
|     }, | ||||
|     "optionalDependencies": { | ||||
|         "node-red-node-serialport":"0.4.*", | ||||
|         "bcrypt":"0.8.7" | ||||
|         "bcrypt":"~1.0.1" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "grunt": "1.0.1", | ||||
|         "grunt-chmod": "1.1.1", | ||||
|         "grunt-cli": "1.2.0", | ||||
|         "grunt-concurrent":"2.3.1", | ||||
|         "grunt-contrib-clean":"1.0.0", | ||||
|         "grunt-contrib-compress": "1.3.0", | ||||
|         "grunt-contrib-concat":"1.0.1", | ||||
|         "grunt-contrib-copy": "1.0.0", | ||||
|         "grunt-contrib-jshint": "1.0.0", | ||||
|         "grunt-contrib-uglify": "2.0.0", | ||||
|         "grunt-contrib-watch":"1.0.0", | ||||
|         "grunt-jsonlint":"1.1.0", | ||||
|         "grunt-nodemon":"0.4.2", | ||||
|         "grunt-sass":"1.2.1", | ||||
|         "grunt-simple-mocha": "0.4.1", | ||||
|         "mocha": "3.1.1", | ||||
|         "should": "8.4.0", | ||||
|         "sinon": "1.17.6", | ||||
|         "supertest": "2.0.0" | ||||
|         "grunt": "~1.0.1", | ||||
|         "grunt-chmod": "~1.1.1", | ||||
|         "grunt-cli": "~1.2.0", | ||||
|         "grunt-concurrent":"~2.3.1", | ||||
|         "grunt-contrib-clean":"~1.0.0", | ||||
|         "grunt-contrib-compress": "~1.3.0", | ||||
|         "grunt-contrib-concat":"~1.0.1", | ||||
|         "grunt-contrib-copy": "~1.0.0", | ||||
|         "grunt-contrib-jshint": "~1.1.0", | ||||
|         "grunt-contrib-uglify": "~2.0.0", | ||||
|         "grunt-contrib-watch":"~1.0.0", | ||||
|         "grunt-jsonlint":"~1.1.0", | ||||
|         "grunt-nodemon":"~0.4.2", | ||||
|         "grunt-sass":"~1.2.1", | ||||
|         "grunt-simple-mocha": "~0.4.1", | ||||
|         "mocha": "~3.2.0", | ||||
|         "should": "^8.4.0", | ||||
|         "sinon": "^1.17.6", | ||||
|         "supertest": "^2.0.0" | ||||
|     }, | ||||
|     "engines": { | ||||
|         "node": ">=0.10" | ||||
|         "node": ">=4" | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -32,7 +32,9 @@ var lastSentTime; | ||||
| function handleStatus(event) { | ||||
|     publish("status/"+event.id,event.status,true); | ||||
| } | ||||
|  | ||||
| function handleRuntimeEvent(event) { | ||||
|     publish("notification/"+event.id,event,true); | ||||
| } | ||||
| function init(_server,runtime) { | ||||
|     server = _server; | ||||
|     settings = runtime.settings; | ||||
| @@ -40,6 +42,9 @@ function init(_server,runtime) { | ||||
|  | ||||
|     runtime.events.removeListener("node-status",handleStatus); | ||||
|     runtime.events.on("node-status",handleStatus); | ||||
|  | ||||
|     runtime.events.removeListener("runtime-event",handleRuntimeEvent); | ||||
|     runtime.events.on("runtime-event",handleRuntimeEvent); | ||||
| } | ||||
|  | ||||
| function start() { | ||||
|   | ||||
| @@ -165,7 +165,12 @@ function init(_server,_runtime) { | ||||
|     } | ||||
| } | ||||
| function start() { | ||||
|     return i18n.registerMessageCatalog("editor",path.resolve(path.join(__dirname,"locales")),"editor.json").then(function(){ | ||||
|     var catalogPath = path.resolve(path.join(__dirname,"locales")); | ||||
|     return i18n.registerMessageCatalogs([ | ||||
|         {namespace: "editor",   dir: catalogPath, file:"editor.json"}, | ||||
|         {namespace: "jsonata",  dir: catalogPath, file:"jsonata.json"}, | ||||
|         {namespace: "infotips", dir: catalogPath, file:"infotips.json"} | ||||
|     ]).then(function(){ | ||||
|         comms.start(); | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -42,13 +42,13 @@ module.exports = { | ||||
|         if (settings.flowFilePretty) { | ||||
|             safeSettings.flowFilePretty = settings.flowFilePretty; | ||||
|         } | ||||
|  | ||||
|         if (!runtime.nodes.paletteEditorEnabled()) { | ||||
|             safeSettings.editorTheme = safeSettings.editorTheme || {}; | ||||
|             safeSettings.editorTheme.palette = safeSettings.editorTheme.palette || {}; | ||||
|             safeSettings.editorTheme.palette.editable = false; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         res.json(safeSettings); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -53,7 +53,8 @@ | ||||
|             "keyboardShortcuts": "Keyboard shortcuts", | ||||
|             "login": "Login", | ||||
|             "logout": "Logout", | ||||
|             "editPalette":"Manage palette" | ||||
|             "editPalette":"Manage palette", | ||||
|             "showTips": "Show tips" | ||||
|         } | ||||
|     }, | ||||
|     "user": { | ||||
| @@ -66,7 +67,8 @@ | ||||
|         "warning": "<strong>Warning</strong>: __message__", | ||||
|         "warnings": { | ||||
|             "undeployedChanges": "node has undeployed changes", | ||||
|             "nodeActionDisabled": "node actions disabled within subflow" | ||||
|             "nodeActionDisabled": "node actions disabled within subflow", | ||||
|             "missing-types": "Flows stopped due to missing node types. Check logs for details." | ||||
|         }, | ||||
|  | ||||
|         "error": "<strong>Error</strong>: __message__", | ||||
| @@ -129,9 +131,31 @@ | ||||
|             "improperlyConfigured": "The workspace contains some nodes that are not properly configured:", | ||||
|             "unknown": "The workspace contains some unknown node types:", | ||||
|             "confirm": "Are you sure you want to deploy?", | ||||
|             "conflict": "The server is running a more recent set of flows." | ||||
|             "conflict": "The server is running a more recent set of flows.", | ||||
|             "conflictChecking": "Checking to see if the changes can be merged automatically", | ||||
|             "conflictAutoMerge": "The changes include no conflicts and can be merged automatically.", | ||||
|             "conflictManualMerge": "The changes include conflicts that must be resolved before they can be deployed." | ||||
|         } | ||||
|     }, | ||||
|     "diff": { | ||||
|         "unresolvedCount": "__count__ unresolved conflict", | ||||
|         "unresolvedCount_plural": "__count__ unresolved conflicts", | ||||
|         "type": { | ||||
|             "added": "added", | ||||
|             "changed": "changed", | ||||
|             "unchanged": "unchanged", | ||||
|             "deleted": "deleted", | ||||
|             "flowDeleted": "flow deleted", | ||||
|             "flowAdded": "flow added", | ||||
|             "movedTo": "moved to __id__", | ||||
|             "movedFrom": "moved from __id__" | ||||
|         }, | ||||
|         "nodeCount": "__count__ node", | ||||
|         "nodeCount_plural": "__count__ nodes", | ||||
|         "local":"Local", | ||||
|         "remote":"Remote" | ||||
|  | ||||
|     }, | ||||
|     "subflow": { | ||||
|         "editSubflow": "Edit flow template: __name__", | ||||
|         "edit": "Edit flow template", | ||||
| @@ -167,11 +191,12 @@ | ||||
|         "selectAll": "Select all nodes", | ||||
|         "selectAllConnected": "Select all connected nodes", | ||||
|         "addRemoveNode": "Add/remove node from selection", | ||||
|         "editSelected": "Edit selected node", | ||||
|         "deleteSelected": "Delete selected nodes or link", | ||||
|         "importNode": "Import nodes", | ||||
|         "exportNode": "Export nodes", | ||||
|         "nudgeNode": "Move selected node(s) by a small amount", | ||||
|         "moveNode": "Move selected node(s) by a large amount", | ||||
|         "nudgeNode": "Move selected nodes (1px)", | ||||
|         "moveNode": "Move selected nodes (20px)", | ||||
|         "toggleSidebar": "Toggle sidebar", | ||||
|         "deleteNode": "Delete selected nodes or link", | ||||
|         "copyNode": "Copy selected nodes", | ||||
| @@ -316,6 +341,11 @@ | ||||
|         "add": "add" | ||||
|     }, | ||||
|     "search": { | ||||
|         "empty": "No matches found" | ||||
|         "empty": "No matches found", | ||||
|         "addNode": "add a node..." | ||||
|     }, | ||||
|     "expressionEditor": { | ||||
|         "functions": "Functions", | ||||
|         "insert": "Insert" | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										32
									
								
								red/api/locales/en-US/infotips.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								red/api/locales/en-US/infotips.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| { | ||||
|     "info": { | ||||
|         "tip0" : "You can remove the selected nodes or links with {{core:delete}}", | ||||
|         "tip1" : "Search for nodes using {{core:search}}", | ||||
|         "tip2" : "{{core:toggle-sidebar}} will toggle the view of this sidebar", | ||||
|         "tip3" : "You can manage your palette of nodes with {{core:manage-palette}}", | ||||
|         "tip4" : "Your flow configuration nodes are listed in the sidebar panel. It can been accessed from the menu or with {{core:show-config-tab}}", | ||||
|         "tip5" : "Enable or disable these tips from the option in the menu", | ||||
|         "tip6" : "Move the selected nodes using the [left] [up] [down] and [right] keys. Hold [shift] to nudge them further", | ||||
|         "tip7" : "Dragging a node onto a wire will splice it into the link", | ||||
|         "tip8" : "Export the selected nodes, or the current tab with {{core:export}}", | ||||
|         "tip9" : "Import a flow by dragging its JSON into the editor, or with {{core:import}}", | ||||
|         "tip10" : "[shift] [click] and drag on a node port to move all of the attached wires or just the selected one", | ||||
|         "tip11" : "Show the Info tab with {{core:show-info-tab}} or the Debug tab with {{core:show-debug-tab}}", | ||||
|         "tip12" : "[ctrl] [click] in the workspace to open the quick-add dialog", | ||||
|         "tip13" : "Hold down [ctrl] when you [click] on a node port to enable quick-wiring", | ||||
|         "tip14" : "Hold down [shift] when you [click] on a node to also select all of its connected nodes", | ||||
|         "tip15" : "Hold down [ctrl] when you [click] on a node to add or remove it from the current selection", | ||||
|         "tip16" : "Switch flow tabs with {{core:show-previous-tab}} and {{core:show-next-tab}}", | ||||
|         "tip17" : "You can confirm your changes in the node edit tray with {{core:confirm-edit-tray}} or cancel them with {{core:cancel-edit-tray}}" | ||||
|     }, | ||||
|     "info-tbd": { | ||||
|         "tip1" : "Press the <code>Deploy</code> button above to start the flow running. You can choose to deploy the whole flow or just the changes.", | ||||
|         "tip2" : "Options like <b>Show grid</b> and <b>Snap to grid</b> are under the menu icon<br/><i class='fa fa-bars'></i> <i class='fa fa-caret-right'></i> <b>View</b>", | ||||
|         "tip4" : "<i class='fa fa-bars'></i> <i class='fa fa-caret-right'></i> <b>Manage palette</b> can be used to find, add and remove extra nodes.", | ||||
|         "tip5" : "Nodes may install examples under<br/><i class='fa fa-bars'></i> <i class='fa fa-caret-right'></i> <b>Import</b> <i class='fa fa-caret-right'></i> <b>Examples</b>", | ||||
|         "tip6" : "Lots of example flows can be found on <a href='http://flows.nodered.org' target='_blank'>flows.nodered.org</a><br/>They can then be imported by drag and drop to the workspace.", | ||||
|         "tip7" : "<b>Shift-click and drag</b> on a connector to move all the attached wires in one go.", | ||||
|         "tip8" : "The <b>Node-RED Dashboard</b> package can be used to create simple User Interfaces.", | ||||
|         "tip10": "Got a question?<br/>Join us on <a href='https://node-red.slack.com/' target='_blank'>Slack</a><br/>or the<br/><a href='https://groups.google.com/forum/#!forum/node-red' target='_blank'>Node-RED Google group</a>" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										98
									
								
								red/api/locales/en-US/jsonata.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								red/api/locales/en-US/jsonata.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| { | ||||
|     "$string": { | ||||
|         "args": "arg", | ||||
|         "desc": "Casts the *arg* parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function" | ||||
|     }, | ||||
|     "$length": { | ||||
|         "args": "str", | ||||
|         "desc": "Returns the number of characters in the string `str`. An error is thrown if `str` is not a string." | ||||
|     }, | ||||
|     "$substring": { | ||||
|         "args": "str, start[, length]", | ||||
|         "desc": "Returns a string containing the characters in the first parameter `str` starting at position `start` (zero-offset). If `length` is specified, then the substring will contain maximum `length` characters. If `start` is negative then it indicates the number of characters from the end of `str`." | ||||
|     }, | ||||
|     "$substringBefore": { | ||||
|         "args": "str, chars", | ||||
|         "desc": "Returns the substring before the first occurrence of the character sequence chars in `str`. If `str` does not contain `chars`, then it returns `str`." | ||||
|     }, | ||||
|     "$substringAfter": { | ||||
|         "args": "str, chars", | ||||
|         "desc": "Returns the substring after the first occurrence of the character sequence `chars` in `str`. If `str` does not contain `chars`, then it returns `str`." | ||||
|     }, | ||||
|     "$uppercase": { | ||||
|         "args": "str", | ||||
|         "desc": "Returns a string with all the characters of `str` converted to uppercase." | ||||
|     }, | ||||
|     "$lowercase": { | ||||
|         "args": "str", | ||||
|         "desc": "Returns a string with all the characters of `str` converted to lowercase." | ||||
|     }, | ||||
|     "$split": { | ||||
|         "args": "str[, separator][, limit]", | ||||
|         "desc": "Splits the `str` parameter into an array of substrings. It is an error if `str` is not a string. The optional `separator` parameter specifies the characters within the `str` about which it should be split. If `separator` is not specified, then the empty string is assumed, and `str` will be split into an array of single characters. It is an error if `separator` is not a string. The optional `limit` parameter is a number that specifies the maximum number of substrings to include in the resultant array. Any additional substrings are discarded. If `limit` is not specified, then `str` is fully split with no limit to the size of the resultant array. It is an error if `limit` is not a non-negative number." | ||||
|     }, | ||||
|     "$join": { | ||||
|         "args": "array[, separator]", | ||||
|         "desc": "Joins an array of component strings into a single concatenated string with each component string separated by the optional `separator` parameter. It is an error if the input `array` contains an item which isn't a string. If `separator` is not specified, then it is assumed to be the empty string, i.e. no `separator` between the component strings. It is an error if `separator` is not a string." | ||||
|     }, | ||||
|  | ||||
|  | ||||
|     "$number": { | ||||
|         "args": "arg", | ||||
|         "desc": "Casts the `arg` parameter to a number using the following casting rules:\n\n - Numbers are unchanged\n - Strings that contain a sequence of characters that represent a legal JSON number are converted to that number\n - All other values cause an error to be thrown." | ||||
|     }, | ||||
|     "$sum": { | ||||
|         "args": "array", | ||||
|         "desc": "Returns the arithmetic sum of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number." | ||||
|     }, | ||||
|     "$max": { | ||||
|         "args": "array", | ||||
|         "desc": "Returns the maximum number in an `array` of numbers. It is an error if the input `array` contains an item which isn't a number." | ||||
|     }, | ||||
|     "$min": { | ||||
|         "args": "array", | ||||
|         "desc": "Returns the minimum number in an `array` of numbers. It is an error if the input `array` contains an item which isn't a number." | ||||
|     }, | ||||
|     "$average": { | ||||
|         "args": "array", | ||||
|         "desc": "Returns the mean value of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number." | ||||
|     }, | ||||
|     "$boolean": { | ||||
|         "args": "arg", | ||||
|         "desc": "Casts the argument to a Boolean using the following rules:\n\n - `Boolean` : unchanged\n - `string`: empty : `false`\n - `string`: non-empty : `true`\n - `number`: `0` : `false`\n - `number`: non-zero : `true`\n - `null` : `false`\n - `array`: empty : `false`\n - `array`: contains a member that casts to `true` : `true`\n - `array`: all members cast to `false` : `false`\n - `object`: empty : `false`\n - `object`: non-empty : `true`\n - `function` : `false`" | ||||
|     }, | ||||
|  | ||||
|     "$not": { | ||||
|         "args": "arg", | ||||
|         "desc": "Returns Boolean NOT on the argument. `arg` is first cast to a boolean" | ||||
|     }, | ||||
|     "$exists": { | ||||
|         "args": "arg", | ||||
|         "desc": "Returns Boolean `true` if the `arg` expression evaluates to a value, or `false` if the expression does not match anything (e.g. a path to a non-existent field reference)." | ||||
|     }, | ||||
|     "$count": { | ||||
|         "args": "array", | ||||
|         "desc": "Returns the number of items in the array" | ||||
|     }, | ||||
|     "$append": { | ||||
|         "args": "array, array", | ||||
|         "desc": "Appends two arrays" | ||||
|     }, | ||||
|  | ||||
|     "$keys": { | ||||
|         "args": "object", | ||||
|         "desc": "Returns an array containing the keys in the object. If the argument is an array of objects, then the array returned contains a de-duplicated list of all the keys in all of the objects." | ||||
|     }, | ||||
|  | ||||
|     "$lookup": { | ||||
|         "args": "object, key", | ||||
|         "desc": "Returns the value associated with key in object. If the first argument is an array of objects, then all of the objects in the array are searched, and the values associated with all occurrences of key are returned." | ||||
|     }, | ||||
|  | ||||
|     "$spread": { | ||||
|         "args": "object", | ||||
|         "desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array." | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -31,7 +31,9 @@ var defaultContext = { | ||||
|         image: "red/images/node-red.png" | ||||
|     }, | ||||
|     asset: { | ||||
|         red: (process.env.NODE_ENV == "development")? "red/red.js":"red/red.min.js" | ||||
|         red: (process.env.NODE_ENV == "development")? "red/red.js":"red/red.min.js", | ||||
|         main: (process.env.NODE_ENV == "development")? "red/main.js":"red/main.min.js", | ||||
|  | ||||
|     } | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,13 @@ var defaultLang = "en-US"; | ||||
| var resourceMap = {}; | ||||
| var resourceCache = {}; | ||||
|  | ||||
| function registerMessageCatalogs(catalogs) { | ||||
|     var promises = catalogs.map(function(catalog) { | ||||
|         return registerMessageCatalog(catalog.namespace,catalog.dir,catalog.file); | ||||
|     }); | ||||
|     return when.settle(promises); | ||||
| } | ||||
|  | ||||
| function registerMessageCatalog(namespace,dir,file) { | ||||
|     return when.promise(function(resolve,reject) { | ||||
|         resourceMap[namespace] = { basedir:dir, file:file}; | ||||
| @@ -109,9 +116,10 @@ function getCatalog(namespace,lang) { | ||||
| var obj = module.exports = { | ||||
|     init: init, | ||||
|     registerMessageCatalog: registerMessageCatalog, | ||||
|     registerMessageCatalogs: registerMessageCatalogs, | ||||
|     catalog: getCatalog, | ||||
|     i: i18n, | ||||
|     defaultLang:defaultLang | ||||
|     defaultLang: defaultLang | ||||
| } | ||||
|  | ||||
| obj['_'] = function() { | ||||
|   | ||||
| @@ -92,7 +92,7 @@ function start() { | ||||
|                     reportMetrics(); | ||||
|                 }, settings.runtimeMetricInterval||15000); | ||||
|             } | ||||
|             console.log("\n\n"+log._("runtime.welcome")+"\n===================\n"); | ||||
|             log.info("\n\n"+log._("runtime.welcome")+"\n===================\n"); | ||||
|             if (settings.version) { | ||||
|                 log.info(log._("runtime.version",{component:"Node-RED",version:"v"+settings.version})); | ||||
|             } | ||||
|   | ||||
| @@ -58,6 +58,7 @@ function init(runtime) { | ||||
|                     log.info(log._("nodes.flows.registered-missing", {type:type})); | ||||
|                     activeFlowConfig.missingTypes.splice(i,1); | ||||
|                     if (activeFlowConfig.missingTypes.length === 0 && started) { | ||||
|                         events.emit("runtime-event",{id:"runtime-state"}); | ||||
|                         start(); | ||||
|                     } | ||||
|                 } | ||||
| @@ -238,6 +239,7 @@ function start(type,diff,muteLog) { | ||||
|             log.info(log._("nodes.flows.missing-type-install-2")); | ||||
|             log.info("  "+settings.userDir); | ||||
|         } | ||||
|         events.emit("runtime-event",{id:"runtime-state",type:"warning",text:"notification.warnings.missing-types"}); | ||||
|         return when.resolve(); | ||||
|     } | ||||
|     if (!muteLog) { | ||||
| @@ -287,6 +289,8 @@ function start(type,diff,muteLog) { | ||||
|         } | ||||
|     } | ||||
|     events.emit("nodes-started"); | ||||
|     events.emit("runtime-event",{id:"runtime-state"}); | ||||
|  | ||||
|     if (!muteLog) { | ||||
|         if (diff) { | ||||
|             log.info(log._("nodes.flows.started-modified-"+type)); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
|  **/ | ||||
|  | ||||
| var clone = require("clone"); | ||||
| var jsonata = require("jsonata"); | ||||
|  | ||||
| function generateId() { | ||||
|     return (1+Math.random()*4294967295).toString(16); | ||||
| @@ -128,6 +129,9 @@ function compareObjects(obj1,obj2) { | ||||
| } | ||||
|  | ||||
| function normalisePropertyExpression(str) { | ||||
|     // This must be kept in sync with validatePropertyExpression | ||||
|     // in editor/js/ui/utils.js | ||||
|  | ||||
|     var length = str.length; | ||||
|     var parts = []; | ||||
|     var start = 0; | ||||
| @@ -139,7 +143,7 @@ function normalisePropertyExpression(str) { | ||||
|         var c = str[i]; | ||||
|         if (!inString) { | ||||
|             if (c === "'" || c === '"') { | ||||
|                 if (!inBox) { | ||||
|                 if (i != start) { | ||||
|                     throw new Error("Invalid property expression: unexpected "+c+" at position "+i); | ||||
|                 } | ||||
|                 inString = true; | ||||
| @@ -200,10 +204,15 @@ function normalisePropertyExpression(str) { | ||||
|             } | ||||
|         } else { | ||||
|             if (c === quoteChar) { | ||||
|                 if (i-start === 0) { | ||||
|                     throw new Error("Invalid property expression: zero-length string at position "+start); | ||||
|                 } | ||||
|                 parts.push(str.substring(start,i)); | ||||
|                 // Next char must be a ] | ||||
|                 if (!/\]/.test(str[i+1])) { | ||||
|                 // If inBox, next char must be a ]. Otherwise it may be [ or . | ||||
|                 if (inBox && !/\]/.test(str[i+1])) { | ||||
|                     throw new Error("Invalid property expression: unexpected array expression at position "+start); | ||||
|                 } else if (!inBox && i+1!==length && !/[\[\.]/.test(str[i+1])) { | ||||
|                     throw new Error("Invalid property expression: unexpected "+str[i+1]+" expression at position "+(i+1)); | ||||
|                 } | ||||
|                 start = i+1; | ||||
|                 inString = false; | ||||
| @@ -310,6 +319,8 @@ function evaluateNodeProperty(value, type, node, msg) { | ||||
|         return node.context().global.get(value); | ||||
|     } else if (type === 'bool') { | ||||
|         return /^true$/i.test(value); | ||||
|     } else if (type === 'jsonata') { | ||||
|         return jsonata(value).evaluate({msg:msg}); | ||||
|     } | ||||
|     return value; | ||||
| } | ||||
|   | ||||
| @@ -49,7 +49,7 @@ describe('debug node', function() { | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug",data:{id:"n1",name:"Debug",msg:"test", | ||||
|                     format:"string [4]",property:"payload"} | ||||
|                     format:"string[4]",property:"payload"} | ||||
|                 }); | ||||
|             }, done); | ||||
|         }); | ||||
| @@ -64,7 +64,7 @@ describe('debug node', function() { | ||||
|                 n1.emit("input", {payload:"test"}); | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string [4]"} | ||||
|                     topic:"debug",data:{id:"n1",msg:"test",property:"payload",format:"string[4]"} | ||||
|                 }); | ||||
|                 count++; | ||||
|             }, function() { | ||||
| @@ -108,7 +108,7 @@ describe('debug node', function() { | ||||
|                 n1.emit("input", {payload:"test", foo:"bar"}); | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string [3]"} | ||||
|                     topic:"debug",data:{id:"n1",msg:"bar",property:"foo",format:"string[3]"} | ||||
|                 }); | ||||
|             }, done); | ||||
|         }); | ||||
| @@ -122,7 +122,7 @@ describe('debug node', function() { | ||||
|                 n1.emit("input", {payload:"test", foo: {bar: "bar"}}); | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string [3]"} | ||||
|                     topic:"debug",data:{id:"n1",msg:"bar",property:"foo.bar",format:"string[3]"} | ||||
|                 }); | ||||
|             }, done); | ||||
|         }); | ||||
| @@ -194,7 +194,7 @@ describe('debug node', function() { | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug", | ||||
|                     data:{id:"n1",msg: '[\n 0,\n 1,\n 2,\n 3\n]',format:"array [4]", | ||||
|                     data:{id:"n1",msg: '[\n 0,\n 1,\n 2,\n 3\n]',format:"array[4]", | ||||
|                     property:"payload"} | ||||
|                 }); | ||||
|             }, done); | ||||
| @@ -214,7 +214,7 @@ describe('debug node', function() { | ||||
|                     topic:"debug", | ||||
|                     data:{ | ||||
|                         id:"n1", | ||||
|                         msg:'{\n "name": "bar",\n "o": "[circular]"\n}', | ||||
|                         msg:'{\n "name": "bar",\n "o": "[Circular ~]"\n}', | ||||
|                         property:"payload",format:"Object" | ||||
|                     } | ||||
|                 }); | ||||
| @@ -222,7 +222,7 @@ describe('debug node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should truncated a long message', function(done) { | ||||
|     it('should truncate a long message', function(done) { | ||||
|         var flow = [{id:"n1", type:"debug" }]; | ||||
|         helper.load(debugNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
| @@ -234,9 +234,9 @@ describe('debug node', function() { | ||||
|                     topic:"debug", | ||||
|                     data:{ | ||||
|                         id:"n1", | ||||
|                         msg: Array(1001).join("X")+' ....', | ||||
|                         msg: Array(1001).join("X")+'...', | ||||
|                         property:"payload", | ||||
|                         format:"string [1001]" | ||||
|                         format:"string[1001]" | ||||
|                     } | ||||
|                 }); | ||||
|             }, done); | ||||
| @@ -256,7 +256,7 @@ describe('debug node', function() { | ||||
|                         id:"n1", | ||||
|                         msg: '48454c4c4f', | ||||
|                         property:"payload", | ||||
|                         format: "buffer [5]" | ||||
|                         format: "buffer[5]" | ||||
|                     } | ||||
|                 }); | ||||
|             }, done); | ||||
| @@ -277,7 +277,7 @@ describe('debug node', function() { | ||||
|                     }); | ||||
|             }, function(msg) { | ||||
|                 JSON.parse(msg).should.eql({ | ||||
|                     topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string [9]"} | ||||
|                     topic:"debug",data:{id:"n1",msg:"message 2",property:"payload",format:"string[9]"} | ||||
|                 }); | ||||
|             }, done); | ||||
|         }); | ||||
|   | ||||
| @@ -43,6 +43,52 @@ describe('template node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should modify payload from flow context', function(done) { | ||||
|         var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{flow.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             n1.context().flow.set("value","foo"); | ||||
|             n2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.have.property('payload', 'payload=foo'); | ||||
|                 done(); | ||||
|             }); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should modify payload from global context', function(done) { | ||||
|         var flow = [{id:"n1",z:"t1", type:"template", field:"payload", template:"payload={{global.value}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             n1.context().global.set("value","foo"); | ||||
|             n2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.have.property('payload', 'payload=foo'); | ||||
|                 done(); | ||||
|             }); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should handle missing node context', function(done) { | ||||
|         // this is artificial test because in flow there is missing z property (probably never happen in real usage) | ||||
|         var flow = [{id:"n1",type:"template", field:"payload", template:"payload={{flow.value}},{{global.value}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             n2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.have.property('payload', 'payload=,'); | ||||
|                 done(); | ||||
|             }); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     it('should modify payload in plain text mode', function(done) { | ||||
|         var flow = [{id:"n1", type:"template", field:"payload", syntax:"plain", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
| @@ -57,32 +103,36 @@ describe('template node', function() { | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     xit('should modify flow context', function(done) { | ||||
|         var flow = [{id:"n1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; | ||||
|     it('should modify flow context', function(done) { | ||||
|         var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"flow", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             setTimeout( function() { | ||||
|                 console.log(n2); | ||||
|                 console.log(n2.context().global.get("payload")); | ||||
|                 //c.should.equal(1); // should only have had one output. | ||||
|             n2.on("input", function(msg) { | ||||
|                 // mesage is intact | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.have.property('payload', 'foo'); | ||||
|                 // result is in flow context | ||||
|                 n2.context().flow.get("payload").should.equal("payload=foo"); | ||||
|                 done(); | ||||
|             },50); | ||||
|             }); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     xit('should modify global context', function(done) { | ||||
|         var flow = [{id:"n1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",type:"helper"}]; | ||||
|     it('should modify global context', function(done) { | ||||
|         var flow = [{id:"n1",z:"t1", type:"template", field:"payload", fieldType:"global", template:"payload={{payload}}",wires:[["n2"]]},{id:"n2",z:"t1",type:"helper"}]; | ||||
|         helper.load(templateNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             setTimeout( function() { | ||||
|                 console.log(n2); | ||||
|                 console.log(n2.context().global.get("payload")); | ||||
|                 //c.should.equal(1); // should only have had one output. | ||||
|             n2.on("input", function(msg) { | ||||
|                 // mesage is intact | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.have.property('payload', 'foo'); | ||||
|                 // result is in global context | ||||
|                 n2.context().global.get("payload").should.equal("payload=foo"); | ||||
|                 done(); | ||||
|             },50); | ||||
|             }); | ||||
|             n1.receive({payload:"foo",topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ describe('XML node', function() { | ||||
|     afterEach(function() { | ||||
|         helper.unload(); | ||||
|     }); | ||||
|      | ||||
|  | ||||
|     it('should be loaded', function(done) { | ||||
|         var flow = [{id:"xmlNode1", type:"xml", name: "xmlNode" }]; | ||||
|         helper.load(xmlNode, flow, function() { | ||||
| @@ -56,7 +56,27 @@ describe('XML node', function() { | ||||
|             n1.receive({payload:string,topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|     | ||||
|  | ||||
|     it('should convert a valid xml string to a javascript object with options', function(done) { | ||||
|         var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, | ||||
|                     {id:"n2", type:"helper"}]; | ||||
|         helper.load(xmlNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             n2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.payload.should.have.property('employees'); | ||||
|                 msg.payload.employees.should.have.property('firstName'); | ||||
|                 should.equal(msg.payload.employees.firstName[0], 'John'); | ||||
|                 msg.payload.employees.should.have.property('lastName'); | ||||
|                 should.equal(msg.payload.employees.lastName[0], 'Smith'); | ||||
|                 done(); | ||||
|             }); | ||||
|             var string = '<employees><firstName>John</firstName><lastName>Smith</lastName></employees>'; | ||||
|             n1.receive({payload:string, topic:"bar", options:{trim:true}}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should convert a javascript object to an xml string', function(done) { | ||||
|         var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, | ||||
|                     {id:"n2", type:"helper"}]; | ||||
| @@ -73,7 +93,24 @@ describe('XML node', function() { | ||||
|             n1.receive({payload:obj,topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|      | ||||
|  | ||||
|     it('should convert a javascript object to an xml string with options', function(done) { | ||||
|         var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, | ||||
|                     {id:"n2", type:"helper"}]; | ||||
|         helper.load(xmlNode, flow, function() { | ||||
|             var n1 = helper.getNode("n1"); | ||||
|             var n2 = helper.getNode("n2"); | ||||
|             n2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 var index = msg.payload.indexOf('<employees>\n  <firstName>John</firstName>\n  <lastName>Smith</lastName>\n</employees>'); | ||||
|                 index.should.be.above(-1); | ||||
|                 done(); | ||||
|             }); | ||||
|             var obj = {"employees":{"firstName":["John"],"lastName":["Smith"] }}; | ||||
|             n1.receive({payload:obj, topic:"bar", options:{headless:true}}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should log an error if asked to parse an invalid xml string', function(done) { | ||||
|         var flow = [{id:"n1",type:"xml",wires:[["n2"]],func:"return msg;"}, | ||||
|                     {id:"n2", type:"helper"}]; | ||||
| @@ -89,7 +126,6 @@ describe('XML node', function() { | ||||
|                     logEvents.should.have.length(1); | ||||
|                     logEvents[0][0].should.have.a.property('msg'); | ||||
|                     logEvents[0][0].msg.toString().should.startWith("Error: Attribute without value"); | ||||
|                      | ||||
|                     done(); | ||||
|                 } catch(err) { | ||||
|                     done(err); | ||||
|   | ||||
							
								
								
									
										154
									
								
								test/nodes/core/parsers/70-YAML_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								test/nodes/core/parsers/70-YAML_spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | ||||
| /** | ||||
|  * Copyright 2014 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| var should = require("should"); | ||||
| var yamlNode = require("../../../../nodes/core/parsers/70-YAML.js"); | ||||
| var helper = require("../../helper.js"); | ||||
|  | ||||
| describe('YAML node', function() { | ||||
|  | ||||
|     before(function(done) { | ||||
|         helper.startServer(done); | ||||
|     }); | ||||
|  | ||||
|     afterEach(function() { | ||||
|         helper.unload(); | ||||
|     }); | ||||
|  | ||||
|     it('should be loaded', function(done) { | ||||
|         var flow = [{id:"yamlNode1", type:"yaml", name: "yamlNode" }]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yamlNode1 = helper.getNode("yamlNode1"); | ||||
|             yamlNode1.should.have.property('name', 'yamlNode'); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should convert a valid yaml string to a javascript object', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yn1 = helper.getNode("yn1"); | ||||
|             var yn2 = helper.getNode("yn2"); | ||||
|             yn2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.payload.should.have.property('employees'); | ||||
|                 msg.payload.employees[0].should.have.property('firstName', 'John'); | ||||
|                 msg.payload.employees[0].should.have.property('lastName', 'Smith'); | ||||
|                 done(); | ||||
|             }); | ||||
|             var yamlString = "employees:\n  - firstName: John\n    lastName: Smith\n"; | ||||
|             yn1.receive({payload:yamlString,topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should convert a javascript object to a yaml string', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yn1 = helper.getNode("yn1"); | ||||
|             var yn2 = helper.getNode("yn2"); | ||||
|             yn2.on("input", function(msg) { | ||||
|                 should.equal(msg.payload, "employees:\n  - firstName: John\n    lastName: Smith\n"); | ||||
|                 done(); | ||||
|             }); | ||||
|             var obj = {employees:[{firstName:"John", lastName:"Smith"}]}; | ||||
|             yn1.receive({payload:obj}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should convert an array to a yaml string', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yn1 = helper.getNode("yn1"); | ||||
|             var yn2 = helper.getNode("yn2"); | ||||
|             yn2.on("input", function(msg) { | ||||
|                 should.equal(msg.payload, "- 1\n- 2\n- 3\n"); | ||||
|                 done(); | ||||
|             }); | ||||
|             var obj = [1,2,3]; | ||||
|             yn1.receive({payload:obj}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should log an error if asked to parse an invalid yaml string', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             try { | ||||
|                 var yn1 = helper.getNode("yn1"); | ||||
|                 var yn2 = helper.getNode("yn2"); | ||||
|                 yn1.receive({payload:'employees:\n-firstName: John\n- lastName: Smith\n',topic: "bar"}); | ||||
|                 var logEvents = helper.log().args.filter(function(evt) { | ||||
|                     return evt[0].type == "yaml"; | ||||
|                 }); | ||||
|                 logEvents.should.have.length(1); | ||||
|                 logEvents[0][0].should.have.a.property('msg'); | ||||
|                 logEvents[0][0].msg.should.startWith("end of the stream"); | ||||
|                 logEvents[0][0].should.have.a.property('level',helper.log().ERROR); | ||||
|                 done(); | ||||
|             } catch(err) { | ||||
|                 done(err); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should log an error if asked to parse something thats not yaml or js', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yn1 = helper.getNode("yn1"); | ||||
|             var yn2 = helper.getNode("yn2"); | ||||
|             setTimeout(function() { | ||||
|                 try { | ||||
|                     var logEvents = helper.log().args.filter(function(evt) { | ||||
|                         return evt[0].type == "yaml"; | ||||
|                     }); | ||||
|                     logEvents.should.have.length(3); | ||||
|                     logEvents[0][0].should.have.a.property('msg'); | ||||
|                     logEvents[0][0].msg.toString().should.eql('yaml.errors.dropped'); | ||||
|                     logEvents[1][0].should.have.a.property('msg'); | ||||
|                     logEvents[1][0].msg.toString().should.eql('yaml.errors.dropped'); | ||||
|                     logEvents[2][0].should.have.a.property('msg'); | ||||
|                     logEvents[2][0].msg.toString().should.eql('yaml.errors.dropped-object'); | ||||
|                     done(); | ||||
|                 } catch(err) { | ||||
|                     done(err); | ||||
|                 } | ||||
|             },150); | ||||
|             yn1.receive({payload:true}); | ||||
|             yn1.receive({payload:1}); | ||||
|             yn1.receive({payload:new Buffer("a")}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should pass straight through if no payload set', function(done) { | ||||
|         var flow = [{id:"yn1",type:"yaml",wires:[["yn2"]],func:"return msg;"}, | ||||
|                     {id:"yn2", type:"helper"}]; | ||||
|         helper.load(yamlNode, flow, function() { | ||||
|             var yn1 = helper.getNode("yn1"); | ||||
|             var yn2 = helper.getNode("yn2"); | ||||
|             yn2.on("input", function(msg) { | ||||
|                 msg.should.have.property('topic', 'bar'); | ||||
|                 msg.should.not.have.property('payload'); | ||||
|                 done(); | ||||
|             }); | ||||
|             yn1.receive({topic: "bar"}); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| }); | ||||
| @@ -332,6 +332,10 @@ describe("red/util", function() { | ||||
|         it("pass a.0.c",function() { testABC("a.0.c",['a',0,'c']); }) | ||||
|         it("pass a['a.b[0]'].c",function() { testABC("a['a.b[0]'].c",['a','a.b[0]','c']); }) | ||||
|         it("pass a[0][0][0]",function() { testABC("a[0][0][0]",['a',0,0,0]); }) | ||||
|         it("pass '1.2.3.4'",function() { testABC("'1.2.3.4'",['1.2.3.4']); }) | ||||
|         it("pass 'a.b'[1]",function() { testABC("'a.b'[1]",['a.b',1]); }) | ||||
|         it("pass 'a.b'.c",function() { testABC("'a.b'.c",['a.b','c']); }) | ||||
|  | ||||
|  | ||||
|         it('pass a.$b.c',function() { testABC('a.$b.c',['a','$b','c']); }) | ||||
|         it('pass a["$b"].c',function() { testABC('a["$b"].c',['a','$b','c']); }) | ||||
| @@ -355,5 +359,8 @@ describe("red/util", function() { | ||||
|         it("fail a. b",function() { testInvalid("a. b"); }) | ||||
|         it("fail  a.b",function() { testInvalid(" a.b"); }) | ||||
|         it("fail a[0].[1]",function() { testInvalid("a[0].[1]"); }) | ||||
|         it("fail a['']",function() { testInvalid("a['']"); }) | ||||
|         it("fail 'a.b'c",function() { testInvalid("'a.b'c"); }) | ||||
|  | ||||
|     }); | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user