Merge pull request #51 from node-red/tabs

Add tabbed workspace
This commit is contained in:
Nick O'Leary 2013-10-30 09:58:16 -07:00
commit e02189d092
8 changed files with 603 additions and 89 deletions

View File

@ -49,6 +49,18 @@
</ul>
</li>
<li class="divider"></li>
<li class="dropdown-submenu pull-left"><a tabindex="-1" href="#"><i class="icon-th-large"></i> Workspaces</a>
<ul class="dropdown-menu">
<li><a id="btn-workspace-add" tabindex="-1" href="#"><i class="icon-plus"></i> Add</a></li>
<li><a id="btn-workspace-edit" tabindex="-1" href="#"><i class="icon-edit"></i> Rename</a></li>
<li><a id="btn-workspace-delete" tabindex="-1" href="#"><i class="icon-minus"></i> Delete</a></li>
<li class="dropdown-submenu pull-left">
<a tabindex="-1" href="#"><i class="icon-share-alt"></i> Switch to...</a>
<ul id="workspace-menu-list" class="dropdown-menu"></ul>
</li>
</ul>
</li>
<li class="divider"></li>
<li><a id="btn-keyboard-shortcuts" tabindex="-1" href="#"><i class="icon-question-sign"></i> Keyboard Shortcuts</a></li>
<li><a id="btn-help" tabindex="-1" href="http://node-red.github.io/docs" target="_blank"><i class="icon-question-sign"></i> Help...</a></li>
</ul>
@ -108,7 +120,11 @@
</div>
</div><!-- /palette -->
<div id="chart"></div>
<div id="workspace">
<ul id="workspace-tabs"></ul>
<div id="workspace-add-tab"><a id="btn-workspace-add-tab" href="#"><i class="icon-plus"></i></a></div>
<div id="chart"></div>
</div>
<div id="chart-zoom-controls">
<div class="btn-group">
@ -119,12 +135,8 @@
</div>
<div id="sidebar">
<ul id="sidebar-tabs">
<li><a href="#tab-info">info</a></li>
</ul>
<div id="sidebar-content">
<div id="tab-info" style="position: relative;"></div>
</div>
<ul id="sidebar-tabs"></ul>
<div id="sidebar-content"></div>
</div>
<div id="sidebar-separator"></div>
@ -231,6 +243,21 @@
</div>
</form>
</div>
<div id="node-dialog-rename-workspace" class="hide">
<form class="form-horizontal">
<div class="form-row">
<label for="node-input-workspace-name" ><i class="icon-tag"></i> Name:</label>
<input type="text" id="node-input-workspace-name">
</div>
</form>
</div>
<div id="node-dialog-delete-workspace" class="hide">
<form class="form-horizontal">
<div style="text-align: center; padding-top: 30px;">
Are you sure you want to delete '<span id="node-dialog-delete-workspace-name"></span>'?
</div>
</form>
</div>
<script type="text/x-red" data-template-name="export-clipboard-dialog">
<div class="form-row">
@ -265,6 +292,7 @@
<script src="red/history.js"></script>
<script src="red/validators.js"></script>
<script src="red/ui/keyboard.js"></script>
<script src="red/ui/tabs.js"></script>
<script src="red/ui/view.js"></script>
<script src="red/ui/sidebar.js"></script>
<script src="red/ui/palette.js"></script>

View File

@ -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]);
}

View File

@ -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;i<n.outputs;i++) {
node.wires.push([]);
@ -166,6 +197,9 @@ RED.nodes = function() {
//TODO: rename this (createCompleteNodeSet)
function createCompleteNodeSet() {
var nns = [];
for (var i in workspaces) {
nns.push(workspaces[i]);
}
for (var i in configNodes) {
nns.push(convertNode(configNodes[i]));
}
@ -193,12 +227,27 @@ RED.nodes = function() {
}
for (var i in newNodes) {
var n = newNodes[i];
if (!getType(n.type)) {
if (n.type != "workspace" && !getType(n.type)) {
// TODO: get this UI thing out of here! (see below as well)
RED.notify("<strong>Failed to import nodes</strong>: 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
};

View File

@ -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 = '<a href="#tab-'+title+'">'+title+'</a>';
$("#sidebar-tabs").append(tab);
$("#sidebar-content").append(content);
$('#sidebar').tabs("refresh");
}
return {
addTab: addTab

103
public/red/ui/tabs.js Normal file
View File

@ -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 = $("<li/>",{class:"red-ui-tab"}).appendTo(ul);
var link = $("<a/>",{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
}
}();

View File

@ -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 = $("<li/>");
var menuA = $("<a/>",{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();
}
};
}();

View File

@ -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;
}

View File

@ -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;