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> </div>
</form> </form>
</div> </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"> <script type="text/x-red" data-template-name="export-library-dialog">
<div class="form-row"> <div class="form-row">
<label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label> <label for="node-input-filename" ><i class="fa fa-file"></i> Filename:</label>
<input type="text" id="node-input-filename" placeholder="Filename"> <input type="text" id="node-input-filename" placeholder="Filename">
</div> </div>
</script> </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"> <script type="text/x-red" data-template-name="subflow">
<div class="form-row"> <div class="form-row">
@ -260,12 +246,14 @@
<script src="red/ui/menu.js"></script> <script src="red/ui/menu.js"></script>
<script src="red/ui/keyboard.js"></script> <script src="red/ui/keyboard.js"></script>
<script src="red/ui/tabs.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/view.js"></script>
<script src="red/ui/sidebar.js"></script> <script src="red/ui/sidebar.js"></script>
<script src="red/ui/palette.js"></script> <script src="red/ui/palette.js"></script>
<script src="red/ui/tab-info.js"></script> <script src="red/ui/tab-info.js"></script>
<script src="red/ui/tab-config.js"></script> <script src="red/ui/tab-config.js"></script>
<script src="red/ui/editor.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/library.js"></script>
<script src="red/ui/notifications.js"></script> <script src="red/ui/notifications.js"></script>
<script src="red/ui/subflow.js"></script> <script src="red/ui/subflow.js"></script>

View File

