diff --git a/public/index.html b/public/index.html index c71f717ce..5f5ba484c 100644 --- a/public/index.html +++ b/public/index.html @@ -49,6 +49,18 @@
  • + +
  • Keyboard Shortcuts
  • Help...
  • @@ -108,7 +120,11 @@ -
    +
    + +
    +
    +
    @@ -119,12 +135,8 @@
    @@ -231,6 +243,21 @@
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + Are you sure you want to delete ''? +
    +
    +
    + diff --git a/public/red/history.js b/public/red/history.js index cc91adacc..281c064c3 100644 --- a/public/red/history.js +++ b/public/red/history.js @@ -36,7 +36,15 @@ RED.history = function() { for (var i in ev.links) { RED.nodes.removeLink(ev.links[i]); } + for (var i in ev.workspaces) { + RED.nodes.removeWorkspace(ev.workspaces[i].id); + RED.view.removeWorkspace(ev.workspaces[i]); + } } else if (ev.t == "delete") { + for (var i in ev.workspaces) { + RED.nodes.addWorkspace(ev.workspaces[i]); + RED.view.addWorkspace(ev.workspaces[i]); + } for (var i in ev.nodes) { RED.nodes.add(ev.nodes[i]); } diff --git a/public/red/nodes.js b/public/red/nodes.js index 222092a6b..1209a13e7 100644 --- a/public/red/nodes.js +++ b/public/red/nodes.js @@ -21,12 +21,20 @@ RED.nodes = function() { var configNodes = {}; var links = []; + var defaultWorkspace; + var workspaces = {}; + + function registerType(nt,def) { node_defs[nt] = def; // TODO: too tightly coupled into palette UI RED.palette.add(nt,def); } + function getID() { + return (1+Math.random()*4294967295).toString(16); + } + function getType(type) { return node_defs[type]; } @@ -87,6 +95,28 @@ RED.nodes = function() { } } + function addWorkspace(ws) { + workspaces[ws.id] = ws; + } + function getWorkspace(id) { + return workspaces[id]; + } + function removeWorkspace(id) { + delete workspaces[id]; + var removedNodes = []; + var removedLinks = []; + for (var n in nodes) { + var node = nodes[n]; + if (node.z == id) { + removedNodes.push(node); + } + } + for (var n in removedNodes) { + var rmlinks = removeNode(removedNodes[n].id); + removedLinks = removedLinks.concat(rmlinks); + } + return {nodes:removedNodes,links:removedLinks}; + } function getAllFlowNodes(node) { var visited = {}; visited[node.id] = true; @@ -120,7 +150,8 @@ RED.nodes = function() { if (n._def.category != "config") { node.x = n.x; node.y = n.y; - + node.z = n.z; + node.wires = []; for(var i=0;iFailed to import nodes: unrecognised type '"+n.type+"'","error"); return null; } } + for (var i in newNodes) { + var n = newNodes[i]; + if (n.type === "workspace") { + if (defaultWorkspace == null) { + defaultWorkspace = n; + } + addWorkspace(n); + RED.view.addWorkspace(n); + } + } + if (defaultWorkspace == null) { + defaultWorkspace = { type:"workspace", id:getID(), label:"Workspace 1" }; + addWorkspace(defaultWorkspace); + RED.view.addWorkspace(defaultWorkspace); + } var node_map = {}; var new_nodes = []; @@ -206,51 +255,57 @@ RED.nodes = function() { for (var i in newNodes) { var n = newNodes[i]; - var def = getType(n.type); - if (def && def.category == "config") { - if (!RED.nodes.node(n.id)) { - var configNode = {id:n.id,type:n.type,users:[]}; - for (var d in def.defaults) { - configNode[d] = n[d]; + if (n.type !== "workspace") { + var def = getType(n.type); + if (def && def.category == "config") { + if (!RED.nodes.node(n.id)) { + var configNode = {id:n.id,type:n.type,users:[]}; + for (var d in def.defaults) { + configNode[d] = n[d]; + } + configNode.label = def.label; + configNode._def = def; + RED.nodes.add(configNode); } - configNode.label = def.label; - configNode._def = def; - RED.nodes.add(configNode); - } - } else { - var node = {x:n.x,y:n.y,type:0,wires:n.wires}; - if (createNewIds) { - node.id = (1+Math.random()*4294967295).toString(16); } else { - node.id = n.id; - } - node.type = n.type; - node._def = def; - if (!node._def) { - node._def = { - color:"#fee", - defaults: {}, - label: "unknown: "+n.type, - labelStyle: "node_label_italic", - outputs: n.outputs||n.wires.length - } - } - node.outputs = n.outputs||node._def.outputs; - - for (var d in node._def.defaults) { - node[d] = n[d]; - if (node._def.defaults[d].type) { - var configNode = RED.nodes.node(n[d]); - if (configNode) { - configNode.users.push(node); + var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires}; + if (createNewIds) { + node.z = RED.view.getWorkspace(); + node.id = getID(); + } else { + node.id = n.id; + if (node.z == null || !workspaces[node.z]) { + node.z = RED.view.getWorkspace(); } } + node.type = n.type; + node._def = def; + if (!node._def) { + node._def = { + color:"#fee", + defaults: {}, + label: "unknown: "+n.type, + labelStyle: "node_label_italic", + outputs: n.outputs||n.wires.length + } + } + node.outputs = n.outputs||node._def.outputs; + + for (var d in node._def.defaults) { + node[d] = n[d]; + if (node._def.defaults[d].type) { + var configNode = RED.nodes.node(n[d]); + if (configNode) { + configNode.users.push(node); + } + } + } + + addNode(node); + RED.editor.validateNode(node); + node_map[n.id] = node; + new_nodes.push(node); } - - addNode(node); - RED.editor.validateNode(node); - node_map[n.id] = node; - new_nodes.push(node); } } for (var i in new_nodes) { @@ -284,6 +339,9 @@ RED.nodes = function() { addLink: addLink, remove: removeNode, removeLink: removeLink, + addWorkspace: addWorkspace, + removeWorkspace: removeWorkspace, + workspace: getWorkspace, eachNode: function(cb) { for (var n in nodes) { cb(nodes[n]); @@ -305,6 +363,7 @@ RED.nodes = function() { getAllFlowNodes: getAllFlowNodes, createExportableNodeSet: createExportableNodeSet, createCompleteNodeSet: createCompleteNodeSet, + id: getID, nodes: nodes, // TODO: exposed for d3 vis links: links // TODO: exposed for d3 vis }; diff --git a/public/red/ui/sidebar.js b/public/red/ui/sidebar.js index c322a19da..1474baf14 100644 --- a/public/red/ui/sidebar.js +++ b/public/red/ui/sidebar.js @@ -15,7 +15,29 @@ **/ RED.sidebar = function() { - $('#sidebar').tabs(); + //$('#sidebar').tabs(); + var sidebar_tabs = RED.tabs.create({ + id:"sidebar-tabs", + onchange:function(id) { + $("#sidebar-content").children().hide(); + $("#"+id).show(); + } + }); + function addTab(title,content) { + $("#sidebar-content").append(content); + $(content).hide(); + sidebar_tabs.addTab({id:"tab-"+title,label:title}); + //content.style.position = "absolute"; + //$('#sidebar').tabs("refresh"); + } + + var content = document.createElement("div"); + content.id = "tab-info"; + content.style.paddingTop = "4px"; + content.style.paddingLeft = "4px"; + content.style.paddingRight = "4px"; + + addTab("info",content); $('#btn-sidebar').click(function() {toggleSidebar();}); RED.keyboard.add(/* SPACE */ 32,{ctrl:true},function(){toggleSidebar();d3.event.preventDefault();}); @@ -28,8 +50,8 @@ RED.sidebar = function() { var winWidth = $(window).width(); sidebarSeparator.start = ui.position.left; sidebarSeparator.width = $("#sidebar").width(); - sidebarSeparator.chartWidth = $("#chart").width(); - sidebarSeparator.chartRight = winWidth-$("#chart").width()-$("#chart").offset().left-2; + sidebarSeparator.chartWidth = $("#workspace").width(); + sidebarSeparator.chartRight = winWidth-$("#workspace").width()-$("#workspace").offset().left-2; sidebarSeparator.closing = false; }, drag: function(event,ui) { @@ -38,7 +60,7 @@ RED.sidebar = function() { if (newSidebarWidth > 180 && sidebarSeparator.chartWidth+d > 200) { var newChartRight = sidebarSeparator.chartRight-d; - $("#chart").css("right",newChartRight); + $("#workspace").css("right",newChartRight); $("#chart-zoom-controls").css("right",newChartRight+20); $("#sidebar").width(newSidebarWidth); } @@ -50,6 +72,8 @@ RED.sidebar = function() { sidebarSeparator.closing = false; $("#sidebar").removeClass("closing"); } + sidebar_tabs.resize(); + RED.view.resize(); }, stop:function(event,ui) { @@ -63,9 +87,9 @@ RED.sidebar = function() { }); function toggleSidebar() { - if ($('#sidebar').tabs( "option", "active" ) === false) { - $('#sidebar').tabs( "option", "active",0); - } + //if ($('#sidebar').tabs( "option", "active" ) === false) { + // $('#sidebar').tabs( "option", "active",0); + //} var btnSidebar = $("#btn-sidebar"); btnSidebar.toggleClass("active"); @@ -77,15 +101,6 @@ RED.sidebar = function() { } toggleSidebar(); - function addTab(title,content) { - var tab = document.createElement("li"); - tab.innerHTML = ''+title+''; - $("#sidebar-tabs").append(tab); - $("#sidebar-content").append(content); - - $('#sidebar').tabs("refresh"); - - } return { addTab: addTab diff --git a/public/red/ui/tabs.js b/public/red/ui/tabs.js new file mode 100644 index 000000000..c2a41ab47 --- /dev/null +++ b/public/red/ui/tabs.js @@ -0,0 +1,103 @@ +/** + * Copyright 2013 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + + + +RED.tabs = function() { + + + function createTabs(options) { + var ul = $("#"+options.id) + ul.addClass("red-ui-tabs"); + ul.children().first().addClass("active"); + ul.children().addClass("red-ui-tab"); + + function onTabClick() { + activateTab($(this)); + return false; + } + + function onTabDblClick() { + if (options.ondblclick) { + options.ondblclick($(this).attr('href').slice(1)); + } + } + + function activateTab(link) { + if (typeof link === "string") { + link = ul.find("a[href='#"+link+"']"); + } + if (!link.parent().hasClass("active")) { + ul.children().removeClass("active"); + link.parent().addClass("active"); + if (options.onchange) { + options.onchange(link.attr('href').slice(1)); + } + } + + } + function updateTabWidths() { + var tabs = ul.find("li.red-ui-tab"); + var width = ul.width(); + var tabCount = tabs.size(); + var tabWidth = (width-6-(tabCount*7))/tabCount; + var pct = 100*tabWidth/width; + tabs.css({width:pct+"%"}); + + } + ul.find("li.red-ui-tab a").on("click",onTabClick).on("dblclick",onTabDblClick); + updateTabWidths(); + + return { + addTab: function(tab) { + var li = $("
  • ",{class:"red-ui-tab"}).appendTo(ul); + var link = $("",{href:"#"+tab.id}).appendTo(li); + link.html(tab.label); + link.on("click",onTabClick); + link.on("dblclick",onTabDblClick); + updateTabWidths(); + if (options.onadd) { + options.onadd(tab); + } + link.attr("title",tab.label); + if (ul.find("li.red-ui-tab").size() == 1) { + activateTab(link); + } + }, + removeTab: function(id) { + var li = ul.find("a[href='#"+id+"']").parent(); + if (li.hasClass("active")) { + var tab = li.prev(); + if (tab.size() == 0) { + tab = li.next(); + } + activateTab(tab.find("a")); + } + li.remove(); + + }, + activateTab: activateTab, + resize: updateTabWidths, + count: function() { + return ul.find("li.red-ui-tab").size(); + } + } + } + + return { + create: createTabs + } +}(); diff --git a/public/red/ui/view.js b/public/red/ui/view.js index a753ac93c..9f9a430cb 100644 --- a/public/red/ui/view.js +++ b/public/red/ui/view.js @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ + + RED.view = function() { var space_width = 5000, space_height = 5000, @@ -21,6 +23,9 @@ RED.view = function() { node_width = 100, node_height = 30; + var activeWorkspace = 0; + var workspaceScrollPositions = {}; + var selected_link = null, mousedown_link = null, mousedown_node = null, @@ -63,6 +68,62 @@ RED.view = function() { var drag_line = vis.append("svg:path").attr("class", "drag_line"); + var workspace_tabs = RED.tabs.create({ + id: "workspace-tabs", + onchange: function(id) { + RED.view.setWorkspace(id); + }, + ondblclick: function(id) { + showRenameWorkspaceDialog(id); + }, + onadd: function(tab) { + var menuli = $("
  • "); + var menuA = $("",{tabindex:"-1",href:"#"+tab.id}).appendTo(menuli); + menuA.html(tab.label); + menuA.on("click",function() { + workspace_tabs.activateTab(tab.id); + }); + + $('#workspace-menu-list').append(menuli); + } + }); + + var workspaceIndex = 0; + + function addWorkspace() { + var tabId = RED.nodes.id(); + do { + workspaceIndex += 1; + } while($("#workspace-tabs a[title='Workspace "+workspaceIndex+"']").size() != 0); + + var ws = {type:"workspace",id:tabId,label:"Workspace "+workspaceIndex}; + RED.nodes.addWorkspace(ws); + workspace_tabs.addTab(ws); + workspace_tabs.activateTab(tabId); + + RED.history.push({t:'add',workspaces:[ws],dirty:dirty}); + RED.view.dirty(true); + } + $('#btn-workspace-add-tab').on("click",addWorkspace); + $('#btn-workspace-add').on("click",addWorkspace); + $('#btn-workspace-edit').on("click",function() { + showRenameWorkspaceDialog(activeWorkspace); + }); + $('#btn-workspace-delete').on("click",function() { + deleteWorkspace(activeWorkspace); + }); + + + function deleteWorkspace(id) { + if (workspace_tabs.count() == 1) { + return; + } + var ws = RED.nodes.workspace(id); + $( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws); + $( "#node-dialog-delete-workspace-name" ).text(ws.label); + $( "#node-dialog-delete-workspace" ).dialog('open'); + } + //d3.select(window).on("keydown", keydown); function canvasMouseDown() { @@ -214,7 +275,7 @@ RED.view = function() { clearSelection(); } RED.nodes.eachNode(function(n) { - if (!n.selected) { + if (n.z == activeWorkspace && !n.selected) { n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2); if (n.selected) { n.dirty = true; @@ -281,7 +342,7 @@ RED.view = function() { mousePos[1] /= scaleFactor; mousePos[0] /= scaleFactor; - var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width}; + var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:activeWorkspace}; nn.type = selected_tool; nn._def = RED.nodes.getType(nn.type); @@ -323,11 +384,13 @@ RED.view = function() { function selectAll() { RED.nodes.eachNode(function(n) { + if (n.z == activeWorkspace) { if (!n.selected) { n.selected = true; n.dirty = true; moving_set.push({n:n}); } + } }); selected_link = null; updateSelection(); @@ -587,7 +650,7 @@ RED.view = function() { if (mouse_mode != RED.state.JOINING) { // Don't bother redrawing nodes if we're drawing links - var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes,function(d){return d.id}); + var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace }),function(d){return d.id}); node.exit().remove(); var nodeEnter = node.enter().insert("svg:g").attr("class", "node nodegroup"); @@ -811,7 +874,7 @@ RED.view = function() { }); } - var link = vis.selectAll(".link").data(RED.nodes.links); + var link = vis.selectAll(".link").data(RED.nodes.links.filter(function(d) { return d.source.z == activeWorkspace && d.target.z == activeWorkspace })); link.enter().insert("svg:path",".node").attr("class","link") .on("mousedown",function(d) { @@ -905,7 +968,7 @@ RED.view = function() { if (result) { var new_nodes = result[0]; var new_links = result[1]; - var new_ms = new_nodes.map(function(n) { return {n:n};}); + var new_ms = new_nodes.map(function(n) { n.z = activeWorkspace; return {n:n};}); var new_node_ids = new_nodes.map(function(n){ return n.id; }); // TODO: pick a more sensible root node @@ -981,6 +1044,94 @@ RED.view = function() { $("#node-input-import").val(""); $( "#dialog" ).dialog("option","title","Import nodes").dialog( "open" ); } + + function showRenameWorkspaceDialog(id) { + var ws = RED.nodes.workspace(id); + $( "#node-dialog-rename-workspace" ).dialog("option","workspace",ws); + + if (workspace_tabs.count() == 1) { + $( "#node-dialog-rename-workspace").next().find(".leftButton") + .prop('disabled',true) + .addClass("ui-state-disabled"); + } else { + $( "#node-dialog-rename-workspace").next().find(".leftButton") + .prop('disabled',false) + .removeClass("ui-state-disabled"); + } + + $( "#node-input-workspace-name" ).val(ws.label); + $( "#node-dialog-rename-workspace" ).dialog("open"); + } + + + $("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();}); + $( "#node-dialog-rename-workspace" ).dialog({ + modal: true, + autoOpen: false, + width: 500, + title: "Rename workspace", + buttons: [ + { + class: 'leftButton', + text: "Delete", + click: function() { + var workspace = $(this).dialog('option','workspace'); + $( this ).dialog( "close" ); + deleteWorkspace(workspace.id); + } + }, + { + text: "Ok", + click: function() { + var workspace = $(this).dialog('option','workspace'); + var label = $( "#node-input-workspace-name" ).val(); + if (workspace.label != label) { + workspace.label = label; + var link = $("#workspace-tabs a[href='#"+workspace.id+"']"); + link.attr("title",label); + link.text(label); + RED.view.dirty(true); + } + $( this ).dialog( "close" ); + } + }, + { + text: "Cancel", + click: function() { + $( this ).dialog( "close" ); + } + } + ] + }); + $( "#node-dialog-delete-workspace" ).dialog({ + modal: true, + autoOpen: false, + width: 500, + title: "Confirm delete", + buttons: [ + { + text: "Ok", + click: function() { + var workspace = $(this).dialog('option','workspace'); + RED.view.removeWorkspace(workspace); + var historyEvent = RED.nodes.removeWorkspace(workspace.id); + historyEvent.t = 'delete'; + historyEvent.dirty = dirty; + historyEvent.workspaces = [workspace]; + RED.history.push(historyEvent); + RED.view.dirty(true); + $( this ).dialog( "close" ); + } + }, + { + text: "Cancel", + click: function() { + $( this ).dialog( "close" ); + } + } + ] + }); + return { state:function(state) { @@ -990,6 +1141,59 @@ RED.view = function() { mouse_mode = state; } }, + addWorkspace: function(ws) { + workspace_tabs.addTab(ws); + workspace_tabs.resize(); + if (workspace_tabs.count() == 1) { + $('#btn-workspace-delete').parent().addClass("disabled"); + } else { + $('#btn-workspace-delete').parent().removeClass("disabled"); + } + }, + removeWorkspace: function(ws) { + workspace_tabs.removeTab(ws.id); + $('#workspace-menu-list a[href="#'+ws.id+'"]').parent().remove(); + if (workspace_tabs.count() == 1) { + $('#btn-workspace-delete').parent().addClass("disabled"); + } else { + $('#btn-workspace-delete').parent().removeClass("disabled"); + } + }, + getWorkspace: function() { + return activeWorkspace; + }, + setWorkspace: function(z) { + var chart = $("#chart"); + if (activeWorkspace != 0) { + workspaceScrollPositions[activeWorkspace] = { + left:chart.scrollLeft(), + top:chart.scrollTop() + }; + } + var scrollStartLeft = chart.scrollLeft(); + var scrollStartTop = chart.scrollTop(); + + activeWorkspace = z; + if (workspaceScrollPositions[activeWorkspace]) { + chart.scrollLeft(workspaceScrollPositions[activeWorkspace].left); + chart.scrollTop(workspaceScrollPositions[activeWorkspace].top); + } else { + chart.scrollLeft(0); + chart.scrollTop(0); + } + var scrollDeltaLeft = chart.scrollLeft() - scrollStartLeft; + var scrollDeltaTop = chart.scrollTop() - scrollStartTop; + if (mouse_position != null) { + mouse_position[0] += scrollDeltaLeft; + mouse_position[1] += scrollDeltaTop; + } + + clearSelection(); + RED.nodes.eachNode(function(n) { + n.dirty = true; + }); + redraw(); + }, redraw:redraw, dirty: function(d) { if (d == null) { @@ -998,6 +1202,9 @@ RED.view = function() { setDirty(d); } }, - importNodes: importNodes + importNodes: importNodes, + resize: function() { + workspace_tabs.resize(); + } }; }(); diff --git a/public/style.css b/public/style.css index 789229f63..781dc64d4 100644 --- a/public/style.css +++ b/public/style.css @@ -40,10 +40,19 @@ a.brand img { padding-right: 10px; } -#chart { +#workspace { margin-left: 160px; + overflow: hidden; +} + +#chart { overflow: auto; background: #e3e3e3; + position: absolute; + bottom:0px; + top: 30px; + left:0px; + right:0px; } #chart-zoom-controls { padding-top: 3px; @@ -144,6 +153,7 @@ a.brand img { #sidebar { background: #fff; + box-sizing: border-box; } #sidebar.closing { background: #eee; @@ -161,7 +171,7 @@ a.brand img { position: absolute; top: 5px; left:10px; bottom: 10px; } -#chart { +#workspace { position: absolute; margin: 0; top:5px; left:160px; bottom: 10px; right: 330px; @@ -184,7 +194,7 @@ a.brand img { .sidebar-closed > #sidebar { display: none; } .sidebar-closed > #sidebar-separator { display: none; } -.sidebar-closed > #chart { right: 10px !important; } +.sidebar-closed > #workspace { right: 10px !important; } .sidebar-closed > #chart-zoom-controls { right: 30px !important; } /* ---------- end layout ---------- */ @@ -224,14 +234,15 @@ a.brand img { margin-left: 20px; } -#chart, #palette, #sidebar { +#workspace, #palette, #sidebar { border: 1px solid #000; border-radius: 3px; } #sidebar-content { + font-size: 1.2em; overflow-y: auto; position: absolute; - top: 50px; left: 5px; right: 0; bottom: 1px; + top: 30px; left: 0px; right: 0; bottom: 1px; } .node_label_italic { @@ -558,3 +569,82 @@ div.node-info { border-radius:5px; overflow: hidden; } +#workspace-tabs { + margin-right: 28px; +} +#workspace-add-tab { + position: absolute; + top: 0; + right: 0; + height: 29px; + width: 28px; + border-bottom: 1px solid #999; +} +#btn-workspace-add-tab { + display: inline-block; + width: 100%; + background: #e3e3e3; + height: 100%; + line-height: 28px; + text-align: center; +} +#btn-workspace-add-tab:hover { + background: #efefef; +} + +ul.red-ui-tabs { + list-style-type: none; + padding:5px 2px 0px 5px; + margin: 0; + display: block; + height: 24px; + border-bottom: 1px solid #999; + background: #e3e3e3; +} + +ul.red-ui-tabs li { + border-top-left-radius: 5px; + border-top-right-radius: 5px; + display: inline-block; + border-left: 1px solid #999; + border-top: 1px solid #999; + border-right: 1px solid #999; + border-bottom: 1px solid #999; + background: #e3e3e3; + margin: 0 5px 0 0; + height: 23px; + line-height: 17px; + max-width: 150px; + width: 14%; + overflow: hidden; + white-space: nowrap; +} + +ul.red-ui-tabs li a { + display: block; + padding: 3px 16px; + color: #666; +} +ul.red-ui-tabs li a:hover { + text-decoration: none; + background: #f0f0f0; +} + +ul.red-ui-tabs li.active { + background: #fff; + border-bottom: 1px solid #fff; +} +ul.red-ui-tabs li.active a { + color: #333; +} +ul.red-ui-tabs li.active a:hover { + background: #fff; +} +ul.red-ui-tabs li.red-ui-add-tab { + width: 25px; + border-top-right-radius: 15px; + line-height: 22px; +} +ul.red-ui-tabs li.red-ui-add-tab a { + padding: 2px 4px; +} diff --git a/red/nodes.js b/red/nodes.js index 5a2b560c3..ac55f0179 100644 --- a/red/nodes.js +++ b/red/nodes.js @@ -292,9 +292,11 @@ var parseConfig = function() { missingTypes = []; for (var i in activeConfig) { var type = activeConfig[i].type; - var nt = node_type_registry.get(type); - if (!nt && missingTypes.indexOf(type) == -1) { - missingTypes.push(type); + if (type != "workspace") { + var nt = node_type_registry.get(type); + if (!nt && missingTypes.indexOf(type) == -1) { + missingTypes.push(type); + } } }; if (missingTypes.length > 0) { @@ -309,19 +311,21 @@ var parseConfig = function() { events.emit("nodes-starting"); for (var i in activeConfig) { var nn = null; - var nt = node_type_registry.get(activeConfig[i].type); - if (nt) { - try { - nn = new nt(activeConfig[i]); + if (activeConfig[i].type != "workspace") { + var nt = node_type_registry.get(activeConfig[i].type); + if (nt) { + try { + nn = new nt(activeConfig[i]); + } + catch (err) { + util.log("[red] "+activeConfig[i].type+" : "+err); + } } - catch (err) { - util.log("[red] "+activeConfig[i].type+" : "+err); + // console.log(nn); + if (nn == null) { + util.log("[red] unknown type: "+activeConfig[i].type); } } - // console.log(nn); - if (nn == null) { - util.log("[red] unknown type: "+activeConfig[i].type); - } } // Clean up any orphaned credentials var deletedCredentials = false;