mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
commit
e02189d092
@ -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>
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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,6 +150,7 @@ 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++) {
|
||||
@ -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;
|
||||
|
||||
addNode(node);
|
||||
RED.editor.validateNode(node);
|
||||
node_map[n.id] = node;
|
||||
new_nodes.push(node);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
||||
|
@ -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
103
public/red/ui/tabs.js
Normal 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
|
||||
}
|
||||
}();
|
@ -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
|
||||
@ -982,6 +1045,94 @@ RED.view = function() {
|
||||
$( "#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) {
|
||||
if (state == null) {
|
||||
@ -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();
|
||||
}
|
||||
};
|
||||
}();
|
||||
|
100
public/style.css
100
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;
|
||||
}
|
||||
|
30
red/nodes.js
30
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;
|
||||
|
Loading…
Reference in New Issue
Block a user