diff --git a/public/index.html b/public/index.html index 4df3e636e..a5aaae94e 100644 --- a/public/index.html +++ b/public/index.html @@ -211,27 +211,13 @@ - + - + + diff --git a/public/red/history.js b/public/red/history.js index 36ab451a2..765606d04 100644 --- a/public/red/history.js +++ b/public/red/history.js @@ -47,20 +47,20 @@ RED.history = (function() { if (ev.workspaces) { for (i=0;iEither, add missing types to Node-RED, restart and then reload page,
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
') + .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 = '
'+ + ''+ + ''+ + '
'+ + '
'+ + 'Select the text above and copy to the clipboard with Ctrl-C.'+ + '
'; + + var importNodesDialog = '
'+ + ''+ + '
'; + + + 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 + } + + + + + +})(); diff --git a/public/red/ui/editor.js b/public/red/ui/editor.js index 2bf9f4acd..31cae03b5 100644 --- a/public/red/ui/editor.js +++ b/public/red/ui/editor.js @@ -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(); } }); diff --git a/public/red/ui/library.js b/public/red/ui/library.js index 28beb7e74..9cbafc429 100644 --- a/public/red/ui/library.js +++ b/public/red/ui/library.js @@ -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 } })(); diff --git a/public/red/ui/sidebar.js b/public/red/ui/sidebar.js index dda7f8d45..f23aa1dca 100644 --- a/public/red/ui/sidebar.js +++ b/public/red/ui/sidebar.js @@ -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); diff --git a/public/red/ui/subflow.js b/public/red/ui/subflow.js index d187f7140..50ada14fc 100644 --- a/public/red/ui/subflow.js +++ b/public/red/ui/subflow.js @@ -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() diff --git a/public/red/ui/tab-info.js b/public/red/ui/tab-info.js index 35ea9932e..848b07c5f 100644 --- a/public/red/ui/tab-info.js +++ b/public/red/ui/tab-info.js @@ -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 { diff --git a/public/red/ui/view.js b/public/red/ui/view.js index f87abd41e..75288960a 100644 --- a/public/red/ui/view.js +++ b/public/red/ui/view.js @@ -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] && @@ -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); } }; })(); diff --git a/public/red/ui/workspaces.js b/public/red/ui/workspaces.js new file mode 100644 index 000000000..ac004adcf --- /dev/null +++ b/public/red/ui/workspaces.js @@ -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