Split cliboard and workspaces out of editor view

This commit is contained in:
Nick O'Leary 2015-03-12 23:38:37 +00:00
parent 7bdb3181e2
commit 4078212089
13 changed files with 512 additions and 336 deletions

View File

@ -211,27 +211,13 @@
</div>
</form>
</div>
<script type="text/x-red" data-template-name="export-clipboard-dialog">
<div class="form-row">
<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> Nodes:</label>
<textarea readonly style="font-family: monospace; font-size: 12px; background:rgb(226, 229, 255); padding-left: 0.5em;" class="input-block-level" id="node-input-export" rows="5"></textarea>
</div>
<div class="form-tips">
Select the text above and copy to the clipboard with Ctrl-A Ctrl-C.
</div>
</script>
<script type="text/x-red" data-template-name="export-library-dialog">
<div class="form-row">
<label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label>
<input type="text" id="node-input-filename" placeholder="Filename">
</div>
</script>
<script type="text/x-red" data-template-name="import-dialog">
<div class="form-row">
<label for="node-input-import"><i class="fa fa-clipboard"></i> Nodes:</label>
<textarea style="font-family: monospace; font-size: 12px; background:rgb(226, 229, 255); padding-left: 0.5em;" class="input-block-level" id="node-input-import" rows="5" placeholder="Paste nodes here"></textarea>
</div>
</script>
<script type="text/x-red" data-template-name="subflow">
<div class="form-row">
@ -260,12 +246,14 @@
<script src="red/ui/menu.js"></script>
<script src="red/ui/keyboard.js"></script>
<script src="red/ui/tabs.js"></script>
<script src="red/ui/workspaces.js"></script>
<script src="red/ui/view.js"></script>
<script src="red/ui/sidebar.js"></script>
<script src="red/ui/palette.js"></script>
<script src="red/ui/tab-info.js"></script>
<script src="red/ui/tab-config.js"></script>
<script src="red/ui/editor.js"></script>
<script src="red/ui/clipboard.js"></script>
<script src="red/ui/library.js"></script>
<script src="red/ui/notifications.js"></script>
<script src="red/ui/subflow.js"></script>

View File

