mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Merge branch 'config' into 0.14.0
explain why this merge is necessary,
This commit is contained in:
		| @@ -120,6 +120,7 @@ module.exports = function(grunt) { | ||||
|                   "editor/js/ui/tab-info.js", | ||||
|                   "editor/js/ui/tab-config.js", | ||||
|                   "editor/js/ui/editor.js", | ||||
|                   "editor/js/ui/tray.js", | ||||
|                   "editor/js/ui/clipboard.js", | ||||
|                   "editor/js/ui/library.js", | ||||
|                   "editor/js/ui/notifications.js", | ||||
|   | ||||
| @@ -273,6 +273,10 @@ RED.history = (function() { | ||||
|                             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); | ||||
|   | ||||
| @@ -21,6 +21,7 @@ RED.nodes = (function() { | ||||
|     var links = []; | ||||
|     var defaultWorkspace; | ||||
|     var workspaces = {}; | ||||
|     var workspacesOrder =[]; | ||||
|     var subflows = {}; | ||||
|  | ||||
|     var dirty = false; | ||||
| @@ -173,8 +174,10 @@ RED.nodes = (function() { | ||||
|                         if (type && type.category == "config") { | ||||
|                             var configNode = configNodes[n[d]]; | ||||
|                             if (configNode) { | ||||
|                                 updatedConfigNode = true; | ||||
|                                 configNode.users.push(n); | ||||
|                                 if (configNode.users.indexOf(n) === -1) { | ||||
|                                     updatedConfigNode = true; | ||||
|                                     configNode.users.push(n); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| @@ -269,12 +272,15 @@ RED.nodes = (function() { | ||||
|  | ||||
|     function addWorkspace(ws) { | ||||
|         workspaces[ws.id] = ws; | ||||
|         workspacesOrder.push(ws.id); | ||||
|     } | ||||
|     function getWorkspace(id) { | ||||
|         return workspaces[id]; | ||||
|     } | ||||
|     function removeWorkspace(id) { | ||||
|         delete workspaces[id]; | ||||
|         workspacesOrder.splice(workspacesOrder.indexOf(id),1); | ||||
|  | ||||
|         var removedNodes = []; | ||||
|         var removedLinks = []; | ||||
|         var n; | ||||
| @@ -517,7 +523,7 @@ RED.nodes = (function() { | ||||
|                         if ((exportable == null || exportable)) { | ||||
|                             if (!(node[d] in exportedConfigNodes)) { | ||||
|                                 exportedConfigNodes[node[d]] = true; | ||||
|                                 nns.unshift(RED.nodes.convertNode(confNode)); | ||||
|                                 set.push(confNode); | ||||
|                             } | ||||
|                         } else { | ||||
|                             convertedNode[d] = ""; | ||||
| @@ -537,11 +543,9 @@ RED.nodes = (function() { | ||||
|     function createCompleteNodeSet() { | ||||
|         var nns = []; | ||||
|         var i; | ||||
|         for (i in workspaces) { | ||||
|             if (workspaces.hasOwnProperty(i)) { | ||||
|                 if (workspaces[i].type == "tab") { | ||||
|                     nns.push(workspaces[i]); | ||||
|                 } | ||||
|         for (i=0;i<workspacesOrder.length;i++) { | ||||
|             if (workspaces[workspacesOrder[i]].type == "tab") { | ||||
|                 nns.push(workspaces[workspacesOrder[i]]); | ||||
|             } | ||||
|         } | ||||
|         for (i in subflows) { | ||||
| @@ -663,6 +667,9 @@ RED.nodes = (function() { | ||||
|         var new_links = []; | ||||
|         var nid; | ||||
|         var def; | ||||
|         var configNode; | ||||
|  | ||||
|         // Find all tabs and subflow templates | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
|             // TODO: remove workspace in next release+1 | ||||
| @@ -706,6 +713,8 @@ RED.nodes = (function() { | ||||
|                 addSubflow(n,createNewIds); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Add a tab if there isn't one there already | ||||
|         if (defaultWorkspace == null) { | ||||
|             defaultWorkspace = { type:"tab", id:getID(), label:RED._('workspace.defaultName',{number:1})}; | ||||
|             addWorkspace(defaultWorkspace); | ||||
| @@ -714,6 +723,7 @@ RED.nodes = (function() { | ||||
|             activeWorkspace = RED.workspaces.active(); | ||||
|         } | ||||
|  | ||||
|         // Find all config nodes and add them | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
|             def = registry.getNodeType(n.type); | ||||
| @@ -750,7 +760,7 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|  | ||||
|                 if (!existingConfigNode) { //} || !compareNodes(existingConfigNode,n,true) || existingConfigNode._def.exclusive || existingConfigNode.z !== n.z) { | ||||
|                     var configNode = {id:n.id, z:n.z, type:n.type, users:[]}; | ||||
|                     configNode = {id:n.id, z:n.z, type:n.type, users:[]}; | ||||
|                     for (var d in def.defaults) { | ||||
|                         if (def.defaults.hasOwnProperty(d)) { | ||||
|                             configNode[d] = n[d]; | ||||
| @@ -768,6 +778,7 @@ RED.nodes = (function() { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Find regular flow nodes and subflow instances | ||||
|         for (i=0;i<newNodes.length;i++) { | ||||
|             n = newNodes[i]; | ||||
|             // TODO: remove workspace in next release+1 | ||||
| @@ -838,15 +849,7 @@ RED.nodes = (function() { | ||||
|                             node.outputs = n.outputs||node._def.outputs; | ||||
|                             for (var d2 in node._def.defaults) { | ||||
|                                 if (node._def.defaults.hasOwnProperty(d2)) { | ||||
|                                     if (node._def.defaults[d2].type) { | ||||
|                                         if (node_map[n[d2]]) { | ||||
|                                             node[d2] = node_map[n[d2]].id; | ||||
|                                         } else { | ||||
|                                             node[d2] = n[d2]; | ||||
|                                         } | ||||
|                                     } else { | ||||
|                                         node[d2] = n[d2]; | ||||
|                                     } | ||||
|                                     node[d2] = n[d2]; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| @@ -860,6 +863,7 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         // Remap all wires and config node references | ||||
|         for (i=0;i<new_nodes.length;i++) { | ||||
|             n = new_nodes[i]; | ||||
|             if (n.wires) { | ||||
| @@ -875,6 +879,19 @@ RED.nodes = (function() { | ||||
|                 } | ||||
|                 delete n.wires; | ||||
|             } | ||||
|             for (var d3 in n._def.defaults) { | ||||
|                 if (n._def.defaults.hasOwnProperty(d3)) { | ||||
|                     if (n._def.defaults[d3].type && node_map[n[d3]]) { | ||||
|                         n[d3] = node_map[n[d3]].id; | ||||
|                         configNode = RED.nodes.node(n[d3]); | ||||
|                         if (configNode && configNode.users.indexOf(n) === -1) { | ||||
|                             configNode.users.push(n); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|         for (i=0;i<new_subflows.length;i++) { | ||||
|             n = new_subflows[i]; | ||||
| @@ -972,6 +989,8 @@ RED.nodes = (function() { | ||||
|  | ||||
|         addWorkspace: addWorkspace, | ||||
|         removeWorkspace: removeWorkspace, | ||||
|         getWorkspaceOrder: function() { return workspacesOrder }, | ||||
|         setWorkspaceOrder: function(order) { workspacesOrder = order; }, | ||||
|         workspace: getWorkspace, | ||||
|  | ||||
|         addSubflow: addSubflow, | ||||
| @@ -1004,10 +1023,8 @@ RED.nodes = (function() { | ||||
|             } | ||||
|         }, | ||||
|         eachWorkspace: function(cb) { | ||||
|             for (var id in workspaces) { | ||||
|                 if (workspaces.hasOwnProperty(id)) { | ||||
|                     cb(workspaces[id]); | ||||
|                 } | ||||
|             for (var i=0;i<workspacesOrder.length;i++) { | ||||
|                 cb(workspaces[workspacesOrder[i]]); | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|   | ||||
| @@ -23,7 +23,7 @@ RED.clipboard = (function() { | ||||
|     var importNodesDialog; | ||||
|  | ||||
|     function setupDialogs() { | ||||
|         dialog = $('<div id="clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>') | ||||
|         dialog = $('<div id="clipboard-dialog" class="hide node-red-dialog"><form class="dialog-form form-horizontal"></form></div>') | ||||
|             .appendTo("body") | ||||
|             .dialog({ | ||||
|                 modal: true, | ||||
| @@ -31,14 +31,6 @@ RED.clipboard = (function() { | ||||
|                 width: 500, | ||||
|                 resizable: false, | ||||
|                 buttons: [ | ||||
|                     { | ||||
|                         id: "clipboard-dialog-ok", | ||||
|                         text: RED._("common.label.ok"), | ||||
|                         click: function() { | ||||
|                             RED.view.importNodes($("#clipboard-import").val()); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "clipboard-dialog-cancel", | ||||
|                         text: RED._("common.label.cancel"), | ||||
| @@ -48,10 +40,20 @@ RED.clipboard = (function() { | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "clipboard-dialog-close", | ||||
|                         class: "primary", | ||||
|                         text: RED._("common.label.close"), | ||||
|                         click: function() { | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     }, | ||||
|                     { | ||||
|                         id: "clipboard-dialog-ok", | ||||
|                         class: "primary", | ||||
|                         text: RED._("common.label.import"), | ||||
|                         click: function() { | ||||
|                             RED.view.importNodes($("#clipboard-import").val()); | ||||
|                             $( this ).dialog( "close" ); | ||||
|                         } | ||||
|                     } | ||||
|                 ], | ||||
|                 open: function(e) { | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -254,7 +254,14 @@ RED.library = (function() { | ||||
|             height: 450, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.load"), | ||||
|                     class: "primary", | ||||
|                     click: function() { | ||||
|                         if (selectedLibraryItem) { | ||||
|                             for (var i=0;i<options.fields.length;i++) { | ||||
| @@ -265,12 +272,6 @@ RED.library = (function() { | ||||
|                         } | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
|             ], | ||||
|             open: function(e) { | ||||
| @@ -359,15 +360,16 @@ RED.library = (function() { | ||||
|             height: 230, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         saveToLibrary(true); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     text: RED._("common.label.save"), | ||||
|                     class: "primary", | ||||
|                     click: function() { | ||||
|                         saveToLibrary(true); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
| @@ -381,15 +383,16 @@ RED.library = (function() { | ||||
|             height: 230, | ||||
|             buttons: [ | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         saveToLibrary(false); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     text: RED._("common.label.save"), | ||||
|                     class: "primary", | ||||
|                     click: function() { | ||||
|                         saveToLibrary(false); | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
| @@ -432,9 +435,17 @@ RED.library = (function() { | ||||
|                     resizable: false, | ||||
|                     title: RED._("library.exportToLibrary"), | ||||
|                     buttons: [ | ||||
|                         { | ||||
|                             id: "library-dialog-cancel", | ||||
|                             text: RED._("common.label.cancel"), | ||||
|                             click: function() { | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             id: "library-dialog-ok", | ||||
|                             text: RED._("common.label.ok"), | ||||
|                             class: "primary", | ||||
|                             text: RED._("common.label.export"), | ||||
|                             click: function() { | ||||
|                                 //TODO: move this to RED.library | ||||
|                                 var flowName = $("#node-input-library-filename").val(); | ||||
| @@ -457,13 +468,6 @@ RED.library = (function() { | ||||
|                                 } | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             id: "library-dialog-cancel", | ||||
|                             text: RED._("common.label.cancel"), | ||||
|                             click: function() { | ||||
|                                 $( this ).dialog( "close" ); | ||||
|                             } | ||||
|                         } | ||||
|                     ], | ||||
|                     open: function(e) { | ||||
|   | ||||
| @@ -102,13 +102,13 @@ RED.sidebar = (function() { | ||||
|                 sidebarSeparator.start = ui.position.left; | ||||
|                 sidebarSeparator.chartWidth = $("#workspace").width(); | ||||
|                 sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2; | ||||
|  | ||||
|  | ||||
|                  | ||||
|                 if (!RED.menu.isSelected("menu-item-sidebar")) { | ||||
|                     sidebarSeparator.opening = true; | ||||
|                     var newChartRight = 7; | ||||
|                     $("#sidebar").addClass("closing"); | ||||
|                     $("#workspace").css("right",newChartRight); | ||||
|                     $("#editor-stack").css("right",newChartRight+1); | ||||
|                     $("#sidebar").width(0); | ||||
|                     RED.menu.setSelected("menu-item-sidebar",true); | ||||
|                     RED.events.emit("sidebar:resize"); | ||||
| @@ -147,6 +147,7 @@ RED.sidebar = (function() { | ||||
|  | ||||
|                 var newChartRight = sidebarSeparator.chartRight-d; | ||||
|                 $("#workspace").css("right",newChartRight); | ||||
|                 $("#editor-stack").css("right",newChartRight+1); | ||||
|                 $("#sidebar").width(newSidebarWidth); | ||||
|  | ||||
|                 sidebar_tabs.resize(); | ||||
| @@ -159,6 +160,7 @@ RED.sidebar = (function() { | ||||
|                     if ($("#sidebar").width() < 180) { | ||||
|                         $("#sidebar").width(180); | ||||
|                         $("#workspace").css("right",187); | ||||
|                         $("#editor-stack").css("right",188); | ||||
|                     } | ||||
|                 } | ||||
|                 $("#sidebar-separator").css("left","auto"); | ||||
|   | ||||
| @@ -17,7 +17,7 @@ RED.sidebar.config = (function() { | ||||
|  | ||||
|  | ||||
|     var content = document.createElement("div"); | ||||
|     content.className = "sidebar-node-config" | ||||
|     content.className = "sidebar-node-config"; | ||||
|  | ||||
|     $('<div class="button-group sidebar-header">'+ | ||||
|       '<a class="sidebar-header-button-toggle selected" id="workspace-config-node-filter-all" href="#"><span data-i18n="sidebar.config.filterAll"></span></a>'+ | ||||
| @@ -35,6 +35,9 @@ RED.sidebar.config = (function() { | ||||
|     var flowCategories = $("<div>").appendTo(content); | ||||
|     var subflowCategories = $("<div>").appendTo(content); | ||||
|  | ||||
|  | ||||
|     var shade = $('<div class="sidebar-node-config-shade hide"></div>').appendTo(content); | ||||
|  | ||||
|     var showUnusedOnly = false; | ||||
|  | ||||
|     var categories = {}; | ||||
| @@ -288,6 +291,12 @@ RED.sidebar.config = (function() { | ||||
|     return { | ||||
|         init:init, | ||||
|         show:show, | ||||
|         refresh:refreshConfigNodeList | ||||
|         refresh:refreshConfigNodeList, | ||||
|         disable: function() { | ||||
|             shade.show(); | ||||
|         }, | ||||
|         enable: function() { | ||||
|             shade.hide(); | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2015 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. | ||||
| @@ -24,7 +24,7 @@ RED.tabs = (function() { | ||||
|         var currentTabWidth; | ||||
|         var currentActiveTabWidth = 0; | ||||
|  | ||||
|         var ul = $("#"+options.id) | ||||
|         var ul = $("#"+options.id); | ||||
|         ul.addClass("red-ui-tabs"); | ||||
|         ul.children().first().addClass("active"); | ||||
|         ul.children().addClass("red-ui-tab"); | ||||
| @@ -118,6 +118,7 @@ RED.tabs = (function() { | ||||
|             addTab: function(tab) { | ||||
|                 tabs[tab.id] = tab; | ||||
|                 var li = $("<li/>",{class:"red-ui-tab"}).appendTo(ul); | ||||
|                 li.data("tabId",tab.id); | ||||
|                 var link = $("<a/>",{href:"#"+tab.id, class:"red-ui-tab-label"}).appendTo(li); | ||||
|                 if (tab.icon) { | ||||
|                     $('<img src="'+tab.icon+'" class="red-ui-tab-icon"/>').appendTo(link); | ||||
| @@ -142,6 +143,85 @@ RED.tabs = (function() { | ||||
|                 if (ul.find("li.red-ui-tab").size() == 1) { | ||||
|                     activateTab(link); | ||||
|                 } | ||||
|                 if (options.onreorder) { | ||||
|                     var originalTabOrder; | ||||
|                     var tabDragIndex; | ||||
|                     var tabElements = []; | ||||
|                     var startDragIndex; | ||||
|  | ||||
|                     li.draggable({ | ||||
|                         axis:"x", | ||||
|                         distance: 20, | ||||
|                         start: function(event,ui) { | ||||
|                             originalTabOrder = []; | ||||
|                             tabElements = []; | ||||
|                             ul.children().each(function(i) { | ||||
|                                 tabElements[i] = { | ||||
|                                     el:$(this), | ||||
|                                     text: $(this).text(), | ||||
|                                     left: $(this).position().left, | ||||
|                                     width: $(this).width() | ||||
|                                 }; | ||||
|                                 if ($(this).is(li)) { | ||||
|                                     tabDragIndex = i; | ||||
|                                     startDragIndex = i; | ||||
|                                 } | ||||
|                                 originalTabOrder.push($(this).data("tabId")); | ||||
|                             }); | ||||
|                             ul.children().each(function(i) { | ||||
|                                 if (i!==tabDragIndex) { | ||||
|                                     $(this).css({ | ||||
|                                         position: 'absolute', | ||||
|                                         left: tabElements[i].left+"px", | ||||
|                                         width: tabElements[i].width+2, | ||||
|                                         transition: "left 0.3s" | ||||
|                                     }); | ||||
|                                 } | ||||
|  | ||||
|                             }) | ||||
|                             if (!li.hasClass('active')) { | ||||
|                                 li.css({'zIndex':1}); | ||||
|                             } | ||||
|                         }, | ||||
|                         drag: function(event,ui) { | ||||
|                             ui.position.left += tabElements[tabDragIndex].left; | ||||
|                             var tabCenter = ui.position.left + tabElements[tabDragIndex].width/2; | ||||
|                             for (var i=0;i<tabElements.length;i++) { | ||||
|                                 if (i === tabDragIndex) { | ||||
|                                     continue; | ||||
|                                 } | ||||
|                                 if (tabCenter > tabElements[i].left && tabCenter < tabElements[i].left+tabElements[i].width) { | ||||
|                                     if (i < tabDragIndex) { | ||||
|                                         tabElements[i].left += tabElements[tabDragIndex].width+8; | ||||
|                                         tabElements[tabDragIndex].el.detach().insertBefore(tabElements[i].el); | ||||
|                                     } else { | ||||
|                                         tabElements[i].left -= tabElements[tabDragIndex].width+8; | ||||
|                                         tabElements[tabDragIndex].el.detach().insertAfter(tabElements[i].el); | ||||
|                                     } | ||||
|                                     tabElements[i].el.css({left:tabElements[i].left+"px"}); | ||||
|  | ||||
|                                     tabElements.splice(i, 0, tabElements.splice(tabDragIndex, 1)[0]); | ||||
|  | ||||
|                                     tabDragIndex = i; | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                             // console.log(ui.position.left,ui.offset.left); | ||||
|                         }, | ||||
|                         stop: function(event,ui) { | ||||
|                             ul.children().css({position:"relative",left:"",transition:""}); | ||||
|                             if (!li.hasClass('active')) { | ||||
|                                 li.css({zIndex:""}); | ||||
|                             } | ||||
|                             updateTabWidths(); | ||||
|                             if (startDragIndex !== tabDragIndex) { | ||||
|                                 options.onreorder(originalTabOrder, $.makeArray(ul.children().map(function() { return $(this).data('tabId');}))); | ||||
|                             } | ||||
|                             activateTab(tabElements[tabDragIndex].el.data('tabId')); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             }, | ||||
|             removeTab: removeTab, | ||||
|             activateTab: activateTab, | ||||
| @@ -158,6 +238,30 @@ RED.tabs = (function() { | ||||
|                 tab.attr("title",label); | ||||
|                 tab.find("span").text(label); | ||||
|                 updateTabWidths(); | ||||
|             }, | ||||
|             order: function(order) { | ||||
|                 var existingTabOrder = $.makeArray(ul.children().map(function() { return $(this).data('tabId');})); | ||||
|                 if (existingTabOrder.length !== order.length) { | ||||
|                     return | ||||
|                 } | ||||
|                 var i; | ||||
|                 var match = true; | ||||
|                 for (i=0;i<order.length;i++) { | ||||
|                     if (order[i] !== existingTabOrder[i]) { | ||||
|                         match = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (match) { | ||||
|                     return; | ||||
|                 } | ||||
|                 var existingTabMap = {}; | ||||
|                 var existingTabs = ul.children().detach().each(function() { | ||||
|                     existingTabMap[$(this).data("tabId")] = $(this); | ||||
|                 }); | ||||
|                 for (i=0;i<order.length;i++) { | ||||
|                     existingTabMap[order[i]].appendTo(ul); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|   | ||||
							
								
								
									
										225
									
								
								editor/js/ui/tray.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								editor/js/ui/tray.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,225 @@ | ||||
| /** | ||||
|  * 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.tray = (function() { | ||||
|  | ||||
|     var stack = []; | ||||
|  | ||||
|     function resize() { | ||||
|  | ||||
|     } | ||||
|     function showTray(options) { | ||||
|         var el = $('<div class="editor-tray"></div>'); | ||||
|         var header = $('<div class="editor-tray-header"></div>').appendTo(el); | ||||
|         var body = $('<div class="editor-tray-body"></div>').appendTo(el); | ||||
|         var footer = $('<div class="editor-tray-footer"></div>').appendTo(el); | ||||
|         var resizer = $('<div class="editor-tray-resize-handle"></div>').appendTo(el); | ||||
|         // var growButton = $('<a class="editor-tray-resize-button" style="cursor: w-resize;"><i class="fa fa-angle-left"></i></a>').appendTo(resizer); | ||||
|         // var shrinkButton = $('<a class="editor-tray-resize-button" style="cursor: e-resize;"><i style="margin-left: 1px;" class="fa fa-angle-right"></i></a>').appendTo(resizer); | ||||
|         if (options.title) { | ||||
|             $('<div class="editor-tray-titlebar">'+options.title+'</div>').appendTo(header); | ||||
|         } | ||||
|         var buttonBar = $('<div class="editor-tray-toolbar"></div>').appendTo(header); | ||||
|         if (options.buttons) { | ||||
|             for (var i=0;i<options.buttons.length;i++) { | ||||
|                 var button = options.buttons[i]; | ||||
|  | ||||
|                 var b = $('<button>').appendTo(buttonBar); | ||||
|                 if (button.id) { | ||||
|                     b.attr('id',button.id); | ||||
|                 } | ||||
|                 if (button.text) { | ||||
|                     b.html(button.text); | ||||
|                 } | ||||
|                 if (button.click) { | ||||
|                     b.click(button.click); | ||||
|                 } | ||||
|                 if (button.class) { | ||||
|                     b.addClass(button.class); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         el.appendTo("#editor-stack"); | ||||
|         var tray = { | ||||
|             tray: el, | ||||
|             header: header, | ||||
|             body: body, | ||||
|             footer: footer, | ||||
|             options: options | ||||
|         }; | ||||
|         stack.push(tray); | ||||
|  | ||||
|         el.draggable({ | ||||
|                 handle: resizer, | ||||
|                 axis: "x", | ||||
|                 start:function(event,ui) { | ||||
|                     el.width('auto'); | ||||
|                 }, | ||||
|                 drag: function(event,ui) { | ||||
|                     if (ui.position.left > -tray.preferredWidth-1) { | ||||
|                         ui.position.left = -tray.preferredWidth-1; | ||||
|                     } else if (tray.options.resize) { | ||||
|                         setTimeout(function() { | ||||
|                             tray.options.resize({width: -ui.position.left}); | ||||
|                         },0); | ||||
|                     } | ||||
|                     tray.width = -ui.position.left; | ||||
|                 }, | ||||
|                 stop:function(event,ui) { | ||||
|                     el.width(-ui.position.left); | ||||
|                     el.css({left:''}); | ||||
|                     if (tray.options.resize) { | ||||
|                         tray.options.resize({width: -ui.position.left}); | ||||
|                     } | ||||
|                     tray.width = -ui.position.left; | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         if (options.open) { | ||||
|             options.open(el); | ||||
|         } | ||||
|  | ||||
|         $("#header-shade").show(); | ||||
|         $("#editor-shade").show(); | ||||
|         RED.sidebar.config.disable(); | ||||
|  | ||||
|         tray.preferredWidth = el.width(); | ||||
|         if (options.width) { | ||||
|             if (options.width > $("#editor-stack").position().left-8) { | ||||
|                 options.width = $("#editor-stack").position().left-8; | ||||
|             } | ||||
|             el.width(options.width); | ||||
|         } | ||||
|         tray.width = el.width(); | ||||
|         el.css({ | ||||
|             right: -(el.width()+10)+"px", | ||||
|             transition: "right 0.2s ease" | ||||
|         }); | ||||
|         $("#workspace").scrollLeft(0); | ||||
|  | ||||
|         var trayHeight = el.height()-header.outerHeight()-footer.outerHeight(); | ||||
|         body.height(trayHeight-40); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|             setTimeout(function() { | ||||
|                 if (!options.width) { | ||||
|                     el.width(tray.preferredWidth); | ||||
|                 } | ||||
|                 if (options.resize) { | ||||
|                     options.resize({width:el.width()}); | ||||
|                 } | ||||
|                 if (options.show) { | ||||
|                     options.show(); | ||||
|                 } | ||||
|             },150); | ||||
|             el.css({right:0}); | ||||
|         },0); | ||||
|  | ||||
|         // growButton.click(function(e) { | ||||
|         //     e.preventDefault(); | ||||
|         //     tray.lastWidth = tray.width; | ||||
|         //     tray.width = $("#editor-stack").position().left-8; | ||||
|         //     el.width(tray.width); | ||||
|         //     if (options.resize) { | ||||
|         //         options.resize({width:tray.width}); | ||||
|         //     } | ||||
|         // }); | ||||
|         // shrinkButton.click(function(e) { | ||||
|         //     e.preventDefault(); | ||||
|         //     if (tray.lastWidth && tray.width > tray.lastWidth) { | ||||
|         //         tray.width = tray.lastWidth; | ||||
|         //     } else if (tray.width > tray.preferredWidth) { | ||||
|         //         tray.width = tray.preferredWidth; | ||||
|         //     } | ||||
|         //     el.width(tray.width); | ||||
|         //     if (options.resize) { | ||||
|         //         options.resize({width:tray.width}); | ||||
|         //     } | ||||
|         // }); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     function handleWindowResize() { | ||||
|         if (stack.length > 0) { | ||||
|             var tray = stack[stack.length-1]; | ||||
|             var trayHeight = tray.tray.height()-tray.header.outerHeight()-tray.footer.outerHeight(); | ||||
|             tray.body.height(trayHeight-40); | ||||
|  | ||||
|             if (tray.width > $("#editor-stack").position().left-8) { | ||||
|                 tray.width = Math.max(tray.preferredWidth,$("#editor-stack").position().left-8); | ||||
|                 tray.tray.width(tray.width); | ||||
|             } | ||||
|             if (tray.options.resize) { | ||||
|                 tray.options.resize({width:tray.width}); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: function init() { | ||||
|             $(window).resize(handleWindowResize); | ||||
|             RED.events.on("sidebar:resize",handleWindowResize); | ||||
|         }, | ||||
|         show: function show(options) { | ||||
|             if (stack.length > 0) { | ||||
|                 var oldTray = stack[stack.length-1]; | ||||
|                 oldTray.tray.css({ | ||||
|                     right: -(oldTray.tray.width()+10)+"px" | ||||
|                 }); | ||||
|                 setTimeout(function() { | ||||
|                     oldTray.tray.detach(); | ||||
|                     showTray(options); | ||||
|                 },200) | ||||
|             } else { | ||||
|                 showTray(options); | ||||
|             } | ||||
|  | ||||
|         }, | ||||
|         close: function close(done) { | ||||
|             if (stack.length > 0) { | ||||
|                 var tray = stack.pop(); | ||||
|                 tray.tray.css({ | ||||
|                     right: -(tray.tray.width()+10)+"px" | ||||
|                 }); | ||||
|                 setTimeout(function() { | ||||
|                     if (tray.options.close) { | ||||
|                         tray.options.close(); | ||||
|                     } | ||||
|                     tray.tray.remove(); | ||||
|                     if (stack.length > 0) { | ||||
|                         var oldTray = stack[stack.length-1]; | ||||
|                         oldTray.tray.appendTo("#editor-stack"); | ||||
|                         setTimeout(function() { | ||||
|                             handleWindowResize(); | ||||
|                             oldTray.tray.css({right:0}); | ||||
|                             if (oldTray.options.show) { | ||||
|                                 oldTray.options.show(); | ||||
|                             } | ||||
|                         },0); | ||||
|                     } | ||||
|                     if (done) { | ||||
|                         done(); | ||||
|                     } | ||||
|                 },200) | ||||
|                 if (stack.length === 0) { | ||||
|                     $("#header-shade").hide(); | ||||
|                     $("#editor-shade").hide(); | ||||
|                     RED.sidebar.config.enable(); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| })(); | ||||
| @@ -334,7 +334,7 @@ RED.view = (function() { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 var nn = { id:(1+Math.random()*4294967295).toString(16),z:RED.workspaces.active()}; | ||||
|                 var nn = { id:RED.nodes.id(),z:RED.workspaces.active()}; | ||||
|  | ||||
|                 nn.type = selected_tool; | ||||
|                 nn._def = RED.nodes.getType(nn.type); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2015 IBM Corp. | ||||
|  * Copyright 2015, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -113,6 +113,11 @@ RED.workspaces = (function() { | ||||
|                 RED.menu.setDisabled("menu-item-workspace-delete",workspace_tabs.count() == 1); | ||||
|                 RED.menu.removeItem("menu-item-workspace-menu-"+tab.id.replace(".","-")); | ||||
|             }, | ||||
|             onreorder: function(oldOrder, newOrder) { | ||||
|                 RED.history.push({t:'reorder',order:oldOrder,dirty:RED.nodes.dirty()}); | ||||
|                 RED.nodes.dirty(true); | ||||
|                 setWorkspaceOrder(newOrder); | ||||
|             }, | ||||
|             minimumActiveTabWidth: 150 | ||||
|         }); | ||||
|  | ||||
| @@ -134,7 +139,14 @@ RED.workspaces = (function() { | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.ok"), | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.done"), | ||||
|                     class: "primary", | ||||
|                     click: function() { | ||||
|                         var workspace = $(this).dialog('option','workspace'); | ||||
|                         var label = $( "#node-input-workspace-name" ).val(); | ||||
| @@ -146,13 +158,8 @@ RED.workspaces = (function() { | ||||
|                         } | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|                     text: RED._("common.label.cancel"), | ||||
|                     click: function() { | ||||
|                         $( this ).dialog( "close" ); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             ], | ||||
|             open: function(e) { | ||||
|                 RED.keyboard.disable(); | ||||
| @@ -216,11 +223,18 @@ RED.workspaces = (function() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function setWorkspaceOrder(order) { | ||||
|         RED.nodes.setWorkspaceOrder(order.filter(function(id) { | ||||
|             return RED.nodes.workspace(id) !== undefined; | ||||
|         })); | ||||
|         workspace_tabs.order(order); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|         add: addWorkspace, | ||||
|         remove: removeWorkspace, | ||||
|  | ||||
|         order: setWorkspaceOrder, | ||||
|         edit: function(id) { | ||||
|             showRenameWorkspaceDialog(id||activeWorkspace); | ||||
|         }, | ||||
|   | ||||
| @@ -14,6 +14,8 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| $background-color: #f3f3f3; | ||||
|  | ||||
| $form-placeholder-color: #bbbbbb; | ||||
| $form-text-color: #444; | ||||
| $form-input-focus-color:  rgba(85,150,230,0.8); | ||||
| @@ -45,6 +47,13 @@ $workspace-button-color-hover: #666; | ||||
| $workspace-button-color-active: #666; | ||||
| $workspace-button-color-selected: #AAA; | ||||
|  | ||||
| $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; | ||||
|  | ||||
| $editor-button-color-primary: #666; | ||||
| $editor-button-color-secondary: #999; | ||||
|  | ||||
| $shade-color: rgba(220,220,220,0.5); | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2015 IBM Corp. | ||||
|  * Copyright 2015, 2016 IBM Corp. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
| @@ -14,7 +14,144 @@ | ||||
|  * limitations under the License. | ||||
|  **/ | ||||
|  | ||||
| .dialog-form, #dialog-form, #dialog-config-form { | ||||
|  | ||||
| #editor-stack { | ||||
|     position: absolute; | ||||
|     margin: 0; | ||||
|     top: 0; | ||||
|     bottom: 0px; | ||||
|     right: 323px; | ||||
|     width: 0; | ||||
|     z-index: 5; | ||||
| } | ||||
| .editor-tray { | ||||
|     position:absolute; | ||||
|     margin: 0; | ||||
|     top: 0; | ||||
|     min-width: 500px; | ||||
|     width: auto; | ||||
|     right: -1000px; | ||||
|     bottom: 0; | ||||
|     background: #fff; | ||||
|     border-left: 1px solid $secondary-border-color; | ||||
|     border-bottom: 1px solid $primary-border-color; | ||||
|     box-sizing: content-box; | ||||
| } | ||||
| .editor-tray.open { | ||||
|     right: 0; | ||||
| } | ||||
| .editor-tray-body { | ||||
|     width: auto; | ||||
|     padding: 20px; | ||||
|     min-width: 500px; | ||||
|     box-sizing: border-box; | ||||
|     overflow-y: scroll; | ||||
| } | ||||
| .editor-tray-header { | ||||
|     @include disable-selection; | ||||
|     position: relative; | ||||
|     box-sizing: border-box; | ||||
|     font-weight: bold; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     background: $palette-header-background; | ||||
|     &:after { | ||||
|         content: ""; | ||||
|         display: table; | ||||
|         clear: both; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .editor-tray-footer { | ||||
|     @include component-footer; | ||||
|     height: 35px; | ||||
| } | ||||
|  | ||||
| .editor-tray-toolbar { | ||||
|     text-align: right; | ||||
|     padding: 8px; | ||||
|  | ||||
|     button { | ||||
|         @include workspace-button; | ||||
|         font-size: 14px; | ||||
|         padding: 6px 14px; | ||||
|         margin-right: 8px; | ||||
|         color: $editor-button-color-primary; | ||||
|         &.leftButton { | ||||
|             float: left; | ||||
|             margin-top: 1px; | ||||
|         } | ||||
|         &:not(.leftButton):not(:last-child) { | ||||
|             margin-right: 16px; | ||||
|         } | ||||
|         &:not(.primary) { | ||||
|             background: none; | ||||
|             &:hover { | ||||
|                 color: $editor-button-color-primary; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| .editor-tray-titlebar { | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     padding: 8px; | ||||
| } | ||||
| .editor-tray-breadcrumbs { | ||||
|     list-style-type: none; | ||||
|     margin: 0; | ||||
|     padding:0; | ||||
|  | ||||
|     li { | ||||
|         display: inline-block; | ||||
|         padding:0; | ||||
|         margin:0; | ||||
|  | ||||
|         &:not(:last-child) { | ||||
|             color: $editor-button-color-secondary; | ||||
|             font-weight: normal; | ||||
|  | ||||
|             &:after { | ||||
|                 display: inline-block; | ||||
|                 content: '>'; | ||||
|                 margin: 0 5px; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| .editor-tray-resize-handle { | ||||
|     position: absolute; | ||||
|     top: 0px; | ||||
|     bottom: 0px; | ||||
|     width: 7px; | ||||
|     left: -9px; | ||||
|     background: $background-color url(images/grip.png) no-repeat 50% 50%; | ||||
|     cursor: col-resize; | ||||
|     border-left: 1px solid $primary-border-color; | ||||
|     box-shadow: -1px 0 6px rgba(0,0,0,0.1); | ||||
| } | ||||
| .editor-tray-resize-button { | ||||
|     @include workspace-button; | ||||
|     display: block; | ||||
|     height: 37px; | ||||
|     line-height: 35px; | ||||
|     border: none; | ||||
|     border-bottom: 1px solid $secondary-border-color; | ||||
|     margin: 0; | ||||
|     background: $background-color; | ||||
|     color: $workspace-button-color; | ||||
| } | ||||
| #editor-shade, #header-shade  { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     bottom:0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     background: $shade-color; | ||||
| } | ||||
|  | ||||
|  | ||||
| .dialog-form,#dialog-form, #dialog-config-form { | ||||
|     margin: 0; | ||||
|     height: 100%; | ||||
| } | ||||
| @@ -23,10 +160,6 @@ | ||||
|     border-color: rgb(214, 97, 95) !important; | ||||
| } | ||||
|  | ||||
| .ui-dialog .ui-dialog-buttonpane button.leftButton { | ||||
|     margin-right: 40px; | ||||
| } | ||||
|  | ||||
| .form-row { | ||||
|     clear: both; | ||||
|     color: $form-text-color; | ||||
| @@ -83,6 +216,7 @@ | ||||
|     cursor: auto; | ||||
|     float: right; | ||||
|     font-size: 12px !important; | ||||
|     line-height: 35px; | ||||
| } | ||||
| #node-config-dialog-scope-warning { | ||||
|     display: inline-block; | ||||
| @@ -94,13 +228,14 @@ | ||||
|     margin: 1px 0 0 0; | ||||
|     padding: 0; | ||||
|     height: 22px; | ||||
|     width: 110px; | ||||
|     width: 150px; | ||||
|  | ||||
| } | ||||
| #node-config-dialog-user-count { | ||||
|     vertical-align: middle; | ||||
|     display:inline-block; | ||||
|     margin-top: 10px; | ||||
|     margin-right: 20px; | ||||
|     float:left; | ||||
|     font-size: 12px; | ||||
|     line-height: 35px; | ||||
| } | ||||
|   | ||||
| @@ -73,6 +73,32 @@ | ||||
| .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { | ||||
|     float: none; | ||||
| } | ||||
|  | ||||
| .ui-dialog-buttonset button { | ||||
|     @include workspace-button; | ||||
|     font-size: 14px; | ||||
|     padding: 6px 14px; | ||||
|     margin-right: 8px; | ||||
|     color: $editor-button-color-primary; | ||||
|     &.leftButton { | ||||
|         float: left; | ||||
|         margin-top: 7px; | ||||
|     } | ||||
|     &:not(.leftButton):not(:last-child) { | ||||
|         margin-right: 16px; | ||||
|     } | ||||
|     &:not(.primary) { | ||||
|         background: none; | ||||
|  | ||||
|         &:hover { | ||||
|             color: $editor-button-color-primary; | ||||
|         } | ||||
|     } | ||||
|     .ui-button-text { | ||||
|         padding: 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .ui-dialog .ui-dialog-buttonpane { | ||||
|     padding: .3em 1em .5em 1em; | ||||
| } | ||||
|   | ||||
| @@ -62,10 +62,13 @@ | ||||
|         background: $workspace-button-background-active; | ||||
|         cursor: default; | ||||
|     } | ||||
|  | ||||
|     .button-group &:not(:first-child) { | ||||
|         border-left: none; | ||||
|     } | ||||
|  | ||||
|     &:focus { | ||||
|         outline: 1px solid $workspace-button-color-focus-outline; | ||||
|     } | ||||
| } | ||||
| @mixin workspace-button-toggle { | ||||
|     @include workspace-button; | ||||
|   | ||||
| @@ -22,6 +22,7 @@ | ||||
|     width: 315px; | ||||
|     background: #fff; | ||||
|     box-sizing: border-box; | ||||
|     z-index: 10; | ||||
|     @include component-border; | ||||
| } | ||||
|  | ||||
| @@ -46,13 +47,15 @@ | ||||
|     right: 315px; | ||||
|     bottom:10px; | ||||
|     width: 7px; | ||||
|     background: url(images/grip.png) no-repeat 50% 50%; | ||||
|     z-index: 11; | ||||
|     background: $background-color url(images/grip.png) no-repeat 50% 50%; | ||||
|     cursor: col-resize; | ||||
| } | ||||
|  | ||||
| .sidebar-closed > #sidebar { display: none; } | ||||
| .sidebar-closed > #sidebar-separator { right: 0px !important; } | ||||
| .sidebar-closed > #workspace { right: 7px !important; } | ||||
| .sidebar-closed > #editor-stack { right: 8px !important; } | ||||
|  | ||||
| #sidebar .button { | ||||
|     @include workspace-button; | ||||
|   | ||||
| @@ -52,7 +52,7 @@ body { | ||||
|     font-size: 14px; | ||||
|     font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; | ||||
|     padding-top: 100px; | ||||
|     background: #f3f3f3; | ||||
|     background: $background-color; | ||||
| } | ||||
|  | ||||
| #main-container { | ||||
|   | ||||
| @@ -15,11 +15,21 @@ | ||||
|  **/ | ||||
|  | ||||
| .sidebar-node-config { | ||||
|     position: relative; | ||||
|     background: #f3f3f3; | ||||
|     height: 100%; | ||||
|     overflow-y:auto; | ||||
|     @include disable-selection; | ||||
| } | ||||
| .sidebar-node-config-shade { | ||||
|     position: absolute; | ||||
|     top:0; | ||||
|     bottom:0; | ||||
|     left:0; | ||||
|     right:0; | ||||
|     background: $shade-color; | ||||
| } | ||||
|  | ||||
|  | ||||
|  .config-node-list { | ||||
|      margin: 0; | ||||
|   | ||||
| @@ -104,6 +104,7 @@ ul.red-ui-tabs li.active { | ||||
|     background: $tab-background-active; | ||||
|     font-weight: bold; | ||||
|     border-bottom: 1px solid #fff; | ||||
|     z-index: 2; | ||||
| } | ||||
| ul.red-ui-tabs li.active a { | ||||
|     color: #333; | ||||
|   | ||||
| @@ -50,6 +50,8 @@ | ||||
|     margin-right: 35px; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| #workspace-add-tab { | ||||
|     position: absolute; | ||||
|     box-sizing: border-box; | ||||
|   | ||||
| @@ -39,6 +39,7 @@ | ||||
|     <ul class="header-toolbar hide"> | ||||
|         <li><a id="btn-sidemenu" class="button" data-toggle="dropdown" href="#"><i class="fa fa-bars"></i></a></li> | ||||
|     </ul> | ||||
|     <div id="header-shade" class="hide"></div> | ||||
| </div> | ||||
| <div id="main-container" class="sidebar-closed hide"> | ||||
|     <div id="palette"> | ||||
| @@ -65,6 +66,8 @@ | ||||
|             <a class="workspace-footer-button" id="btn-zoom-in" href="#"><i class="fa fa-plus"></i></a> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div id="editor-shade" class="hide"></div> | ||||
|     <div id="editor-stack"></div> | ||||
|     <div id="sidebar"> | ||||
|         <ul id="sidebar-tabs"></ul> | ||||
|         <div id="sidebar-content"></div> | ||||
| @@ -78,24 +81,6 @@ | ||||
| <div id="notifications"></div> | ||||
| <div id="dropTarget"><div data-i18n="[append]workspace.dropFlowHere"><br/><i class="fa fa-download"></i></div></div> | ||||
|  | ||||
| <div id="dialog" class="hide"><form id="dialog-form" class="form-horizontal"></form></div> | ||||
| <div id="node-config-dialog" class="hide"><form id="dialog-config-form" class="form-horizontal"><div id="node-config-dialog-edit-form"></div><!--<div id="node-config-dialog-toolbar" class="form-row"><label><span>Node scope</span></label><select id="node-config-dialog-scope"></select></div>--></form></div> | ||||
| <div id="subflow-dialog" class="hide"> | ||||
|     <form class="form-horizontal"> | ||||
|         <div class="form-row"> | ||||
|             <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"> | ||||
|         </div> | ||||
|         <div class="form-row" style="margin-bottom: 0px;"> | ||||
|             <label for="subflow-input-info" data-i18n="subflow.info"></label> | ||||
|             <a href="https://help.github.com/articles/markdown-basics/" style="font-size: 0.8em; float: right;" data-i18n="[html]subflow.format"></a> | ||||
|         </div> | ||||
|         <div class="form-row node-text-editor-row"> | ||||
|             <div style="height: 250px;" class="node-text-editor" id="subflow-input-info-editor"></div> | ||||
|         </div> | ||||
|         <div class="form-row form-tips" id="subflow-dialog-user-count"></div> | ||||
|     </form> | ||||
| </div> | ||||
|  | ||||
| <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"> | ||||
| @@ -166,6 +151,20 @@ | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="subflow-template"> | ||||
|     <div class="form-row"> | ||||
|         <label for="subflow-input-name" data-i18n="common.label.name"></label><input type="text" id="subflow-input-name"> | ||||
|     </div> | ||||
|     <div class="form-row" style="margin-bottom: 0px;"> | ||||
|         <label for="subflow-input-info" data-i18n="subflow.info"></label> | ||||
|         <a href="https://help.github.com/articles/markdown-basics/" style="font-size: 0.8em; float: right;" data-i18n="[html]subflow.format"></a> | ||||
|     </div> | ||||
|     <div class="form-row node-text-editor-row"> | ||||
|         <div style="height: 250px;" class="node-text-editor" id="subflow-input-info-editor"></div> | ||||
|     </div> | ||||
|     <div class="form-row form-tips" id="subflow-dialog-user-count"></div> | ||||
| </script> | ||||
|  | ||||
| <script src="vendor/vendor.js"></script> | ||||
| <script src="vendor/ace/ace.js"></script> | ||||
| <script src="vendor/ace/ext-language_tools.js"></script> | ||||
|   | ||||
							
								
								
									
										73
									
								
								nodes/core/io/05-tls.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								nodes/core/io/05-tls.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| <!-- | ||||
|   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. | ||||
| --> | ||||
|  | ||||
| <script type="text/x-red" data-template-name="tls-config"> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: 120px;" for="node-config-input-cert"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.cert"></span></label> | ||||
|         <input style="width: 60%;" type="text" id="node-config-input-cert" data-i18n="[placeholder]tls.placeholder.cert"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: 120px;" for="node-config-input-key"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.key"></span></label> | ||||
|         <input style="width: 60%;" type="text" id="node-config-input-key" data-i18n="[placeholder]tls.placeholder.key"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: 120px;" for="node-config-input-ca"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.ca"></span></label> | ||||
|         <input style="width: 60%;" type="text" id="node-config-input-ca" data-i18n="[placeholder]tls.placeholder.ca"> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="tls.label.verify-server-cert"></label> | ||||
|     </div> | ||||
|     <div class="form-row"> | ||||
|         <label style="width: 120px;" for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label> | ||||
|         <input style="width: 60%;" type="text" id="node-config-input-name" data-i18n="[placeholder]common.label.name"> | ||||
|     </div> | ||||
| </script> | ||||
|  | ||||
| <script type="text/x-red" data-help-name="tls-config"> | ||||
|     <p>Configuration options for TLS connections.</p> | ||||
| </script> | ||||
|  | ||||
| <script type="text/javascript"> | ||||
|     RED.nodes.registerType('tls-config',{ | ||||
|         category: 'config', | ||||
|         defaults: { | ||||
|             name: {value:""}, | ||||
|             cert: {value:"", validate: function(v) { | ||||
|                 var currentKey = $("#node-config-input-key").val(); | ||||
|                 if (currentKey === undefined) { | ||||
|                     currentKey = this.key; | ||||
|                 } | ||||
|                 return currentKey === '' || v != ''; | ||||
|             }}, | ||||
|             key: {value:"", validate: function(v) { | ||||
|                 var currentCert = $("#node-config-input-cert").val(); | ||||
|                 if (currentCert === undefined) { | ||||
|                     currentCert = this.cert; | ||||
|                 } | ||||
|                 return currentCert === '' || v != ''; | ||||
|             }}, | ||||
|             ca: {value:""}, | ||||
|             verifyservercert: {value: true} | ||||
|         }, | ||||
|         label: function() { | ||||
|             return this.name || this._("tls.tls"); | ||||
|         }, | ||||
|         labelStyle: function() { | ||||
|             return this.name?"node_label_italic":""; | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
							
								
								
									
										69
									
								
								nodes/core/io/05-tls.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								nodes/core/io/05-tls.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /** | ||||
|  * 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. | ||||
|  **/ | ||||
|  | ||||
| var fs = require('fs'); | ||||
| module.exports = function(RED) { | ||||
|     "use strict"; | ||||
|  | ||||
|     function TLSConfig(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         this.valid = true; | ||||
|         var certPath = n.cert.trim(); | ||||
|         var keyPath = n.key.trim(); | ||||
|         var caPath = n.ca.trim(); | ||||
|  | ||||
|         if ( (certPath.length > 0) !== (keyPath.length > 0)) { | ||||
|             this.valid = false; | ||||
|             this.error(RED._("tls.error.missing-file")); | ||||
|             return; | ||||
|         } | ||||
|         this.verifyservercert = n.verifyservercert; | ||||
|  | ||||
|         try { | ||||
|             if (certPath) { | ||||
|                 this.cert = fs.readFileSync(certPath); | ||||
|             } | ||||
|             if (keyPath) { | ||||
|                 this.key = fs.readFileSync(keyPath); | ||||
|             } | ||||
|             if (caPath) { | ||||
|                 this.ca = fs.readFileSync(caPath); | ||||
|             } | ||||
|         } catch(err) { | ||||
|             this.valid = false; | ||||
|             this.error(err.toString()); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     RED.nodes.registerType("tls-config",TLSConfig); | ||||
|  | ||||
|     TLSConfig.prototype.addTLSOptions = function(opts) { | ||||
|         if (this.valid) { | ||||
|             if (this.key) { | ||||
|                 opts.key = this.key; | ||||
|             } | ||||
|             if (this.cert) { | ||||
|                 opts.cert = this.cert; | ||||
|             } | ||||
|             if (this.ca) { | ||||
|                 opts.ca = this.ca; | ||||
|             } | ||||
|             opts.rejectUnauthorized = this.verifyservercert; | ||||
|         } | ||||
|         return opts; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -150,11 +150,17 @@ | ||||
|                 <label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> <span data-i18n="mqtt.label.port"></span></label> | ||||
|                 <input type="text" id="node-config-input-port" data-i18n="[placeholder]mqtt.label.port" style="width:45px"> | ||||
|             </div> | ||||
|             <div class="form-row"> | ||||
|                 <input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|                 <label for="node-config-input-usetls" style="width: auto" data-i18n="mqtt.label.use-tls"></label> | ||||
|                 <div id="node-config-row-tls" class="hide"> | ||||
|                     <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-config-input-tls"><span data-i18n="mqtt.label.tls-config"></span></label><input style="width: 300px;" type="text" id="node-config-input-tls"> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="form-row"> | ||||
|                 <label for="node-config-input-clientid"><i class="fa fa-tag"></i> <span data-i18n="mqtt.label.clientid"></span></label> | ||||
|                 <input type="text" id="node-config-input-clientid" data-i18n="[placeholder]mqtt.placeholder.clientid"> | ||||
|             </div> | ||||
|  | ||||
|             <div class="form-row"> | ||||
|                 <label for="node-config-input-keepalive" style="width: auto"><i class="fa fa-clock-o"></i> <span data-i18n="mqtt.label.keepalive"></span></label> | ||||
|                 <input type="text" id="node-config-input-keepalive" style="width: 50px"> | ||||
| @@ -175,14 +181,6 @@ | ||||
|                 <label for="node-config-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label> | ||||
|                 <input type="password" id="node-config-input-password"> | ||||
|             </div> | ||||
|             <div class="form-row"> | ||||
|                 <input type="checkbox" id="node-config-input-usetls" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|                 <label for="node-config-input-usetls" style="width: 70%;" data-i18n="mqtt.label.use-tls"></label> | ||||
|             </div> | ||||
|             <div class="form-row"> | ||||
|                 <input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|                 <label for="node-config-input-verifyservercert" style="width: 70%;" data-i18n="mqtt.label.verify-server-cert"></label> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div id="mqtt-broker-tab-birth" style="display:none"> | ||||
|             <div class="form-row"> | ||||
| @@ -240,6 +238,7 @@ | ||||
|         defaults: { | ||||
|             broker: {value:"",required:true}, | ||||
|             port: {value:1883,required:true,validate:RED.validators.number()}, | ||||
|             tls: {type:"tls-config",required: false}, | ||||
|             clientid: { value:"", validate: function(v) { | ||||
|                 if ($("#node-config-input-clientid").length) { | ||||
|                     // Currently editing the node | ||||
| @@ -303,10 +302,6 @@ | ||||
|                 this.usetls = false; | ||||
|                 $("#node-config-input-usetls").prop("checked",false); | ||||
|             } | ||||
|             if (typeof this.verifyservercert  === 'undefined'){ | ||||
|                 this.verifyservercert = true; | ||||
|                 $("#node-config-input-verifyservercert").prop("checked",true); | ||||
|             } | ||||
|             if (typeof this.compatmode  === 'undefined'){ | ||||
|                 this.compatmode = true; | ||||
|                 $("#node-config-input-compatmode").prop('checked', true); | ||||
| @@ -326,11 +321,9 @@ | ||||
|  | ||||
|             function updateTLSOptions() { | ||||
|                 if ($("#node-config-input-usetls").is(':checked')) { | ||||
|                     $("#node-config-input-verifyservercert").prop("disabled", false); | ||||
|                     $("#node-config-input-verifyservercert").next().css("color",""); | ||||
|                     $("#node-config-row-tls").show(); | ||||
|                 } else { | ||||
|                     $("#node-config-input-verifyservercert").prop("disabled", true); | ||||
|                     $("#node-config-input-verifyservercert").next().css("color","#aaa"); | ||||
|                     $("#node-config-row-tls").hide(); | ||||
|                 } | ||||
|             } | ||||
|             updateTLSOptions(); | ||||
| @@ -350,6 +343,11 @@ | ||||
|             $("#node-config-input-cleansession").on("click",function() { | ||||
|                 updateClientId(); | ||||
|             }); | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-config-input-usetls").is(':checked')) { | ||||
|                 $("#node-config-input-tls").val(""); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -114,8 +114,18 @@ module.exports = function(RED) { | ||||
|             this.options.protocolId = 'MQIsdp'; | ||||
|             this.options.protocolVersion = 3; | ||||
|         } | ||||
|  | ||||
|         this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true) | ||||
|         if (this.usetls && n.tls) { | ||||
|             var tlsNode = RED.nodes.getNode(n.tls); | ||||
|             if (tlsNode) { | ||||
|                 tlsNode.addTLSOptions(this.options); | ||||
|             } | ||||
|         } | ||||
|         // If there's no rejectUnauthorized already, then this could be an | ||||
|         // old config where this option was provided on the broker node and | ||||
|         // not the tls node | ||||
|         if (typeof this.options.rejectUnauthorized === 'undefined') { | ||||
|             this.options.rejectUnauthorized = (this.verifyservercert == "true" || this.verifyservercert === true); | ||||
|         } | ||||
|  | ||||
|         if (n.willTopic) { | ||||
|             this.options.will = { | ||||
| @@ -284,6 +294,9 @@ module.exports = function(RED) { | ||||
|                     done(); | ||||
|                 }); | ||||
|                 this.client.end(); | ||||
|             } if (this.connecting) { | ||||
|                 node.client.end(); | ||||
|                 done(); | ||||
|             } else { | ||||
|                 done(); | ||||
|             } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <!-- | ||||
|   Copyright 2013, 2015 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. | ||||
| @@ -29,19 +29,31 @@ | ||||
|         <label for="node-input-url"><i class="fa fa-globe"></i> <span data-i18n="httpin.label.url"></span></label> | ||||
|         <input type="text" id="node-input-url" placeholder="http://"> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-input-usetls" style="width: auto" data-i18n="httpin.use-tls"></label> | ||||
|         <div id="node-row-tls" class="hide"> | ||||
|             <label style="width: auto; margin-left: 20px; margin-right: 10px;" for="node-input-tls"><span data-i18n="httpin.tls-config"></span></label><input type="text" style="width: 300px" id="node-input-tls"> | ||||
|         </div> | ||||
|     </div> | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label> </label> | ||||
|         <input type="checkbox" id="node-input-useAuth" style="display: inline-block; width: auto; vertical-align: top;"> | ||||
|         <label for="node-input-useAuth" style="width: 70%;"><span data-i18n="httpin.basicauth"></span></label> | ||||
|         <div style="margin-left: 20px" class="node-input-useAuth-row hide"> | ||||
|             <div class="form-row"> | ||||
|                 <label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label> | ||||
|                 <input type="text" id="node-input-user"> | ||||
|             </div> | ||||
|             <div class="form-row"> | ||||
|                 <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label> | ||||
|                 <input type="password" id="node-input-password"> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div class="form-row node-input-useAuth-row"> | ||||
|         <label for="node-input-user"><i class="fa fa-user"></i> <span data-i18n="common.label.username"></span></label> | ||||
|         <input type="text" id="node-input-user"> | ||||
|     </div> | ||||
|     <div class="form-row node-input-useAuth-row"> | ||||
|         <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="common.label.password"></span></label> | ||||
|         <input type="password" id="node-input-password"> | ||||
|     </div> | ||||
|  | ||||
|  | ||||
|     <div class="form-row"> | ||||
|         <label for="node-input-ret"><i class="fa fa-arrow-left"></i> <span data-i18n="httpin.label.return"></span></label> | ||||
|         <select type="text" id="node-input-ret" style="width:72%;"> | ||||
| @@ -91,8 +103,7 @@ | ||||
|             method:{value:"GET"}, | ||||
|             ret: {value:"txt"}, | ||||
|             url:{value:""}, | ||||
|             //user -> credentials | ||||
|             //pass -> credentials | ||||
|             tls: {type:"tls-config",required: false} | ||||
|         }, | ||||
|         credentials: { | ||||
|             user: {type:"text"}, | ||||
| @@ -108,14 +119,6 @@ | ||||
|             return this.name?"node_label_italic":""; | ||||
|         }, | ||||
|         oneditprepare: function() { | ||||
|             if (this.credentials.user || this.credentials.has_password) { | ||||
|                 $('#node-input-useAuth').prop('checked', true); | ||||
|                 $(".node-input-useAuth-row").show(); | ||||
|             } else { | ||||
|                 $('#node-input-useAuth').prop('checked', false); | ||||
|                 $(".node-input-useAuth-row").hide(); | ||||
|             } | ||||
|  | ||||
|             $("#node-input-useAuth").change(function() { | ||||
|                 if ($(this).is(":checked")) { | ||||
|                     $(".node-input-useAuth-row").show(); | ||||
| @@ -125,7 +128,29 @@ | ||||
|                     $('#node-input-password').val(''); | ||||
|                 } | ||||
|             }); | ||||
|             if (this.credentials.user || this.credentials.has_password) { | ||||
|                 $('#node-input-useAuth').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-useAuth').prop('checked', false); | ||||
|             } | ||||
|             $("#node-input-useAuth").change(); | ||||
|  | ||||
|             function updateTLSOptions() { | ||||
|                 if ($("#node-input-usetls").is(':checked')) { | ||||
|                     $("#node-row-tls").show(); | ||||
|                 } else { | ||||
|                     $("#node-row-tls").hide(); | ||||
|                 } | ||||
|             } | ||||
|             if (this.tls) { | ||||
|                 $('#node-input-usetls').prop('checked', true); | ||||
|             } else { | ||||
|                 $('#node-input-usetls').prop('checked', false); | ||||
|             } | ||||
|             updateTLSOptions(); | ||||
|             $("#node-input-usetls").on("click",function() { | ||||
|                 updateTLSOptions(); | ||||
|             }); | ||||
|             $("#node-input-ret").change(function() { | ||||
|                 if ($("#node-input-ret").val() === "obj") { | ||||
|                     $("#tip-json").show(); | ||||
| @@ -133,6 +158,11 @@ | ||||
|                     $("#tip-json").hide(); | ||||
|                 } | ||||
|             }); | ||||
|         }, | ||||
|         oneditsave: function() { | ||||
|             if (!$("#node-input-usetls").is(':checked')) { | ||||
|                 $("#node-input-tls").val("_ADD_"); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| </script> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| /** | ||||
|  * Copyright 2013, 2015 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. | ||||
| @@ -24,13 +24,16 @@ module.exports = function(RED) { | ||||
|  | ||||
|     function HTTPRequest(n) { | ||||
|         RED.nodes.createNode(this,n); | ||||
|         var node = this; | ||||
|         var nodeUrl = n.url; | ||||
|         var isTemplatedUrl = (nodeUrl||"").indexOf("{{") != -1; | ||||
|         var nodeMethod = n.method || "GET"; | ||||
|         if (n.tls) { | ||||
|             var tlsNode = RED.nodes.getNode(n.tls); | ||||
|         } | ||||
|         this.ret = n.ret || "txt"; | ||||
|         if (RED.settings.httpRequestTimeout) { this.reqTimeout = parseInt(RED.settings.httpRequestTimeout) || 120000; } | ||||
|         else { this.reqTimeout = 120000; } | ||||
|         var node = this; | ||||
|  | ||||
|         var prox, noprox; | ||||
|         if (process.env.http_proxy != null) { prox = process.env.http_proxy; } | ||||
| @@ -54,7 +57,11 @@ module.exports = function(RED) { | ||||
|             } | ||||
|             // url must start http:// or https:// so assume http:// if not set | ||||
|             if (!((url.indexOf("http://") === 0) || (url.indexOf("https://") === 0))) { | ||||
|                 url = "http://"+url; | ||||
|                 if (tlsNode) { | ||||
|                     url = "https://"+url; | ||||
|                 } else { | ||||
|                     url = "http://"+url; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var method = nodeMethod.toUpperCase() || "GET"; | ||||
| @@ -133,6 +140,9 @@ module.exports = function(RED) { | ||||
|                 } | ||||
|                 else { node.warn("Bad proxy url: "+process.env.http_proxy); } | ||||
|             } | ||||
|             if (tlsNode) { | ||||
|                 tlsNode.addTLSOptions(opts); | ||||
|             } | ||||
|             var req = ((/^https/.test(urltotest))?https:http).request(opts,function(res) { | ||||
|                 (node.ret === "bin") ? res.setEncoding('binary') : res.setEncoding('utf8'); | ||||
|                 msg.statusCode = res.statusCode; | ||||
| @@ -172,6 +182,7 @@ module.exports = function(RED) { | ||||
|                 req.abort(); | ||||
|             }); | ||||
|             req.on('error',function(err) { | ||||
|                 node.error(err,msg); | ||||
|                 msg.payload = err.toString() + " : " + url; | ||||
|                 msg.statusCode = err.code; | ||||
|                 node.send(msg); | ||||
|   | ||||
| @@ -123,6 +123,23 @@ | ||||
|             "event": "Event name" | ||||
|         } | ||||
|     }, | ||||
|     "tls": { | ||||
|         "tls": "TLS configuration", | ||||
|         "label": { | ||||
|             "cert": "Certificate", | ||||
|             "key": "Private Key", | ||||
|             "ca": "CA Certificate", | ||||
|             "verify-server-cert":"Verify server certificate" | ||||
|         }, | ||||
|         "placeholder": { | ||||
|             "cert":"path to certificate (PEM format)", | ||||
|             "key":"path to private key (PEM format)", | ||||
|             "ca":"path to CA certificate (PEM format)" | ||||
|         }, | ||||
|         "error": { | ||||
|             "missing-file": "No certificate/key file provided" | ||||
|         } | ||||
|     }, | ||||
|     "exec": { | ||||
|         "label": { | ||||
|             "command": "Command", | ||||
| @@ -240,6 +257,7 @@ | ||||
|             "keepalive": "Keep alive time (s)", | ||||
|             "cleansession": "Use clean session", | ||||
|             "use-tls": "Enable secure (SSL/TLS) connection", | ||||
|             "tls-config":"TLS Configuration", | ||||
|             "verify-server-cert":"Verify server certificate", | ||||
|             "compatmode": "Use legacy MQTT 3.1 support" | ||||
|         }, | ||||
| @@ -279,7 +297,9 @@ | ||||
|             "return": "Return" | ||||
|         }, | ||||
|         "setby": "- set by msg.method -", | ||||
|         "basicauth": "Use basic authentication?", | ||||
|         "basicauth": "Use basic authentication", | ||||
|         "use-tls": "Enable secure (SSL/TLS) connection", | ||||
|         "tls-config":"TLS Configuration", | ||||
|         "utf8": "a UTF-8 string", | ||||
|         "binary": "a binary buffer", | ||||
|         "json": "a parsed JSON object", | ||||
|   | ||||
| @@ -3,9 +3,14 @@ | ||||
|         "label": { | ||||
|             "name": "Name", | ||||
|             "ok": "Ok", | ||||
|             "done":"Done", | ||||
|             "cancel": "Cancel", | ||||
|             "delete": "Delete", | ||||
|             "close": "Close" | ||||
|             "close": "Close", | ||||
|             "load": "Load", | ||||
|             "save": "Save", | ||||
|             "import": "Import", | ||||
|             "export": "Export" | ||||
|         } | ||||
|     }, | ||||
|     "workspace": { | ||||
| @@ -105,10 +110,10 @@ | ||||
|         } | ||||
|     }, | ||||
|     "subflow": { | ||||
|         "editSubflow": "Edit flow __name__", | ||||
|         "edit": "Edit flow", | ||||
|         "subflowInstances": "There is __count__ instance of this subflow", | ||||
|         "subflowInstances_plural": "There are __count__ instances of this subflow", | ||||
|         "editSubflow": "Edit flow template: __name__", | ||||
|         "edit": "Edit flow template", | ||||
|         "subflowInstances": "There is __count__ instance of this subflow template", | ||||
|         "subflowInstances_plural": "There are __count__ instances of this subflow template", | ||||
|         "editSubflowProperties": "edit properties", | ||||
|         "input": "inputs:", | ||||
|         "output": "outputs:", | ||||
| @@ -121,12 +126,14 @@ | ||||
|         } | ||||
|     }, | ||||
|     "editor": { | ||||
|         "configEdit": "edit", | ||||
|         "configAdd": "add", | ||||
|         "configEdit": "Edit", | ||||
|         "configAdd": "Add", | ||||
|         "configUpdate": "Update", | ||||
|         "configDelete": "Delete", | ||||
|         "nodesUse": "__count__ node uses this config", | ||||
|         "nodesUse_plural": "__count__ nodes use this config", | ||||
|         "addNewConfig": "Add new __type__ config node", | ||||
|         "editNode": "Edit __type__ node", | ||||
|         "editConfig": "Edit __type__ config node", | ||||
|         "addNewType": "Add new __type__...", | ||||
|         "errors": { | ||||
| @@ -193,7 +200,7 @@ | ||||
|     }, | ||||
|     "sidebar": { | ||||
|         "info": { | ||||
|             "name": "Information", | ||||
|             "name": "Node information", | ||||
|             "label": "info", | ||||
|             "node": "Node", | ||||
|             "type": "Type", | ||||
|   | ||||
| @@ -36,10 +36,32 @@ function Flow(global,flow) { | ||||
|         var id; | ||||
|         catchNodeMap = {}; | ||||
|         statusNodeMap = {}; | ||||
|         for (id in flow.configs) { | ||||
|             if (flow.configs.hasOwnProperty(id)) { | ||||
|                 node = flow.configs[id]; | ||||
|                 if (!activeNodes[id]) { | ||||
|  | ||||
|         var configNodes = Object.keys(flow.configs); | ||||
|         var configNodeAttempts = {}; | ||||
|         while(configNodes.length > 0) { | ||||
|             id = configNodes.shift(); | ||||
|             node = flow.configs[id]; | ||||
|             if (!activeNodes[id]) { | ||||
|                 var readyToCreate = true; | ||||
|                 // This node doesn't exist. | ||||
|                 // Check it doesn't reference another non-existent config node | ||||
|                 for (var prop in node) { | ||||
|                     if (node.hasOwnProperty(prop) && prop !== 'id' && prop !== 'wires' && prop !== '_users' && flow.configs[node[prop]]) { | ||||
|                         if (!activeNodes[node[prop]]) { | ||||
|                             // References a non-existent config node | ||||
|                             // Add it to the back of the list to try again later | ||||
|                             configNodes.push(id); | ||||
|                             configNodeAttempts[id] = (configNodeAttempts[id]||0)+1; | ||||
|                             if (configNodeAttempts[id] === 100) { | ||||
|                                 throw new Error("Circular config node dependency detected: "+id); | ||||
|                             } | ||||
|                             readyToCreate = false; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (readyToCreate) { | ||||
|                     newNode = createNode(node.type,node); | ||||
|                     if (newNode) { | ||||
|                         activeNodes[id] = newNode; | ||||
| @@ -47,6 +69,7 @@ function Flow(global,flow) { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (diff && diff.rewired) { | ||||
|             for (var j=0;j<diff.rewired.length;j++) { | ||||
|                 var rewireNode = activeNodes[diff.rewired[j]]; | ||||
|   | ||||
| @@ -230,22 +230,32 @@ module.exports = { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (id in newConfig.allNodes) { | ||||
|             if (newConfig.allNodes.hasOwnProperty(id)) { | ||||
|                 node = newConfig.allNodes[id]; | ||||
|                 for (var prop in node) { | ||||
|                     if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") { | ||||
|                         // This node has a property that references a changed/removed node | ||||
|                         // Assume it is a config node change and mark this node as | ||||
|                         // changed. | ||||
|                         if (changed[node[prop]] || removed[node[prop]]) { | ||||
|                             if (!changed[node.id]) { | ||||
|                                 changed[node.id] = node; | ||||
|                                 if (newConfig.allNodes[node.z]) { | ||||
|                                     changed[node.z] = newConfig.allNodes[node.z]; | ||||
|                                     if (changed[node.z].type === "subflow") { | ||||
|                                         changedSubflows[node.z] = changed[node.z]; | ||||
|                                         delete changed[node.id]; | ||||
|         var madeChange; | ||||
|         // Loop through the nodes looking for references to changed config nodes | ||||
|         // Repeat the loop if anything is marked as changed as it may need to be | ||||
|         // propagated to parent nodes. | ||||
|         // TODO: looping through all nodes every time is a bit inefficient - could be more targeted | ||||
|         do { | ||||
|             madeChange = false; | ||||
|             for (id in newConfig.allNodes) { | ||||
|                 if (newConfig.allNodes.hasOwnProperty(id)) { | ||||
|                     node = newConfig.allNodes[id]; | ||||
|                     for (var prop in node) { | ||||
|                         if (node.hasOwnProperty(prop) && prop != "z" && prop != "id" && prop != "wires") { | ||||
|                             // This node has a property that references a changed/removed node | ||||
|                             // Assume it is a config node change and mark this node as | ||||
|                             // changed. | ||||
|                             if (changed[node[prop]] || removed[node[prop]]) { | ||||
|                                 if (!changed[node.id]) { | ||||
|                                     madeChange = true; | ||||
|                                     changed[node.id] = node; | ||||
|                                     // This node exists within subflow template | ||||
|                                     // Mark the template as having changed | ||||
|                                     if (newConfig.allNodes[node.z]) { | ||||
|                                         changed[node.z] = newConfig.allNodes[node.z]; | ||||
|                                         if (changed[node.z].type === "subflow") { | ||||
|                                             changedSubflows[node.z] = changed[node.z]; | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
| @@ -253,8 +263,18 @@ module.exports = { | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         } while(madeChange===true) | ||||
|  | ||||
|         // Find any nodes that exist on a subflow template and remove from changed | ||||
|         // list as the parent subflow will now be marked as containing a change | ||||
|         for (id in newConfig.allNodes) { | ||||
|             if (newConfig.allNodes.hasOwnProperty(id)) { | ||||
|                 node = newConfig.allNodes[id]; | ||||
|                 if (newConfig.allNodes[node.z] && newConfig.allNodes[node.z].type === "subflow") { | ||||
|                     delete changed[node.id]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Recursively mark all instances of changed subflows as changed | ||||
|         var changedSubflowStack = Object.keys(changedSubflows); | ||||
|   | ||||
| @@ -45,7 +45,7 @@ describe('Flow', function() { | ||||
|  | ||||
|     var TestNode = function(n) { | ||||
|         Node.call(this,n); | ||||
|         createCount++; | ||||
|         this._index = createCount++; | ||||
|         this.scope = n.scope; | ||||
|         var node = this; | ||||
|         this.foo = n.foo; | ||||
| @@ -173,6 +173,61 @@ describe('Flow', function() { | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         it("instantiates config nodes in the right order",function(done) { | ||||
|             var config = flowUtils.parseConfig([ | ||||
|                 {id:"t1",type:"tab"}, | ||||
|                 {id:"1",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["2"]}, | ||||
|                 {id:"2",x:10,y:10,z:"t1",type:"test",foo:"a",wires:["3"]}, | ||||
|                 {id:"3",x:10,y:10,z:"t1",type:"test",foo:"a",wires:[]}, | ||||
|                 {id:"4",z:"t1",type:"test",foo:"5"}, // This node depends on #5 | ||||
|                 {id:"5",z:"t1",type:"test"} | ||||
|             ]); | ||||
|             var flow = Flow.create(config,config.flows["t1"]); | ||||
|             flow.start(); | ||||
|  | ||||
|             Object.keys(flow.getActiveNodes()).should.have.length(5); | ||||
|  | ||||
|  | ||||
|             currentNodes.should.have.a.property("1"); | ||||
|             currentNodes.should.have.a.property("2"); | ||||
|             currentNodes.should.have.a.property("3"); | ||||
|             currentNodes.should.have.a.property("4"); | ||||
|             currentNodes.should.have.a.property("5"); | ||||
|  | ||||
|             currentNodes["1"].should.have.a.property("_index",2); | ||||
|             currentNodes["2"].should.have.a.property("_index",3); | ||||
|             currentNodes["3"].should.have.a.property("_index",4); | ||||
|             currentNodes["4"].should.have.a.property("_index",1); | ||||
|             currentNodes["5"].should.have.a.property("_index",0); | ||||
|  | ||||
|             flow.stop().then(function() { | ||||
|                 currentNodes.should.not.have.a.property("1"); | ||||
|                 currentNodes.should.not.have.a.property("2"); | ||||
|                 currentNodes.should.not.have.a.property("3"); | ||||
|                 currentNodes.should.not.have.a.property("4"); | ||||
|                 currentNodes.should.not.have.a.property("5"); | ||||
|                 stoppedNodes.should.have.a.property("1"); | ||||
|                 stoppedNodes.should.have.a.property("2"); | ||||
|                 stoppedNodes.should.have.a.property("3"); | ||||
|                 stoppedNodes.should.have.a.property("4"); | ||||
|                 stoppedNodes.should.have.a.property("5"); | ||||
|                 done(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|  | ||||
|         it("detects dependency loops in config nodes",function() { | ||||
|             var config = flowUtils.parseConfig([ | ||||
|                 {id:"t1",type:"tab"}, | ||||
|                 {id:"node1",z:"t1",type:"test",foo:"node2"}, // This node depends on #5 | ||||
|                 {id:"node2",z:"t1",type:"test",foo:"node1"} | ||||
|             ]); | ||||
|             var flow = Flow.create(config,config.flows["t1"]); | ||||
|             /*jshint immed: false */ | ||||
|             (function(){ | ||||
|                 flow.start(); | ||||
|             }).should.throw("Circular config node dependency detected: node1"); | ||||
|         }); | ||||
|         it("instantiates a subflow and stops it",function(done) { | ||||
|             var config = flowUtils.parseConfig([ | ||||
|                 {id:"t1",type:"tab"}, | ||||
|   | ||||
| @@ -308,7 +308,7 @@ describe('flows/util', function() { | ||||
|             diffResult.linked.sort().should.eql(["3"]); | ||||
|         }); | ||||
|  | ||||
|         it('identifies config nodes changes', function() { | ||||
|         it('identifies config nodes changes, node->config', function() { | ||||
|             var config = [ | ||||
|                 {id:"1",type:"test",foo:"configNode",wires:[["2"]]}, | ||||
|                 {id:"2",type:"test",bar:"b",wires:[["3"]]}, | ||||
| @@ -329,7 +329,30 @@ describe('flows/util', function() { | ||||
|             diffResult.removed.should.have.length(0); | ||||
|             diffResult.rewired.should.have.length(0); | ||||
|             diffResult.linked.sort().should.eql(["2","3"]); | ||||
|         }); | ||||
|  | ||||
|         it('identifies config nodes changes, node->config->config', function() { | ||||
|             var config = [ | ||||
|                 {id:"1",type:"test",foo:"configNode1",wires:[["2"]]}, | ||||
|                 {id:"2",type:"test",bar:"b",wires:[["3"]]}, | ||||
|                 {id:"3",type:"test",foo:"a",wires:[]}, | ||||
|                 {id:"configNode1",foo:"configNode2",type:"testConfig"}, | ||||
|                 {id:"configNode2",type:"testConfig"} | ||||
|             ]; | ||||
|             var newConfig = clone(config); | ||||
|             newConfig[4].foo = "bar"; | ||||
|  | ||||
|             var originalConfig = flowUtil.parseConfig(config); | ||||
|             var changedConfig = flowUtil.parseConfig(newConfig); | ||||
|  | ||||
|             originalConfig.missingTypes.should.have.length(0); | ||||
|  | ||||
|             var diffResult = flowUtil.diffConfigs(originalConfig,changedConfig); | ||||
|             diffResult.added.should.have.length(0); | ||||
|             diffResult.changed.sort().should.eql(["1","configNode1","configNode2"]); | ||||
|             diffResult.removed.should.have.length(0); | ||||
|             diffResult.rewired.should.have.length(0); | ||||
|             diffResult.linked.sort().should.eql(["2","3"]); | ||||
|         }); | ||||
|  | ||||
|         it('marks a parent subflow as changed for an internal property change', function() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user