@ -47,20 +47,20 @@ RED.history = (function() {
if (ev.workspaces) { if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) { for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.removeWorkspace(ev.workspaces[i].id); RED.nodes.removeWorkspace(ev.workspaces[i].id);
RED.view.removeWorkspace(ev.workspaces[i]); RED.workspaces.remove(ev.workspaces[i]);
} }
} }
if (ev.subflows) { if (ev.subflows) {
for (i=0;i<ev.subflows.length;i++) { for (i=0;i<ev.subflows.length;i++) {
RED.nodes.removeSubflow(ev.subflows[i]); RED.nodes.removeSubflow(ev.subflows[i]);
RED.view.removeWorkspace(ev.subflows[i]); RED.workspaces.remove(ev.subflows[i]);
} }
} }
} else if (ev.t == "delete") { } else if (ev.t == "delete") {
if (ev.workspaces) { if (ev.workspaces) {
for (i=0;i<ev.workspaces.length;i++) { for (i=0;i<ev.workspaces.length;i++) {
RED.nodes.addWorkspace(ev.workspaces[i]); RED.nodes.addWorkspace(ev.workspaces[i]);
RED.view.addWorkspace(ev.workspaces[i]); RED.workspaces.add(ev.workspaces[i]);
} }
} }
if (ev.subflow) { if (ev.subflow) {
@ -183,7 +183,7 @@ RED.history = (function() {
} }
RED.nodes.removeSubflow(ev.subflow); RED.nodes.removeSubflow(ev.subflow);
RED.view.removeWorkspace(ev.subflow); RED.workspaces.remove(ev.subflow);
if (ev.removedLinks) { if (ev.removedLinks) {
for (i=0;i<ev.removedLinks.length;i++) { 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}, {id:"btn-node-status",label:"Display node status",toggle:true,onselect:toggleStatus, selected: true},
null, null,
{id:"btn-import-menu",label:"Import",options:[ {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-import-library",label:"Library",options:[]}
]}, ]},
{id:"btn-export-menu",label:"Export",disabled:true,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-clipboard",label:"Clipboard",disabled:true,onselect:RED.clipboard.export},
{id:"btn-export-library",label:"Library",disabled:true,onselect:RED.view.showExportNodesLibraryDialog} {id:"btn-export-library",label:"Library",disabled:true,onselect:RED.library.export}
]}, ]},
null, null,
{id:"btn-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show}, {id:"btn-config-nodes",label:"Configuration nodes",onselect:RED.sidebar.config.show},
@ -291,9 +291,9 @@ var RED = (function() {
]}, ]},
null, null,
{id:"btn-workspace-menu",label:"Workspaces",options:[ {id:"btn-workspace-menu",label:"Workspaces",options:[
{id:"btn-workspace-add",label:"Add"}, {id:"btn-workspace-add",label:"Add",onselect:RED.workspaces.add},
{id:"btn-workspace-edit",label:"Rename"}, {id:"btn-workspace-edit",label:"Rename",onselect:RED.workspaces.edit},
{id:"btn-workspace-delete",label:"Delete"}, {id:"btn-workspace-delete",label:"Delete",onselect:RED.workspaces.remove},
null null
]}, ]},
null, null,
@ -357,6 +357,8 @@ var RED = (function() {
RED.palette.init(); RED.palette.init();
RED.sidebar.init(); RED.sidebar.init();
RED.subflow.init(); RED.subflow.init();
RED.workspaces.init();
RED.clipboard.init();
RED.view.init(); RED.view.init();
RED.keyboard.add(/* ? */ 191,{shift:true},function(){showHelp();d3.event.preventDefault();}); RED.keyboard.add(/* ? */ 191,{shift:true},function(){showHelp();d3.event.preventDefault();});

View File

@ -429,16 +429,16 @@ RED.nodes = (function() {
var exportedConfigNodes = {}; var exportedConfigNodes = {};
var exportedSubflows = {}; var exportedSubflows = {};
for (var n=0;n<set.length;n++) { for (var n=0;n<set.length;n++) {
var node = set[n].n; var node = set[n];
if (node.type.substring(0,8) == "subflow:") { if (node.type.substring(0,8) == "subflow:") {
var subflowId = node.type.substring(8); var subflowId = node.type.substring(8);
if (!exportedSubflows[subflowId]) { if (!exportedSubflows[subflowId]) {
exportedSubflows[subflowId] = true; exportedSubflows[subflowId] = true;
var subflow = getSubflow(subflowId); var subflow = getSubflow(subflowId);
var subflowSet = [{n:subflow}]; var subflowSet = [subflow];
RED.nodes.eachNode(function(n) { RED.nodes.eachNode(function(n) {
if (n.z == subflowId) { if (n.z == subflowId) {
subflowSet.push({n:n}); subflowSet.push(n);
} }
}); });
var exportableSubflow = createExportableNodeSet(subflowSet); 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"); //"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); var activeSubflow = getSubflow(activeWorkspace);
if (activeSubflow) { if (activeSubflow) {
for (i=0;i<newNodes.length;i++) { for (i=0;i<newNodes.length;i++) {
@ -589,7 +589,7 @@ RED.nodes = (function() {
n.id = nid; n.id = nid;
} }
addWorkspace(n); addWorkspace(n);
RED.view.addWorkspace(n); RED.workspaces.add(n);
new_workspaces.push(n); new_workspaces.push(n);
} else if (n.type === "subflow") { } else if (n.type === "subflow") {
subflow_map[n.id] = n; subflow_map[n.id] = n;
@ -634,9 +634,9 @@ RED.nodes = (function() {
if (defaultWorkspace == null) { if (defaultWorkspace == null) {
defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" }; defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
addWorkspace(defaultWorkspace); addWorkspace(defaultWorkspace);
RED.view.addWorkspace(defaultWorkspace); RED.workspaces.add(defaultWorkspace);
new_workspaces.push(defaultWorkspace); new_workspaces.push(defaultWorkspace);
activeWorkspace = RED.view.getWorkspace(); activeWorkspace = RED.workspaces.active();
} }
var node_map = {}; 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; editing_node.dirty = true;
validateNode(editing_node); validateNode(editing_node);
RED.view.redraw(); RED.view.redraw();
} else if (RED.view.state() == RED.state.EXPORT) { } else if (/Export nodes to library/.test($( "#dialog" ).dialog("option","title"))) {
if (/library/.test($( "#dialog" ).dialog("option","title"))) { //TODO: move this to RED.library
//TODO: move this to RED.library var flowName = $("#node-input-filename").val();
var flowName = $("#node-input-filename").val(); if (!/^\s*$/.test(flowName)) {
if (!/^\s*$/.test(flowName)) { $.ajax({
$.ajax({ url:'library/flows/'+flowName,
url:'library/flows/'+flowName, type: "POST",
type: "POST", data: $("#node-input-filename").attr('nodes'),
data: $("#node-input-filename").attr('nodes'), contentType: "application/json; charset=utf-8"
contentType: "application/json; charset=utf-8" }).done(function() {
}).done(function() { RED.library.loadFlowLibrary();
RED.library.loadFlowLibrary(); RED.notify("Saved nodes","success");
RED.notify("Saved nodes","success"); });
});
}
} }
} else if (RED.view.state() == RED.state.IMPORT) {
RED.view.importNodes($("#node-input-import").val());
} }
$( this ).dialog( "close" ); $( this ).dialog( "close" );
} }
@ -500,7 +496,7 @@ RED.editor = (function() {
class: 'leftButton', class: 'leftButton',
text: "Edit flow", text: "Edit flow",
click: function() { click: function() {
RED.view.showSubflow(id); RED.workspaces.show(id);
$("#node-dialog-ok").click(); $("#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 { return {
init: function() { init: function() {
RED.view.on("selection-changed",function(selection) { RED.view.on("selection-changed",function(selection) {
@ -397,7 +405,9 @@ RED.library = (function() {
loadFlowLibrary(); loadFlowLibrary();
}, },
create: createUI, create: createUI,
loadFlowLibrary: loadFlowLibrary loadFlowLibrary: loadFlowLibrary,
export: exportFlow
} }
})(); })();

View File

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

View File

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

View File

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

View File

@ -29,14 +29,13 @@ RED.view = (function() {
moveTouchCenter = [], moveTouchCenter = [],
touchStartTime = 0; touchStartTime = 0;
var workspaceScrollPositions = {};
var activeWorkspace = 0;
var activeSubflow = null; var activeSubflow = null;
var activeNodes = []; var activeNodes = [];
var activeLinks = []; var activeLinks = [];
var workspaceScrollPositions = {};
var selected_link = null, var selected_link = null,
mousedown_link = null, mousedown_link = null,
mousedown_node = null, mousedown_node = null,
@ -234,40 +233,38 @@ RED.view = (function() {
var drag_line = vis.append("svg:path").attr("class", "drag_line"); var drag_line = vis.append("svg:path").attr("class", "drag_line");
function updateActiveNodes() { function updateActiveNodes() {
//TODO: remove direct access to RED.nodes.nodes
activeNodes = RED.nodes.nodes.filter(function(d) { activeNodes = RED.nodes.nodes.filter(function(d) {
return d.z == activeWorkspace; return d.z == RED.workspaces.active();
}); });
activeLinks = RED.nodes.links.filter(function(d) { 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({ function init() {
id: "workspace-tabs", RED.workspaces.on("change",function(event) {
onchange: function(tab) {
if (tab.type == "subflow") {
$("#workspace-toolbar").show();
} else {
$("#workspace-toolbar").hide();
}
var chart = $("#chart"); var chart = $("#chart");
if (activeWorkspace !== 0) { if (event.old !== 0) {
workspaceScrollPositions[activeWorkspace] = { workspaceScrollPositions[event.old] = {
left:chart.scrollLeft(), left:chart.scrollLeft(),
top:chart.scrollTop() top:chart.scrollTop()
}; };
} }
var scrollStartLeft = chart.scrollLeft(); var scrollStartLeft = chart.scrollLeft();
var scrollStartTop = chart.scrollTop(); var scrollStartTop = chart.scrollTop();
activeWorkspace = tab.id; activeSubflow = RED.nodes.subflow(event.workspace);
activeSubflow = RED.nodes.subflow(activeWorkspace);
if (activeSubflow) { if (activeSubflow) {
$("#workspace-subflow-add-input").toggleClass("disabled",activeSubflow.in.length > 0); $("#workspace-subflow-add-input").toggleClass("disabled",activeSubflow.in.length > 0);
} }
if (workspaceScrollPositions[activeWorkspace]) {
chart.scrollLeft(workspaceScrollPositions[activeWorkspace].left); RED.menu.setDisabled("btn-workspace-edit", activeSubflow);
chart.scrollTop(workspaceScrollPositions[activeWorkspace].top); 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 { } else {
chart.scrollLeft(0); chart.scrollLeft(0);
chart.scrollTop(0); chart.scrollTop(0);
@ -278,10 +275,6 @@ RED.view = (function() {
mouse_position[0] += scrollDeltaLeft; mouse_position[0] += scrollDeltaLeft;
mouse_position[1] += scrollDeltaTop; mouse_position[1] += scrollDeltaTop;
} }
RED.menu.setDisabled("btn-workspace-edit", activeSubflow);
RED.menu.setDisabled("btn-workspace-delete",workspace_tabs.count() == 1 || activeSubflow);
clearSelection(); clearSelection();
RED.nodes.eachNode(function(n) { RED.nodes.eachNode(function(n) {
n.dirty = true; n.dirty = true;
@ -289,69 +282,8 @@ RED.view = (function() {
updateSelection(); updateSelection();
updateActiveNodes(); updateActiveNodes();
redraw(); 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() { function canvasMouseDown() {
@ -530,7 +462,7 @@ RED.view = (function() {
clearSelection(); clearSelection();
} }
RED.nodes.eachNode(function(n) { 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); n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2);
if (n.selected) { if (n.selected) {
n.dirty = true; n.dirty = true;
@ -625,7 +557,7 @@ RED.view = (function() {
mousePos[1] /= scaleFactor; mousePos[1] /= scaleFactor;
mousePos[0] /= 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.type = selected_tool;
nn._def = RED.nodes.getType(nn.type); nn._def = RED.nodes.getType(nn.type);
@ -688,7 +620,7 @@ RED.view = (function() {
function selectAll() { function selectAll() {
RED.nodes.eachNode(function(n) { RED.nodes.eachNode(function(n) {
if (n.z == activeWorkspace) { if (n.z == RED.workspaces.active()) {
if (!n.selected) { if (!n.selected) {
n.selected = true; n.selected = true;
n.dirty = true; n.dirty = true;
@ -960,7 +892,7 @@ RED.view = (function() {
if (mouse_mode == RED.state.JOINING && mousedown_node) { if (mouse_mode == RED.state.JOINING && mousedown_node) {
if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) { if (typeof TouchEvent != "undefined" && d3.event instanceof TouchEvent) {
RED.nodes.eachNode(function(n) { RED.nodes.eachNode(function(n) {
if (n.z == activeWorkspace) { if (n.z == RED.workspaces.active()) {
var hw = n.w/2; var hw = n.w/2;
var hh = n.h/2; var hh = n.h/2;
if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] && if (n.x-hw<mouse_position[0] && n.x+hw> mouse_position[0] &&
@ -1246,7 +1178,7 @@ RED.view = (function() {
vis.selectAll(".subflowinput").remove(); 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}); var node = vis.selectAll(".nodegroup").data(activeNodes,function(d){return d.id});
node.exit().remove(); node.exit().remove();
@ -1712,8 +1644,6 @@ RED.view = (function() {
RED.keyboard.add(/* - */ 189,{ctrl:true},function(){zoomOut();d3.event.preventDefault();}); 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(/* 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(/* 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 // TODO: 'dirty' should be a property of RED.nodes - with an event callback for ui hooks
function setDirty(d) { function setDirty(d) {
@ -1744,7 +1674,7 @@ RED.view = (function() {
var new_workspaces = result[2]; var new_workspaces = result[2];
var new_subflows = result[3]; 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; }); var new_node_ids = new_nodes.map(function(n){ return n.id; });
// TODO: pick a more sensible root node // 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() { function hideDropTarget() {
$("#dropTarget").hide(); $("#dropTarget").hide();
RED.keyboard.remove(/* ESCAPE */ 27); RED.keyboard.remove(/* ESCAPE */ 27);
@ -2004,30 +1804,12 @@ RED.view = (function() {
mouse_mode = state; 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) { redraw: function(updateActive) {
if (updateActive) { if (updateActive) {
updateActiveNodes(); updateActiveNodes();
} }
RED.nodes.eachSubflow(function(sf) { RED.workspaces.refresh();
if (workspace_tabs.contains(sf.id)) {
workspace_tabs.renameTab(sf.id,"Subflow: "+sf.name);
}
});
redraw(); redraw();
}, },
dirty: function(d) { dirty: function(d) {
@ -2039,14 +1821,15 @@ RED.view = (function() {
}, },
focus: focusView, focus: focusView,
importNodes: importNodes, importNodes: importNodes,
resize: function() {
workspace_tabs.resize();
},
status: function(s) { status: function(s) {
showStatus = s; if (s == null) {
RED.nodes.eachNode(function(n) { n.dirty = true;}); return showStatus;
//TODO: subscribe/unsubscribe here } else {
redraw(); showStatus = s;
RED.nodes.eachNode(function(n) { n.dirty = true;});
//TODO: subscribe/unsubscribe here
redraw();
}
}, },
calculateTextWidth: calculateTextWidth, calculateTextWidth: calculateTextWidth,
select: function(selection) { select: function(selection) {
@ -2073,26 +1856,6 @@ RED.view = (function() {
selection.link = selected_link; selection.link = selected_link;
} }
return selection; 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; margin-bottom: 5px;
} }
#dialog-form, #dialog-config-form { .dialog-form, #dialog-form, #dialog-config-form {
margin: 0; margin: 0;
height: 100%; height: 100%;
} }