@ -47,20 +47,20 @@ RED.history = (function() {
if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.removeWorkspace(ev.workspaces[i].id);
RED.view.removeWorkspace(ev.workspaces[i]);
RED.workspaces.remove(ev.workspaces[i]);
}
}
if (ev.subflows) {
for (i=0;i<ev.subflows.length;i++) {
RED.nodes.removeSubflow(ev.subflows[i]);
RED.view.removeWorkspace(ev.subflows[i]);
RED.workspaces.remove(ev.subflows[i]);
}
}
} else if (ev.t == "delete") {
if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.addWorkspace(ev.workspaces[i]);
RED.view.addWorkspace(ev.workspaces[i]);
RED.workspaces.add(ev.workspaces[i]);
}
}
if (ev.subflow) {
@ -183,7 +183,7 @@ RED.history = (function() {
}
RED.nodes.removeSubflow(ev.subflow);
RED.view.removeWorkspace(ev.subflow);
RED.workspaces.remove(ev.subflow);
if (ev.removedLinks) {
for (i=0;i<ev.removedLinks.length;i++) {

View File

@ -275,12 +275,12 @@ var RED = (function() {
{id:"btn-node-status",label:"Display node status",toggle:true,onselect:toggleStatus, selected: true},
null,
{id:"btn-import-menu",label:"Import",options:[
{id:"btn-import-clipboard",label:"Clipboard",onselect:RED.view.showImportNodesDialog},
{id:"btn-import-clipboard",label:"Clipboard",onselect:RED.clipboard.import},
{id:"btn-import-library",label:"Library",options:[]}
]},
{id:"btn-export-menu",label:"Export",disabled:true,options:[
{id:"btn-export-clipboard",label:"Clipboard",disabled:true,onselect:RED.view.showExportNodesDialog},
{id:"btn-export-library",label:"Library",disabled:true,onselect:RED.view.showExportNodesLibraryDialog}
{id:"btn-export-clipboard",label:"Clipboard",disabled:true,onselect:RED.clipboard.export},
{id:"btn-export-library",label:"Library",disabled:true,onselect:RED.library.export}
]},
null,
{id:"btn-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show},
@ -291,9 +291,9 @@ var RED = (function() {
]},
null,
{id:"btn-workspace-menu",label:"Workspaces",options:[
{id:"btn-workspace-add",label:"Add"},
{id:"btn-workspace-edit",label:"Rename"},
{id:"btn-workspace-delete",label:"Delete"},
{id:"btn-workspace-add",label:"Add",onselect:RED.workspaces.add},
{id:"btn-workspace-edit",label:"Rename",onselect:RED.workspaces.edit},
{id:"btn-workspace-delete",label:"Delete",onselect:RED.workspaces.remove},
null
]},
null,
@ -357,6 +357,8 @@ var RED = (function() {
RED.palette.init();
RED.sidebar.init();
RED.subflow.init();
RED.workspaces.init();
RED.clipboard.init();
RED.view.init();
RED.keyboard.add(/* ? */ 191,{shift:true},function(){showHelp();d3.event.preventDefault();});

View File

@ -429,16 +429,16 @@ RED.nodes = (function() {
var exportedConfigNodes = {};
var exportedSubflows = {};
for (var n=0;n<set.length;n++) {
var node = set[n].n;
var node = set[n];
if (node.type.substring(0,8) == "subflow:") {
var subflowId = node.type.substring(8);
if (!exportedSubflows[subflowId]) {
exportedSubflows[subflowId] = true;
var subflow = getSubflow(subflowId);
var subflowSet = [{n:subflow}];
var subflowSet = [subflow];
RED.nodes.eachNode(function(n) {
if (n.z == subflowId) {
subflowSet.push({n:n});
subflowSet.push(n);
}
});
var exportableSubflow = createExportableNodeSet(subflowSet);
@ -543,7 +543,7 @@ RED.nodes = (function() {
//"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
}
var activeWorkspace = RED.view.getWorkspace();
var activeWorkspace = RED.workspaces.active();
var activeSubflow = getSubflow(activeWorkspace);
if (activeSubflow) {
for (i=0;i<newNodes.length;i++) {
@ -589,7 +589,7 @@ RED.nodes = (function() {
n.id = nid;
}
addWorkspace(n);
RED.view.addWorkspace(n);
RED.workspaces.add(n);
new_workspaces.push(n);
} else if (n.type === "subflow") {
subflow_map[n.id] = n;
@ -634,9 +634,9 @@ RED.nodes = (function() {
if (defaultWorkspace == null) {
defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
addWorkspace(defaultWorkspace);
RED.view.addWorkspace(defaultWorkspace);
RED.workspaces.add(defaultWorkspace);
new_workspaces.push(defaultWorkspace);
activeWorkspace = RED.view.getWorkspace();
activeWorkspace = RED.workspaces.active();
}
var node_map = {};

147
public/red/ui/clipboard.js Normal file
View File

@ -0,0 +1,147 @@
/**
* Copyright 2015 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.clipboard = (function() {
var dialog = $('<div id="clipboard-dialog" class="hide"><form class="dialog-form form-horizontal"></form></div>')
.appendTo("body")
.dialog({
modal: true,
autoOpen: false,
width: 500,
resizable: false,
buttons: [
{
id: "clipboard-dialog-ok",
text: "Ok",
click: function() {
if (/Import/.test(dialog.dialog("option","title"))) {
RED.view.importNodes($("#clipboard-import").val());
}
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-cancel",
text: "Cancel",
click: function() {
$( this ).dialog( "close" );
}
},
{
id: "clipboard-dialog-close",
text: "Close",
click: function() {
$( this ).dialog( "close" );
}
}
],
open: function(e) {
$(this).parent().find(".ui-dialog-titlebar-close").hide();
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
}
});
var dialogContainer = dialog.children(".dialog-form");
var exportNodesDialog = '<div class="form-row">'+
'<label for="node-input-export" style="display: block; width:100%;"><i class="fa fa-clipboard"></i> Nodes:</label>'+
'<textarea readonly style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-export" rows="5"></textarea>'+
'</div>'+
'<div class="form-tips">'+
'Select the text above and copy to the clipboard with Ctrl-C.'+
'</div>';
var importNodesDialog = '<div class="form-row">'+
'<textarea style="resize: none; width: 100%; border-radius: 0px;font-family: monospace; font-size: 12px; background:#eee; padding-left: 0.5em; box-sizing:border-box;" id="clipboard-import" rows="5" placeholder="Paste nodes here"></textarea>'+
'</div>';
function importNodes() {
dialogContainer.empty();
dialogContainer.append($(importNodesDialog));
$("#clipboard-dialog-ok").show();
$("#clipboard-dialog-cancel").show();
$("#clipboard-dialog-close").hide();
$("#clipboard-dialog-ok").button("disable");
$("#clipboard-import").keyup(function() {
var v = $(this).val();
try {
JSON.parse(v);
$(this).removeClass("input-error");
$("#clipboard-dialog-ok").button("enable");
} catch(err) {
if (v !== "") {
$(this).addClass("input-error");
}
$("#clipboard-dialog-ok").button("disable");
}
});
dialog.dialog("option","title","Import nodes").dialog("open");
}
function exportNodes() {
dialogContainer.empty();
dialogContainer.append($(exportNodesDialog));
$("#clipboard-dialog-ok").hide();
$("#clipboard-dialog-cancel").hide();
$("#clipboard-dialog-close").show();
var selection = RED.view.selection();
if (selection.nodes) {
var nns = RED.nodes.createExportableNodeSet(selection.nodes);
$("#clipboard-export")
.val(JSON.stringify(nns))
.focus(function() {
var textarea = $(this);
textarea.select();
textarea.mouseup(function() {
textarea.unbind("mouseup");
return false;
})
});
dialog.dialog("option","title","Export nodes to clipboard").dialog( "open" );
}
}
return {
init: function() {
RED.view.on("selection-changed",function(selection) {
if (!selection.nodes) {
RED.menu.setDisabled("btn-export-menu",true);
RED.menu.setDisabled("btn-export-clipboard",true);
RED.menu.setDisabled("btn-export-library",true);
} else {
RED.menu.setDisabled("btn-export-menu",false);
RED.menu.setDisabled("btn-export-clipboard",false);
RED.menu.setDisabled("btn-export-library",false);
}
});
RED.keyboard.add(/* e */ 69,{ctrl:true},function(){exportNodes();d3.event.preventDefault();});
RED.keyboard.add(/* i */ 73,{ctrl:true},function(){importNodes();d3.event.preventDefault();});
},
import: importNodes,
export: exportNodes
}
})();

View File

@ -234,24 +234,20 @@ RED.editor = (function() {
editing_node.dirty = true;
validateNode(editing_node);
RED.view.redraw();
} else if (RED.view.state() == RED.state.EXPORT) {
if (/library/.test($( "#dialog" ).dialog("option","title"))) {
//TODO: move this to RED.library
var flowName = $("#node-input-filename").val();
if (!/^\s*$/.test(flowName)) {
$.ajax({
url:'library/flows/'+flowName,
type: "POST",
data: $("#node-input-filename").attr('nodes'),
contentType: "application/json; charset=utf-8"
}).done(function() {
RED.library.loadFlowLibrary();
RED.notify("Saved nodes","success");
});
}
} else if (/Export nodes to library/.test($( "#dialog" ).dialog("option","title"))) {
//TODO: move this to RED.library
var flowName = $("#node-input-filename").val();
if (!/^\s*$/.test(flowName)) {
$.ajax({
url:'library/flows/'+flowName,
type: "POST",
data: $("#node-input-filename").attr('nodes'),
contentType: "application/json; charset=utf-8"
}).done(function() {
RED.library.loadFlowLibrary();
RED.notify("Saved nodes","success");
});
}
} else if (RED.view.state() == RED.state.IMPORT) {
RED.view.importNodes($("#node-input-import").val());
}
$( this ).dialog( "close" );
}
@ -500,7 +496,7 @@ RED.editor = (function() {
class: 'leftButton',
text: "Edit flow",
click: function() {
RED.view.showSubflow(id);
RED.workspaces.show(id);
$("#node-dialog-ok").click();
}
});

View File

@ -380,6 +380,14 @@ RED.library = (function() {
}
function exportFlow() {
//TODO: don't rely on the main dialog
var nns = RED.nodes.createExportableNodeSet(RED.view.selection().nodes);
$("#dialog-form").html($("script[data-template-name='export-library-dialog']").html());
$("#node-input-filename").attr('nodes',JSON.stringify(nns));
$( "#dialog" ).dialog("option","title","Export nodes to library").dialog( "open" );
}
return {
init: function() {
RED.view.on("selection-changed",function(selection) {
@ -397,7 +405,9 @@ RED.library = (function() {
loadFlowLibrary();
},
create: createUI,
loadFlowLibrary: loadFlowLibrary
loadFlowLibrary: loadFlowLibrary,
export: exportFlow
}
})();

View File

@ -59,7 +59,6 @@ RED.sidebar = (function() {
$("#chart-zoom-controls").css("right",newChartRight+20);
$("#sidebar").width(0);
RED.menu.setSelected("btn-sidebar",true);
RED.view.resize();
eventHandler.emit("resize");
}
sidebarSeparator.width = $("#sidebar").width();
@ -100,11 +99,9 @@ RED.sidebar = (function() {
$("#sidebar").width(newSidebarWidth);
sidebar_tabs.resize();
RED.view.resize();
eventHandler.emit("resize");
},
stop:function(event,ui) {
RED.view.resize();
if (sidebarSeparator.closing) {
$("#sidebar").removeClass("closing");
RED.menu.setSelected("btn-sidebar",false);

View File

@ -18,7 +18,7 @@ RED.subflow = (function() {
function getSubflow() {
return RED.nodes.subflow(RED.view.getWorkspace());
return RED.nodes.subflow(RED.workspaces.active());
}
function findAvailableSubflowIOPosition(subflow) {
@ -39,7 +39,7 @@ RED.subflow = (function() {
}
function addSubflowInput() {
var subflow = RED.nodes.subflow(RED.view.getWorkspace());
var subflow = RED.nodes.subflow(RED.workspaces.active());
var position = findAvailableSubflowIOPosition(subflow);
var newInput = {
type:"subflow",
@ -79,7 +79,7 @@ RED.subflow = (function() {
}
function addSubflowOutput(id) {
var subflow = RED.nodes.subflow(RED.view.getWorkspace());
var subflow = RED.nodes.subflow(RED.workspaces.active());
var position = findAvailableSubflowIOPosition(subflow);
var newOutput = {
@ -120,7 +120,7 @@ RED.subflow = (function() {
function init() {
$("#workspace-subflow-edit").click(function(event) {
RED.editor.editSubflow(RED.nodes.subflow(RED.view.getWorkspace()));
RED.editor.editSubflow(RED.nodes.subflow(RED.workspaces.active()));
event.preventDefault();
});
$("#workspace-subflow-add-input").click(function(event) {
@ -170,7 +170,7 @@ RED.subflow = (function() {
dirty:startDirty
});
RED.view.removeWorkspace(activeSubflow);
RED.workspaces.remove(activeSubflow);
RED.view.dirty(true);
RED.view.redraw();
});
@ -210,7 +210,7 @@ RED.subflow = (function() {
subflow: subflow,
dirty:RED.view.dirty()
});
RED.view.showSubflow(subflowId);
RED.workspaces.show(subflowId);
}
function convertToSubflow() {
@ -328,7 +328,7 @@ RED.subflow = (function() {
type:"subflow:"+subflow.id,
x: center[0],
y: center[1],
z: RED.view.getWorkspace(),
z: RED.workspaces.active(),
inputs: subflow.in.length,
outputs: subflow.out.length,
h: Math.max(30/*node_height*/,(subflow.out.length||0) * 15),
@ -381,7 +381,7 @@ RED.subflow = (function() {
links:new_links,
subflow: subflow,
activeWorkspace: RED.view.getWorkspace(),
activeWorkspace: RED.workspaces.active(),
removedLinks: removedLinks,
dirty:RED.view.dirty()

View File

@ -148,7 +148,7 @@ RED.sidebar.info = (function() {
}
}
} else {
var subflow = RED.nodes.subflow(RED.view.getWorkspace());
var subflow = RED.nodes.subflow(RED.workspaces.active());
if (subflow) {
refresh(subflow);
} else {

View File

@ -29,14 +29,13 @@ RED.view = (function() {
moveTouchCenter = [],
touchStartTime = 0;
var workspaceScrollPositions = {};
var activeWorkspace = 0;
var activeSubflow = null;
var activeNodes = [];
var activeLinks = [];
var workspaceScrollPositions = {};
var selected_link = null,
mousedown_link = null,
mousedown_node = null,
@ -234,40 +233,38 @@ RED.view = (function() {
var drag_line = vis.append("svg:path").attr("class", "drag_line");
function updateActiveNodes() {
//TODO: remove direct access to RED.nodes.nodes
activeNodes = RED.nodes.nodes.filter(function(d) {
return d.z == activeWorkspace;
return d.z == RED.workspaces.active();
});
activeLinks = RED.nodes.links.filter(function(d) {
return d.source.z == activeWorkspace && d.target.z == activeWorkspace;
return d.source.z == RED.workspaces.active() && d.target.z == RED.workspaces.active();
})
}
var workspace_tabs = RED.tabs.create({
id: "workspace-tabs",
onchange: function(tab) {
if (tab.type == "subflow") {
$("#workspace-toolbar").show();
} else {
$("#workspace-toolbar").hide();
}
function init() {
RED.workspaces.on("change",function(event) {
var chart = $("#chart");
if (activeWorkspace !== 0) {
workspaceScrollPositions[activeWorkspace] = {
if (event.old !== 0) {
workspaceScrollPositions[event.old] = {
left:chart.scrollLeft(),
top:chart.scrollTop()
};
}
var scrollStartLeft = chart.scrollLeft();
var scrollStartTop = chart.scrollTop();
activeWorkspace = tab.id;
activeSubflow = RED.nodes.subflow(activeWorkspace);
activeSubflow = RED.nodes.subflow(event.workspace);
if (activeSubflow) {
$("#workspace-subflow-add-input").toggleClass("disabled",activeSubflow.in.length > 0);
}
if (workspaceScrollPositions[activeWorkspace]) {
chart.scrollLeft(workspaceScrollPositions[activeWorkspace].left);
chart.scrollTop(workspaceScrollPositions[activeWorkspace].top);
RED.menu.setDisabled("btn-workspace-edit", activeSubflow);
RED.menu.setDisabled("btn-workspace-delete",RED.workspaces.count() == 1 || activeSubflow);
if (workspaceScrollPositions[event.workspace]) {
chart.scrollLeft(workspaceScrollPositions[event.workspace].left);
chart.scrollTop(workspaceScrollPositions[event.workspace].top);
} else {
chart.scrollLeft(0);
chart.scrollTop(0);
@ -278,10 +275,6 @@ RED.view = (function() {
mouse_position[0] += scrollDeltaLeft;
mouse_position[1] += scrollDeltaTop;
}
RED.menu.setDisabled("btn-workspace-edit", activeSubflow);
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1 || activeSubflow);
clearSelection();
RED.nodes.eachNode(function(n) {
n.dirty = true;
@ -289,69 +282,8 @@ RED.view = (function() {
updateSelection();
updateActiveNodes();
redraw();
},
ondblclick: function(tab) {
if (tab.type != "subflow") {
showRenameWorkspaceDialog(tab.id);
} else {
RED.editor.editSubflow(RED.nodes.subflow(tab.id));
}
},
onadd: function(tab) {
RED.menu.addItem("btn-workspace-menu",{
id:"btn-workspace-menu-"+tab.id.replace(".","-"),
label:tab.label,
onselect:function() {
workspace_tabs.activateTab(tab.id);
}
});
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
updateActiveNodes();
},
onremove: function(tab) {
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
RED.menu.removeItem("btn-workspace-menu-"+tab.id.replace(".","-"));
updateActiveNodes();
}
});
var workspaceIndex = 0;
function addWorkspace() {
var tabId = RED.nodes.id();
do {
workspaceIndex += 1;
} while($("#workspace-tabs a[title='Sheet "+workspaceIndex+"']").size() !== 0);
var ws = {type:"tab",id:tabId,label:"Sheet "+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);
}
function init() {
$('#btn-workspace-add-tab').on("click",addWorkspace);
RED.menu.setAction('btn-workspace-add',addWorkspace);
RED.menu.setAction('btn-workspace-edit',function() {
showRenameWorkspaceDialog(activeWorkspace);
});
RED.menu.setAction('btn-workspace-delete',function() {
deleteWorkspace(activeWorkspace);
});
updateActiveNodes();
}
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');
}
function canvasMouseDown() {
@ -530,7 +462,7 @@ RED.view = (function() {
clearSelection();
}
RED.nodes.eachNode(function(n) {
if (n.z == activeWorkspace && !n.selected) {
if (n.z == RED.workspaces.active() && !n.selected) {
n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2);
if (n.selected) {
n.dirty = true;
@ -625,7 +557,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,z:activeWorkspace};
var nn = { id:(1+Math.random()*4294967295).toString(16),x: mousePos[0],y:mousePos[1],w:node_width,z:RED.workspaces.active()};
nn.type = selected_tool;
nn._def = RED.nodes.getType(nn.type);
@ -688,7 +620,7 @@ RED.view = (function() {
function selectAll() {
RED.nodes.eachNode(function(n) {
if (n.z == activeWorkspace) {
if (n.z == RED.workspaces.active()) {
if (!n.selected) {
n.selected = true;
n.dirty = true;
@ -960,7 +892,7 @@ RED.view = (function() {
if (mouse_mode == RED.state.JOINING && mousedown_node) {
if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) {
RED.nodes.eachNode(function(n) {
if (n.z == activeWorkspace) {
if (n.z == RED.workspaces.active()) {
var hw = n.w/2;
var hh = n.h/2;
if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
@ -1246,7 +1178,7 @@ RED.view = (function() {
vis.selectAll(".subflowinput").remove();
}
//var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == activeWorkspace }),function(d){return d.id});
//var node = vis.selectAll(".nodegroup").data(RED.nodes.nodes.filter(function(d) { return d.z == RED.workspaces.active() }),function(d){return d.id});
var node = vis.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
node.exit().remove();
@ -1712,8 +1644,6 @@ RED.view = (function() {
RED.keyboard.add(/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();});
RED.keyboard.add(/* 0 */ 48,{ctrl:true},function(){zoomZero();d3.event.preventDefault();});
RED.keyboard.add(/* v */ 86,{ctrl:true},function(){importNodes(clipboard);d3.event.preventDefault();});
RED.keyboard.add(/* e */ 69,{ctrl:true},function(){showExportNodesDialog();d3.event.preventDefault();});
RED.keyboard.add(/* i */ 73,{ctrl:true},function(){showImportNodesDialog();d3.event.preventDefault();});
// TODO: 'dirty' should be a property of RED.nodes - with an event callback for ui hooks
function setDirty(d) {
@ -1744,7 +1674,7 @@ RED.view = (function() {
var new_workspaces = result[2];
var new_subflows = result[3];
var new_ms = new_nodes.filter(function(n) { return n.z == activeWorkspace }).map(function(n) { return {n:n};});
var new_ms = new_nodes.filter(function(n) { return n.z == RED.workspaces.active() }).map(function(n) { return {n:n};});
var new_node_ids = new_nodes.map(function(n){ return n.id; });
// TODO: pick a more sensible root node
@ -1816,136 +1746,6 @@ RED.view = (function() {
}
}
function showExportNodesDialog() {
mouse_mode = RED.state.EXPORT;
var nns = RED.nodes.createExportableNodeSet(moving_set);
$("#dialog-form").html($("script[data-template-name='export-clipboard-dialog']").html());
$("#node-input-export").val(JSON.stringify(nns));
$("#node-input-export").focus(function() {
var textarea = $(this);
textarea.select();
textarea.mouseup(function() {
textarea.unbind("mouseup");
return false;
});
});
$( "#dialog" ).dialog("option","title","Export nodes to clipboard").dialog( "open" );
$("#node-input-export").focus();
}
function showExportNodesLibraryDialog() {
mouse_mode = RED.state.EXPORT;
var nns = RED.nodes.createExportableNodeSet(moving_set);
$("#dialog-form").html($("script[data-template-name='export-library-dialog']").html());
$("#node-input-filename").attr('nodes',JSON.stringify(nns));
$( "#dialog" ).dialog("option","title","Export nodes to library").dialog( "open" );
}
function showImportNodesDialog() {
mouse_mode = RED.state.IMPORT;
$("#dialog-form").html($("script[data-template-name='import-dialog']").html());
$("#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 sheet",
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_tabs.renameTab(workspace.id,label);
RED.view.dirty(true);
$("#btn-workspace-menu-"+workspace.id.replace(".","-")).text(label);
// TODO: update entry in menu
}
$( this ).dialog( "close" );
}
},
{
text: "Cancel",
click: function() {
$( this ).dialog( "close" );
}
}
],
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
}
});
$( "#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" );
}
}
],
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
}
});
function hideDropTarget() {
$("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27);
@ -2004,30 +1804,12 @@ RED.view = (function() {
mouse_mode = state;
}
},
addWorkspace: function(ws) {
workspace_tabs.addTab(ws);
workspace_tabs.resize();
},
removeWorkspace: function(ws) {
if (workspace_tabs.contains(ws.id)) {
workspace_tabs.removeTab(ws.id);
}
},
getWorkspace: function() {
return activeWorkspace;
},
showWorkspace: function(id) {
workspace_tabs.activateTab(id);
},
redraw: function(updateActive) {
if (updateActive) {
updateActiveNodes();
}
RED.nodes.eachSubflow(function(sf) {
if (workspace_tabs.contains(sf.id)) {
workspace_tabs.renameTab(sf.id,"Subflow: "+sf.name);
}
});
RED.workspaces.refresh();
redraw();
},
dirty: function(d) {
@ -2039,14 +1821,15 @@ RED.view = (function() {
},
focus: focusView,
importNodes: importNodes,
resize: function() {
workspace_tabs.resize();
},
status: function(s) {
showStatus = s;
RED.nodes.eachNode(function(n) { n.dirty = true;});
//TODO: subscribe/unsubscribe here
redraw();
if (s == null) {
return showStatus;
} else {
showStatus = s;
RED.nodes.eachNode(function(n) { n.dirty = true;});
//TODO: subscribe/unsubscribe here
redraw();
}
},
calculateTextWidth: calculateTextWidth,
select: function(selection) {
@ -2073,26 +1856,6 @@ RED.view = (function() {
selection.link = selected_link;
}
return selection;
},
//TODO: should these move to an import/export module?
showImportNodesDialog: showImportNodesDialog,
showExportNodesDialog: showExportNodesDialog,
showExportNodesLibraryDialog: showExportNodesLibraryDialog,
addFlow: function() {
var ws = {type:"subflow",id:RED.nodes.id(),label:"Flow 1", closeable: true};
RED.nodes.addWorkspace(ws);
workspace_tabs.addTab(ws);
workspace_tabs.activateTab(ws.id);
return ws;
},
showSubflow: function(id) {
if (!workspace_tabs.contains(id)) {
var sf = RED.nodes.subflow(id);
workspace_tabs.addTab({type:"subflow",id:id,label:"Subflow: "+sf.name, closeable: true});
workspace_tabs.resize();
}
workspace_tabs.activateTab(id);
}
};
})();

273
public/red/ui/workspaces.js Normal file
View File

@ -0,0 +1,273 @@
/**
* Copyright 2015 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.workspaces = (function() {
var activeWorkspace = 0;
var workspaceIndex = 0;
function addWorkspace(ws) {
if (ws) {
workspace_tabs.addTab(ws);
workspace_tabs.resize();
} else {
var tabId = RED.nodes.id();
do {
workspaceIndex += 1;
} while($("#workspace-tabs a[title='Sheet "+workspaceIndex+"']").size() !== 0);
ws = {type:"tab",id:tabId,label:"Sheet "+workspaceIndex};
RED.nodes.addWorkspace(ws);
workspace_tabs.addTab(ws);
workspace_tabs.activateTab(tabId);
RED.history.push({t:'add',workspaces:[ws],dirty:RED.view.dirty()});
RED.view.dirty(true);
}
}
function deleteWorkspace(ws,force) {
if (workspace_tabs.count() == 1) {
return;
}
var nodes = [];
if (!force) {
//TODO: remove direct access to RED.nodes.nodes
nodes = RED.nodes.nodes.filter(function(d) {
return d.z == ws.id;
});
}
if (force || nodes.length === 0) {
removeWorkspace(ws);
var historyEvent = RED.nodes.removeWorkspace(ws.id);
historyEvent.t = 'delete';
historyEvent.dirty = RED.view.dirty();
historyEvent.workspaces = [ws];
RED.history.push(historyEvent);
RED.view.dirty(true);
} else {
$( "#node-dialog-delete-workspace" ).dialog('option','workspace',ws);
$( "#node-dialog-delete-workspace-name" ).text(ws.label);
$( "#node-dialog-delete-workspace" ).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");
}
var workspace_tabs = RED.tabs.create({
id: "workspace-tabs",
onchange: function(tab) {
if (tab.type == "subflow") {
$("#workspace-toolbar").show();
} else {
$("#workspace-toolbar").hide();
}
var event = {
old: activeWorkspace
}
activeWorkspace = tab.id;
event.workspace = activeWorkspace;
eventHandler.emit("change",event);
},
ondblclick: function(tab) {
if (tab.type != "subflow") {
showRenameWorkspaceDialog(tab.id);
} else {
RED.editor.editSubflow(RED.nodes.subflow(tab.id));
}
},
onadd: function(tab) {
RED.menu.addItem("btn-workspace-menu",{
id:"btn-workspace-menu-"+tab.id.replace(".","-"),
label:tab.label,
onselect:function() {
workspace_tabs.activateTab(tab.id);
}
});
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
},
onremove: function(tab) {
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1);
RED.menu.removeItem("btn-workspace-menu-"+tab.id.replace(".","-"));
}
});
$("#node-dialog-rename-workspace form" ).submit(function(e) { e.preventDefault();});
$( "#node-dialog-rename-workspace" ).dialog({
modal: true,
autoOpen: false,
width: 500,
title: "Rename sheet",
buttons: [
{
class: 'leftButton',
text: "Delete",
click: function() {
var workspace = $(this).dialog('option','workspace');
$( this ).dialog( "close" );
deleteWorkspace(workspace);
}
},
{
text: "Ok",
click: function() {
var workspace = $(this).dialog('option','workspace');
var label = $( "#node-input-workspace-name" ).val();
if (workspace.label != label) {
workspace_tabs.renameTab(workspace.id,label);
RED.view.dirty(true);
$("#btn-workspace-menu-"+workspace.id.replace(".","-")).text(label);
// TODO: update entry in menu
}
$( this ).dialog( "close" );
}
},
{
text: "Cancel",
click: function() {
$( this ).dialog( "close" );
}
}
],
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
}
});
$( "#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');
deleteWorkspace(workspace,true);
$( this ).dialog( "close" );
}
},
{
text: "Cancel",
click: function() {
$( this ).dialog( "close" );
}
}
],
open: function(e) {
RED.keyboard.disable();
},
close: function(e) {
RED.keyboard.enable();
}
});
function init() {
$('#btn-workspace-add-tab').on("click",function(e) {addWorkspace(); e.preventDefault()});
RED.sidebar.on("resize",workspace_tabs.resize);
RED.menu.setAction('btn-workspace-delete',function() {
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
});
}
// TODO: DRY
var eventHandler = (function() {
var handlers = {};
return {
on: function(evt,func) {
handlers[evt] = handlers[evt]||[];
handlers[evt].push(func);
},
emit: function(evt,arg) {
if (handlers[evt]) {
for (var i=0;i<handlers[evt].length;i++) {
handlers[evt][i](arg);
}
}
}
}
})();
function removeWorkspace(ws) {
if (!ws) {
deleteWorkspace(RED.nodes.workspace(activeWorkspace));
} else {
if (workspace_tabs.contains(ws.id)) {
workspace_tabs.removeTab(ws.id);
}
}
}
return {
init: init,
on: eventHandler.on,
add: addWorkspace,
remove: removeWorkspace,
edit: function(id) {
showRenameWorkspaceDialog(id||activeWorkspace);
},
contains: function(id) {
return workspace_tabs.contains(id);
},
count: function() {
return workspace_tabs.count();
},
active: function() {
return activeWorkspace
},
show: function(id) {
if (!workspace_tabs.contains(id)) {
var sf = RED.nodes.subflow(id);
if (sf) {
addWorkspace({type:"subflow",id:id,label:"Subflow: "+sf.name, closeable: true});
}
}
workspace_tabs.activateTab(id);
},
refresh: function() {
RED.nodes.eachSubflow(function(sf) {
if (workspace_tabs.contains(sf.id)) {
workspace_tabs.renameTab(sf.id,"Subflow: "+sf.name);
}
});
},
resize: function() {
workspace_tabs.resize();
}
}
})();

View File

@ -752,7 +752,7 @@ g.link_unknown path.link_line {
margin-bottom: 5px;
}
#dialog-form, #dialog-config-form {
.dialog-form, #dialog-form, #dialog-config-form {
margin: 0;
height: 100%;
}