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