diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view_copy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view_copy.js deleted file mode 100644 index 569fee1d5..000000000 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view_copy.js +++ /dev/null @@ -1,6287 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * 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-ui-workspace-chart - * \- "outer" - * \- - * \- .red-ui-workspace-chart-event-layer "eventLayer" - * |- .red-ui-workspace-chart-background - * |- .red-ui-workspace-chart-grid "gridLayer" - * |- "groupLayer" - * |- "groupSelectLayer" - * |- "linkLayer" - * |- "junctionLayer" - * |- "dragGroupLayer" - * |- "nodeLayer" - */ - -RED.view = (function() { - var space_width = 5000, - space_height = 5000, - lineCurveScale = 0.75, - scaleFactor = 1, - node_width = 100, - node_height = 30, - dblClickInterval = 650; - - var touchLongPressTimeout = 1000, - startTouchDistance = 0, - startTouchCenter = [], - moveTouchCenter = [], - touchStartTime = 0; - - var workspaceScrollPositions = {}; - var entryCoordinates = {x:-1, y:-1}; - var gridSize = 20; - var snapGrid = false; - - var activeSpliceLink; - var spliceActive = false; - var spliceTimer; - var groupHoverTimer; - - var activeSubflow = null; - var activeNodes = []; - var activeLinks = []; - var activeJunctions = []; - var activeFlowLinks = []; - var activeLinkNodes = {}; - var activeGroup = null; - var activeHoverGroup = null; - var activeGroups = []; - var dirtyGroups = {}; - - var mousedown_link = null; - var mousedown_node = null; - var mousedown_group = null; - var mousedown_port_type = null; - var mousedown_port_index = 0; - var mouseup_node = null; - var mouse_offset = [0,0]; - var mouse_position = null; - var mouse_mode = 0; - var mousedown_group_handle = null; - var lasso = null; - var slicePath = null; - var slicePathLast = null; - var ghostNode = null; - var showStatus = false; - var lastClickNode = null; - var dblClickPrimed = null; - var clickTime = 0; - var clickElapsed = 0; - var scroll_position = []; - var quickAddActive = false; - var quickAddLink = null; - var showAllLinkPorts = -1; - var groupNodeSelectPrimed = false; - var lastClickPosition = []; - var selectNodesOptions; - - let flashingNodeId; - - var clipboard = ""; - - // Note: these are the permitted status colour aliases. The actual RGB values - // are set in the CSS - flow.scss/colors.scss - var status_colours = { - "red": "#c00", - "green": "#5a8", - "yellow": "#F9DF31", - "blue": "#53A3F3", - "grey": "#d3d3d3", - "gray": "#d3d3d3" - } - - var PORT_TYPE_INPUT = 1; - var PORT_TYPE_OUTPUT = 0; - - var chart; - var outer; - var eventLayer; - var gridLayer; - var linkLayer; - var junctionLayer; - var dragGroupLayer; - var groupSelectLayer; - var nodeLayer; - var groupLayer; - var drag_lines; - - var movingSet = (function() { - var setIds = new Set(); - var set = []; - var api = { - add: function(node) { - if (Array.isArray(node)) { - for (var i=0;i1) { - clearTimeout(touchStartTime); - touchStartTime = null; - d3.event.preventDefault(); - touch0 = d3.event.touches.item(0); - var touch1 = d3.event.touches.item(1); - var a = touch0["pageY"]-touch1["pageY"]; - var b = touch0["pageX"]-touch1["pageX"]; - - var offset = chart.offset(); - var scrollPos = [chart.scrollLeft(),chart.scrollTop()]; - startTouchCenter = [ - (touch1["pageX"]+(b/2)-offset.left+scrollPos[0])/scaleFactor, - (touch1["pageY"]+(a/2)-offset.top+scrollPos[1])/scaleFactor - ]; - moveTouchCenter = [ - touch1["pageX"]+(b/2), - touch1["pageY"]+(a/2) - ] - startTouchDistance = Math.sqrt((a*a)+(b*b)); - } else { - var obj = d3.select(document.body); - touch0 = d3.event.touches.item(0); - var pos = [touch0.pageX,touch0.pageY]; - startTouchCenter = [touch0.pageX,touch0.pageY]; - startTouchDistance = 0; - var point = d3.touches(this)[0]; - touchStartTime = setTimeout(function() { - touchStartTime = null; - showTouchMenu(obj,pos); - //lasso = eventLayer.append("rect") - // .attr("ox",point[0]) - // .attr("oy",point[1]) - // .attr("rx",2) - // .attr("ry",2) - // .attr("x",point[0]) - // .attr("y",point[1]) - // .attr("width",0) - // .attr("height",0) - // .attr("class","nr-ui-view-lasso"); - },touchLongPressTimeout); - } - d3.event.preventDefault(); - }) - .on("touchmove", function(){ - if (RED.touch.radialMenu.active()) { - d3.event.preventDefault(); - return; - } - if (RED.view.DEBUG) { console.warn("eventLayer.touchmove", mouse_mode, mousedown_node); } - var touch0; - if (d3.event.touches.length<2) { - if (touchStartTime) { - touch0 = d3.event.touches.item(0); - var dx = (touch0.pageX-startTouchCenter[0]); - var dy = (touch0.pageY-startTouchCenter[1]); - var d = Math.abs(dx*dx+dy*dy); - if (d > 64) { - clearTimeout(touchStartTime); - touchStartTime = null; - if (!mousedown_node && !mousedown_group) { - mouse_mode = RED.state.PANNING; - mouse_position = [touch0.pageX,touch0.pageY] - scroll_position = [chart.scrollLeft(),chart.scrollTop()]; - } - - } - } else if (lasso) { - d3.event.preventDefault(); - } - canvasMouseMove.call(this); - } else { - touch0 = d3.event.touches.item(0); - var touch1 = d3.event.touches.item(1); - var a = touch0["pageY"]-touch1["pageY"]; - var b = touch0["pageX"]-touch1["pageX"]; - var offset = chart.offset(); - var scrollPos = [chart.scrollLeft(),chart.scrollTop()]; - var moveTouchDistance = Math.sqrt((a*a)+(b*b)); - var touchCenter = [ - touch1["pageX"]+(b/2), - touch1["pageY"]+(a/2) - ]; - - if (!isNaN(moveTouchDistance)) { - oldScaleFactor = scaleFactor; - scaleFactor = Math.min(2,Math.max(0.3, scaleFactor + (Math.floor(((moveTouchDistance*100)-(startTouchDistance*100)))/10000))); - - var deltaTouchCenter = [ // Try to pan whilst zooming - not 100% - startTouchCenter[0]*(scaleFactor-oldScaleFactor),//-(touchCenter[0]-moveTouchCenter[0]), - startTouchCenter[1]*(scaleFactor-oldScaleFactor) //-(touchCenter[1]-moveTouchCenter[1]) - ]; - - startTouchDistance = moveTouchDistance; - moveTouchCenter = touchCenter; - - chart.scrollLeft(scrollPos[0]+deltaTouchCenter[0]); - chart.scrollTop(scrollPos[1]+deltaTouchCenter[1]); - redraw(); - } - } - d3.event.preventDefault(); - }); - - // Workspace Background - eventLayer.append("svg:rect") - .attr("class","red-ui-workspace-chart-background") - .attr("width", space_width) - .attr("height", space_height); - - gridLayer = eventLayer.append("g").attr("class","red-ui-workspace-chart-grid"); - updateGrid(); - - groupLayer = eventLayer.append("g"); - groupSelectLayer = eventLayer.append("g"); - linkLayer = eventLayer.append("g"); - dragGroupLayer = eventLayer.append("g"); - junctionLayer = eventLayer.append("g"); - nodeLayer = eventLayer.append("g"); - - drag_lines = []; - - RED.events.on("workspace:change",function(event) { - if (event.old !== 0) { - workspaceScrollPositions[event.old] = { - left:chart.scrollLeft(), - top:chart.scrollTop() - }; - } - var scrollStartLeft = chart.scrollLeft(); - var scrollStartTop = chart.scrollTop(); - - activeSubflow = RED.nodes.subflow(event.workspace); - - RED.menu.setDisabled("menu-item-workspace-edit", activeSubflow || event.workspace === 0); - RED.menu.setDisabled("menu-item-workspace-delete",event.workspace === 0 || 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); - } - var scrollDeltaLeft = chart.scrollLeft() - scrollStartLeft; - var scrollDeltaTop = chart.scrollTop() - scrollStartTop; - if (mouse_position != null) { - mouse_position[0] += scrollDeltaLeft; - mouse_position[1] += scrollDeltaTop; - } - if (RED.workspaces.selection().length === 0) { - resetMouseVars(); - clearSelection(); - } - RED.nodes.eachNode(function(n) { - n.dirty = true; - n.dirtyStatus = true; - }); - updateSelection(); - updateActiveNodes(); - redraw(); - }); - - RED.statusBar.add({ - id: "view-zoom-controls", - align: "right", - element: $(''+ - ''+ - ''+ - ''+ - '') - }) - - $("#red-ui-view-zoom-out").on("click", zoomOut); - RED.popover.tooltip($("#red-ui-view-zoom-out"),RED._('actions.zoom-out'),'core:zoom-out'); - $("#red-ui-view-zoom-zero").on("click", zoomZero); - RED.popover.tooltip($("#red-ui-view-zoom-zero"),RED._('actions.zoom-reset'),'core:zoom-reset'); - $("#red-ui-view-zoom-in").on("click", zoomIn); - RED.popover.tooltip($("#red-ui-view-zoom-in"),RED._('actions.zoom-in'),'core:zoom-in'); - chart.on("DOMMouseScroll mousewheel", function (evt) { - if ( evt.altKey ) { - evt.preventDefault(); - evt.stopPropagation(); - var move = -(evt.originalEvent.detail) || evt.originalEvent.wheelDelta; - if (move <= 0) { zoomOut(); } - else { zoomIn(); } - } - }); - - //add search to status-toolbar - RED.statusBar.add({ - id: "view-search-tools", - align: "left", - hidden: false, - element: $(''+ - '' + - '' + - '' + - '? of ?' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '') - }) - $("#red-ui-view-searchtools-search").on("click", searchFlows); - RED.popover.tooltip($("#red-ui-view-searchtools-search"),RED._('actions.search-flows'),'core:search'); - $("#red-ui-view-searchtools-prev").on("click", searchPrev); - RED.popover.tooltip($("#red-ui-view-searchtools-prev"),RED._('actions.search-prev'),'core:search-previous'); - $("#red-ui-view-searchtools-next").on("click", searchNext); - RED.popover.tooltip($("#red-ui-view-searchtools-next"),RED._('actions.search-next'),'core:search-next'); - RED.popover.tooltip($("#red-ui-view-searchtools-close"),RED._('common.label.close')); - - // Handle nodes dragged from the palette - chart.droppable({ - accept:".red-ui-palette-node", - drop: function( event, ui ) { - d3.event = event; - var selected_tool = $(ui.draggable[0]).attr("data-palette-type"); - var result = createNode(selected_tool); - if (!result) { - return; - } - var historyEvent = result.historyEvent; - var nn = result.node; - - RED.nodes.add(nn); - - var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label"); - if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) { - nn.l = showLabel; - } - - var helperOffset = d3.touches(ui.helper.get(0))[0]||d3.mouse(ui.helper.get(0)); - var helperWidth = ui.helper.width(); - var helperHeight = ui.helper.height(); - var mousePos = d3.touches(this)[0]||d3.mouse(this); - - try { - var isLink = (nn.type === "link in" || nn.type === "link out") - var hideLabel = nn.hasOwnProperty('l')?!nn.l : isLink; - - var label = RED.utils.getNodeLabel(nn, nn.type); - var labelParts = getLabelParts(label, "red-ui-flow-node-label"); - if (hideLabel) { - nn.w = node_height; - nn.h = Math.max(node_height,(nn.outputs || 0) * 15); - } else { - nn.w = Math.max(node_width,20*(Math.ceil((labelParts.width+50+(nn._def.inputs>0?7:0))/20)) ); - nn.h = Math.max(6+24*labelParts.lines.length,(nn.outputs || 0) * 15, 30); - } - } catch(err) { - } - - mousePos[1] += this.scrollTop + ((helperHeight/2)-helperOffset[1]); - mousePos[0] += this.scrollLeft + ((helperWidth/2)-helperOffset[0]); - mousePos[1] /= scaleFactor; - mousePos[0] /= scaleFactor; - - nn.x = mousePos[0]; - nn.y = mousePos[1]; - - if (snapGrid) { - var gridOffset = RED.view.tools.calculateGridSnapOffsets(nn); - nn.x -= gridOffset.x; - nn.y -= gridOffset.y; - } - - var spliceLink = $(ui.helper).data("splice"); - if (spliceLink) { - // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp/showQuickAddDialog - RED.nodes.removeLink(spliceLink); - var link1 = { - source:spliceLink.source, - sourcePort:spliceLink.sourcePort, - target: nn - }; - var link2 = { - source:nn, - sourcePort:0, - target: spliceLink.target - }; - RED.nodes.addLink(link1); - RED.nodes.addLink(link2); - historyEvent.links = [link1,link2]; - historyEvent.removedLinks = [spliceLink]; - } - - - var group = $(ui.helper).data("group"); - if (group) { - RED.group.addToGroup(group, nn); - historyEvent = { - t: 'multi', - events: [historyEvent], - - } - historyEvent.events.push({ - t: "addToGroup", - group: group, - nodes: nn - }) - } - - RED.history.push(historyEvent); - RED.editor.validateNode(nn); - RED.nodes.dirty(true); - // auto select dropped node - so info shows (if visible) - exitActiveGroup(); - clearSelection(); - nn.selected = true; - movingSet.add(nn); - if (group) { - selectGroup(group,false); - enterActiveGroup(group); - activeGroup = group; - } - updateActiveNodes(); - updateSelection(); - redraw(); - - if (nn._def.autoedit) { - RED.editor.edit(nn); - } - } - }); - chart.on("focus", function() { - $("#red-ui-workspace-tabs").addClass("red-ui-workspace-focussed"); - }); - chart.on("blur", function() { - $("#red-ui-workspace-tabs").removeClass("red-ui-workspace-focussed"); - }); - - RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection); - RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();}); - RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true, generateDefaultNames: true});}); - - RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() }) - - RED.events.on("view:selection-changed", function(selection) { - var hasSelection = (selection.nodes && selection.nodes.length > 0); - var hasMultipleSelection = hasSelection && selection.nodes.length > 1; - RED.menu.setDisabled("menu-item-edit-cut",!hasSelection); - RED.menu.setDisabled("menu-item-edit-copy",!hasSelection); - RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-to-back",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-to-front",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-backwards",!hasSelection); - RED.menu.setDisabled("menu-item-view-tools-move-forwards",!hasSelection); - - RED.menu.setDisabled("menu-item-view-tools-align-left",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-center",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-right",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-top",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-middle",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-align-bottom",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally",!hasMultipleSelection); - RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally",!hasMultipleSelection); - }) - - RED.actions.add("core:delete-selection",deleteSelection); - RED.actions.add("core:delete-selection-and-reconnect",function() { deleteSelection(true) }); - RED.actions.add("core:edit-selected-node",editSelection); - RED.actions.add("core:go-to-selection",function() { - if (movingSet.length() > 0) { - var node = movingSet.get(0).n; - if (/^subflow:/.test(node.type)) { - RED.workspaces.show(node.type.substring(8)) - } else if (node.type === 'group') { - enterActiveGroup(node); - redraw(); - } - } - }); - RED.actions.add("core:undo",RED.history.pop); - RED.actions.add("core:redo",RED.history.redo); - RED.actions.add("core:select-all-nodes",selectAll); - RED.actions.add("core:select-none", selectNone); - RED.actions.add("core:zoom-in",zoomIn); - RED.actions.add("core:zoom-out",zoomOut); - RED.actions.add("core:zoom-reset",zoomZero); - RED.actions.add("core:enable-selected-nodes", function() { setSelectedNodeState(false)}); - RED.actions.add("core:disable-selected-nodes", function() { setSelectedNodeState(true)}); - - RED.actions.add("core:toggle-show-grid",function(state) { - if (state === undefined) { - RED.userSettings.toggle("view-show-grid"); - } else { - toggleShowGrid(state); - } - }); - RED.actions.add("core:toggle-snap-grid",function(state) { - if (state === undefined) { - RED.userSettings.toggle("view-snap-grid"); - } else { - toggleSnapGrid(state); - } - }); - RED.actions.add("core:toggle-status",function(state) { - if (state === undefined) { - RED.userSettings.toggle("view-node-status"); - } else { - toggleStatus(state); - } - }); - - RED.view.annotations.init(); - RED.view.navigator.init(); - RED.view.tools.init(); - - - RED.view.annotations.register("red-ui-flow-node-changed",{ - type: "badge", - class: "red-ui-flow-node-changed", - element: function() { - var changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle"); - changeBadge.setAttribute("cx",5); - changeBadge.setAttribute("cy",5); - changeBadge.setAttribute("r",5); - return changeBadge; - }, - show: function(n) { return n.changed||n.moved } - }) - - RED.view.annotations.register("red-ui-flow-node-error",{ - type: "badge", - class: "red-ui-flow-node-error", - element: function(d) { - var errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path"); - errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z"); - return errorBadge - }, - tooltip: function(d) { - if (d.validationErrors && d.validationErrors.length > 0) { - return RED._("editor.errors.invalidProperties")+"\n - "+d.validationErrors.join("\n - ") - } - }, - show: function(n) { return !n.valid } - }) - - if (RED.settings.get("editor.view.view-store-zoom")) { - var userZoomLevel = parseFloat(RED.settings.getLocal('zoom-level')) - if (!isNaN(userZoomLevel)) { - scaleFactor = userZoomLevel - } - } - - var onScrollTimer = null; - function storeScrollPosition() { - workspaceScrollPositions[RED.workspaces.active()] = { - left:chart.scrollLeft(), - top:chart.scrollTop() - }; - RED.settings.setLocal('scroll-positions', JSON.stringify(workspaceScrollPositions) ) - } - chart.on("scroll", function() { - if (RED.settings.get("editor.view.view-store-position")) { - if (onScrollTimer) { - clearTimeout(onScrollTimer) - } - onScrollTimer = setTimeout(storeScrollPosition, 200); - } - }) - - if (RED.settings.get("editor.view.view-store-position")) { - var scrollPositions = RED.settings.getLocal('scroll-positions') - if (scrollPositions) { - try { - workspaceScrollPositions = JSON.parse(scrollPositions) - } catch(err) { - } - } - } - } - - - - function updateGrid() { - var gridTicks = []; - for (var i=0;i 0) { - if (delta < node_width) { - scale = 0.75-0.75*((node_width-delta)/node_width); - // scale += 2*(Math.min(5*node_width,Math.abs(dx))/(5*node_width)); - // if (Math.abs(dy) < 3*node_height) { - // scaleY = ((dy>0)?0.5:-0.5)*(((3*node_height)-Math.abs(dy))/(3*node_height))*(Math.min(node_width,Math.abs(dx))/(node_width)) ; - // } - } - } else { - scale = 0.4-0.2*(Math.max(0,(node_width-Math.min(Math.abs(dx),Math.abs(dy)))/node_width)); - } - if (dx*sc > 0) { - return "M "+origX+" "+origY+ - " C "+(origX+sc*(node_width*scale))+" "+(origY+scaleY*node_height)+" "+ - (destX-sc*(scale)*node_width)+" "+(destY-scaleY*node_height)+" "+ - destX+" "+destY - } else { - - var midX = Math.floor(destX-dx/2); - var midY = Math.floor(destY-dy/2); - // - if (dy === 0) { - midY = destY + node_height; - } - var cp_height = node_height/2; - var y1 = (destY + midY)/2 - var topX =origX + sc*node_width*scale; - var topY = dy>0?Math.min(y1 - dy/2 , origY+cp_height):Math.max(y1 - dy/2 , origY-cp_height); - var bottomX = destX - sc*node_width*scale; - var bottomY = dy>0?Math.max(y1, destY-cp_height):Math.min(y1, destY+cp_height); - var x1 = (origX+topX)/2; - var scy = dy>0?1:-1; - var cp = [ - // Orig -> Top - [x1,origY], - [topX,dy>0?Math.max(origY, topY-cp_height):Math.min(origY, topY+cp_height)], - // Top -> Mid - // [Mirror previous cp] - [x1,dy>0?Math.min(midY, topY+cp_height):Math.max(midY, topY-cp_height)], - // Mid -> Bottom - // [Mirror previous cp] - [bottomX,dy>0?Math.max(midY, bottomY-cp_height):Math.min(midY, bottomY+cp_height)], - // Bottom -> Dest - // [Mirror previous cp] - [(destX+bottomX)/2,destY] - ]; - if (cp[2][1] === topY+scy*cp_height) { - if (Math.abs(dy) < cp_height*10) { - cp[1][1] = topY-scy*cp_height/2; - cp[3][1] = bottomY-scy*cp_height/2; - } - cp[2][0] = topX; - } - return "M "+origX+" "+origY+ - " C "+ - cp[0][0]+" "+cp[0][1]+" "+ - cp[1][0]+" "+cp[1][1]+" "+ - topX+" "+topY+ - " S "+ - cp[2][0]+" "+cp[2][1]+" "+ - midX+" "+midY+ - " S "+ - cp[3][0]+" "+cp[3][1]+" "+ - bottomX+" "+bottomY+ - " S "+ - cp[4][0]+" "+cp[4][1]+" "+ - destX+" "+destY - } - } - - function canvasMouseDown() { - if (RED.view.DEBUG) { - console.warn("canvasMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); - } - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - - if (d3.event.button === 1) { - // Middle Click pan - mouse_mode = RED.state.PANNING; - mouse_position = [d3.event.pageX,d3.event.pageY] - scroll_position = [chart.scrollLeft(),chart.scrollTop()]; - return; - } - if (!mousedown_node && !mousedown_link && !mousedown_group) { - selectedLinks.clear(); - updateSelection(); - } - if (mouse_mode === 0 && lasso) { - lasso.remove(); - lasso = null; - } - if (d3.event.touches || d3.event.button === 0) { - if ((mouse_mode === 0 || mouse_mode === RED.state.QUICK_JOINING) && (d3.event.metaKey || d3.event.ctrlKey) && !(d3.event.altKey || d3.event.shiftKey)) { - // Trigger quick add dialog - d3.event.stopPropagation(); - clearSelection(); - const point = d3.mouse(this); - var clickedGroup = getGroupAt(point[0], point[1]); - if (drag_lines.length > 0) { - clickedGroup = clickedGroup || RED.nodes.group(drag_lines[0].node.g) - } - showQuickAddDialog({ position: point, group: clickedGroup }); - } else if (mouse_mode === 0 && !(d3.event.metaKey || d3.event.ctrlKey)) { - // CTRL not being held - if (!d3.event.altKey) { - // ALT not held (shift is allowed) Trigger lasso - if (!touchStartTime) { - const point = d3.mouse(this); - lasso = eventLayer.append("rect") - .attr("ox", point[0]) - .attr("oy", point[1]) - .attr("rx", 1) - .attr("ry", 1) - .attr("x", point[0]) - .attr("y", point[1]) - .attr("width", 0) - .attr("height", 0) - .attr("class", "nr-ui-view-lasso"); - d3.event.preventDefault(); - } - } else if (d3.event.altKey) { - //Alt [+shift] held - Begin slicing - clearSelection(); - mouse_mode = (d3.event.shiftKey) ? RED.state.SLICING_JUNCTION : RED.state.SLICING; - const point = d3.mouse(this); - slicePath = eventLayer.append("path").attr("class", "nr-ui-view-slice").attr("d", `M${point[0]} ${point[1]}`) - slicePathLast = point; - RED.view.redraw(); - } - } - } - } - - function showQuickAddDialog(options) { - options = options || {}; - var point = options.position || lastClickPosition; - var spliceLink = options.splice; - var targetGroup = options.group; - var touchTrigger = options.touchTrigger; - - if (targetGroup && !targetGroup.active) { - selectGroup(targetGroup,false); - enterActiveGroup(targetGroup); - RED.view.redraw(); - } - - var ox = point[0]; - var oy = point[1]; - - if (RED.settings.get("editor").view['view-snap-grid']) { - // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','red') - point[0] = Math.round(point[0] / gridSize) * gridSize; - point[1] = Math.round(point[1] / gridSize) * gridSize; - // eventLayer.append("circle").attr("cx",point[0]).attr("cy",point[1]).attr("r","2").attr('fill','blue') - } - - var mainPos = $("#red-ui-main-container").position(); - - if (mouse_mode !== RED.state.QUICK_JOINING) { - mouse_mode = RED.state.QUICK_JOINING; - $(window).on('keyup',disableQuickJoinEventHandler); - } - quickAddActive = true; - - if (ghostNode) { - ghostNode.remove(); - } - ghostNode = eventLayer.append("g").attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); - ghostNode.append("rect") - .attr("class","red-ui-flow-node-placeholder") - .attr("rx", 5) - .attr("ry", 5) - .attr("width",node_width) - .attr("height",node_height) - .attr("fill","none") - // var ghostLink = ghostNode.append("svg:path") - // .attr("class","red-ui-flow-link-link") - // .attr("d","M 0 "+(node_height/2)+" H "+(gridSize * -2)) - // .attr("opacity",0); - - var filter; - if (drag_lines.length > 0) { - if (drag_lines[0].virtualLink) { - filter = {type:drag_lines[0].node.type === 'link in'?'link out':'link in'} - } else if (drag_lines[0].portType === PORT_TYPE_OUTPUT) { - filter = {input:true} - } else { - filter = {output:true} - } - - quickAddLink = { - node: drag_lines[0].node, - port: drag_lines[0].port, - portType: drag_lines[0].portType, - } - if (drag_lines[0].virtualLink) { - quickAddLink.virtualLink = true; - } - hideDragLines(); - } - if (spliceLink) { - filter = {input:true, output:true} - } - - var rebuildQuickAddLink = function() { - if (!quickAddLink) { - return; - } - if (!quickAddLink.el) { - quickAddLink.el = dragGroupLayer.append("svg:path").attr("class", "red-ui-flow-drag-line"); - } - var numOutputs = (quickAddLink.portType === PORT_TYPE_OUTPUT)?(quickAddLink.node.outputs || 1):1; - var sourcePort = quickAddLink.port; - var portY = -((numOutputs-1)/2)*13 +13*sourcePort; - var sc = (quickAddLink.portType === PORT_TYPE_OUTPUT)?1:-1; - quickAddLink.el.attr("d",generateLinkPath(quickAddLink.node.x+sc*quickAddLink.node.w/2,quickAddLink.node.y+portY,point[0]-sc*node_width/2,point[1],sc)); - } - if (quickAddLink) { - rebuildQuickAddLink(); - } - - - var lastAddedX; - var lastAddedWidth; - - RED.typeSearch.show({ - x:d3.event.clientX-mainPos.left-node_width/2 - (ox-point[0]), - y:d3.event.clientY-mainPos.top+ node_height/2 + 5 - (oy-point[1]), - disableFocus: touchTrigger, - filter: filter, - move: function(dx,dy) { - if (ghostNode) { - var pos = d3.transform(ghostNode.attr("transform")).translate; - ghostNode.attr("transform","translate("+(pos[0]+dx)+","+(pos[1]+dy)+")") - point[0] += dx; - point[1] += dy; - rebuildQuickAddLink(); - } - }, - cancel: function() { - if (quickAddLink) { - if (quickAddLink.el) { - quickAddLink.el.remove(); - } - quickAddLink = null; - } - quickAddActive = false; - if (ghostNode) { - ghostNode.remove(); - } - resetMouseVars(); - updateSelection(); - hideDragLines(); - redraw(); - }, - add: function(type,keepAdding) { - if (touchTrigger) { - keepAdding = false; - resetMouseVars(); - } - - var nn; - var historyEvent; - if (type === 'junction') { - nn = { - _def: {defaults:{}}, - type: 'junction', - z: RED.workspaces.active(), - id: RED.nodes.id(), - x: 0, - y: 0, - w: 0, h: 0, - outputs: 1, - inputs: 1, - dirty: true - } - historyEvent = { - t:'add', - junctions:[nn] - } - } else { - var result = createNode(type); - if (!result) { - return; - } - nn = result.node; - historyEvent = result.historyEvent; - } - if (keepAdding) { - mouse_mode = RED.state.QUICK_JOINING; - } - - nn.x = point[0]; - nn.y = point[1]; - var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label"); - if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) { - nn.l = showLabel; - } - if (quickAddLink) { - var drag_line = quickAddLink; - var src = null,dst,src_port; - if (drag_line.portType === PORT_TYPE_OUTPUT && (nn.inputs > 0 || drag_line.virtualLink) ) { - src = drag_line.node; - src_port = drag_line.port; - dst = nn; - } else if (drag_line.portType === PORT_TYPE_INPUT && (nn.outputs > 0 || drag_line.virtualLink)) { - src = nn; - dst = drag_line.node; - src_port = 0; - } - - if (src !== null) { - // Joining link nodes via virual wires. Need to update - // the src and dst links property - if (drag_line.virtualLink) { - historyEvent = { - t:'multi', - events: [historyEvent] - } - var oldSrcLinks = $.extend(true,{},{v:src.links}).v - var oldDstLinks = $.extend(true,{},{v:dst.links}).v - src.links.push(dst.id); - dst.links.push(src.id); - src.dirty = true; - dst.dirty = true; - - historyEvent.events.push({ - t:'edit', - node: src, - dirty: RED.nodes.dirty(), - changed: src.changed, - changes: { - links:oldSrcLinks - } - }); - historyEvent.events.push({ - t:'edit', - node: dst, - dirty: RED.nodes.dirty(), - changed: dst.changed, - changes: { - links:oldDstLinks - } - }); - src.changed = true; - dst.changed = true; - } else { - var link = {source: src, sourcePort:src_port, target: dst}; - RED.nodes.addLink(link); - historyEvent.links = [link]; - } - if (!keepAdding) { - quickAddLink.el.remove(); - quickAddLink = null; - if (mouse_mode === RED.state.QUICK_JOINING) { - if (drag_line.portType === PORT_TYPE_OUTPUT && nn.outputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); - } else if (!quickAddLink && drag_line.portType === PORT_TYPE_INPUT && nn.inputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); - } else { - resetMouseVars(); - } - } - } else { - quickAddLink.node = nn; - quickAddLink.port = 0; - } - } else { - hideDragLines(); - resetMouseVars(); - } - } else { - if (!keepAdding) { - if (mouse_mode === RED.state.QUICK_JOINING) { - if (nn.outputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_OUTPUT}]); - } else if (nn.inputs > 0) { - showDragLines([{node:nn,port:0,portType:PORT_TYPE_INPUT}]); - } else { - resetMouseVars(); - } - } - } else { - if (nn.outputs > 0) { - quickAddLink = { - node: nn, - port: 0, - portType: PORT_TYPE_OUTPUT - } - } else if (nn.inputs > 0) { - quickAddLink = { - node: nn, - port: 0, - portType: PORT_TYPE_INPUT - } - } else { - resetMouseVars(); - } - } - } - if (nn.type === 'junction') { - RED.nodes.addJunction(nn); - } else { - RED.nodes.add(nn); - } - RED.editor.validateNode(nn); - - if (targetGroup) { - RED.group.addToGroup(targetGroup, nn); - if (historyEvent.t !== "multi") { - historyEvent = { - t:'multi', - events: [historyEvent] - } - } - historyEvent.events.push({ - t: "addToGroup", - group: targetGroup, - nodes: nn - }) - - } - - if (spliceLink) { - resetMouseVars(); - // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp/showQuickAddDialog - RED.nodes.removeLink(spliceLink); - var link1 = { - source:spliceLink.source, - sourcePort:spliceLink.sourcePort, - target: nn - }; - var link2 = { - source:nn, - sourcePort:0, - target: spliceLink.target - }; - RED.nodes.addLink(link1); - RED.nodes.addLink(link2); - historyEvent.links = (historyEvent.links || []).concat([link1,link2]); - historyEvent.removedLinks = [spliceLink]; - } - RED.history.push(historyEvent); - RED.nodes.dirty(true); - // auto select dropped node - so info shows (if visible) - clearSelection(); - nn.selected = true; - if (targetGroup) { - selectGroup(targetGroup,false); - enterActiveGroup(targetGroup); - } - movingSet.add(nn); - updateActiveNodes(); - updateSelection(); - redraw(); - // At this point the newly added node will have a real width, - // so check if the position needs nudging - if (lastAddedX !== undefined) { - var lastNodeRHEdge = lastAddedX + lastAddedWidth/2; - var thisNodeLHEdge = nn.x - nn.w/2; - var gap = thisNodeLHEdge - lastNodeRHEdge; - if (gap != gridSize *2) { - nn.x = nn.x + gridSize * 2 - gap; - nn.dirty = true; - nn.x = Math.ceil(nn.x / gridSize) * gridSize; - redraw(); - } - } - if (keepAdding) { - if (lastAddedX === undefined) { - // ghostLink.attr("opacity",1); - setTimeout(function() { - RED.typeSearch.refresh({filter:{input:true}}); - },100); - } - - lastAddedX = nn.x; - lastAddedWidth = nn.w; - - point[0] = nn.x + nn.w/2 + node_width/2 + gridSize * 2; - ghostNode.attr('transform','translate('+(point[0] - node_width/2)+','+(point[1] - node_height/2)+')'); - rebuildQuickAddLink(); - } else { - quickAddActive = false; - ghostNode.remove(); - } - } - }); - - updateActiveNodes(); - updateSelection(); - redraw(); - } - - function canvasMouseMove() { - var i; - var node; - // Prevent touch scrolling... - //if (d3.touches(this)[0]) { - // d3.event.preventDefault(); - //} - - // TODO: auto scroll the container - //var point = d3.mouse(this); - //if (point[0]-container.scrollLeft < 30 && container.scrollLeft > 0) { container.scrollLeft -= 15; } - //console.log(d3.mouse(this),container.offsetWidth,container.offsetHeight,container.scrollLeft,container.scrollTop); - - if (mouse_mode === RED.state.PANNING) { - var pos = [d3.event.pageX,d3.event.pageY]; - if (d3.event.touches) { - var touch0 = d3.event.touches.item(0); - pos = [touch0.pageX, touch0.pageY]; - } - var deltaPos = [ - mouse_position[0]-pos[0], - mouse_position[1]-pos[1] - ]; - - chart.scrollLeft(scroll_position[0]+deltaPos[0]) - chart.scrollTop(scroll_position[1]+deltaPos[1]) - return - } - if (entryCoordinates.x != -1) { - mouse_position = [entryCoordinates.x, entryCoordinates.y] - } else { - mouse_position = d3.touches(this)[0]||d3.mouse(this); - } -if(RED.view.DEBUG) { console.log(`mousemove ${JSON.stringify(mouse_position)}`)} - if (lasso) { - var ox = parseInt(lasso.attr("ox")); - var oy = parseInt(lasso.attr("oy")); - var x = parseInt(lasso.attr("x")); - var y = parseInt(lasso.attr("y")); - var w; - var h; - if (mouse_position[0] < ox) { - x = mouse_position[0]; - w = ox-x; - } else { - w = mouse_position[0]-x; - } - if (mouse_position[1] < oy) { - y = mouse_position[1]; - h = oy-y; - } else { - h = mouse_position[1]-y; - } - lasso - .attr("x",x) - .attr("y",y) - .attr("width",w) - .attr("height",h) - ; - return; - } else if (mouse_mode === RED.state.SLICING || mouse_mode === RED.state.SLICING_JUNCTION) { - if (slicePath) { - var delta = Math.max(1,Math.abs(slicePathLast[0]-mouse_position[0]))*Math.max(1,Math.abs(slicePathLast[1]-mouse_position[1])) - if (delta > 20) { - var currentPath = slicePath.attr("d") - currentPath += " L"+mouse_position[0]+" "+mouse_position[1] - slicePath.attr("d",currentPath); - slicePathLast = mouse_position - } - } - return - } - - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - - if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && mouse_mode != RED.state.DETACHED_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length() === 0) { - return; - } - - var mousePos; - // if (mouse_mode === RED.state.GROUP_RESIZE) { - // mousePos = mouse_position; - // var nx = mousePos[0] + mousedown_group.dx; - // var ny = mousePos[1] + mousedown_group.dy; - // switch(mousedown_group.activeHandle) { - // case 0: mousedown_group.pos.x0 = nx; mousedown_group.pos.y0 = ny; break; - // case 1: mousedown_group.pos.x1 = nx; mousedown_group.pos.y0 = ny; break; - // case 2: mousedown_group.pos.x1 = nx; mousedown_group.pos.y1 = ny; break; - // case 3: mousedown_group.pos.x0 = nx; mousedown_group.pos.y1 = ny; break; - // } - // mousedown_group.dirty = true; - // } - if (mouse_mode == RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) { - // update drag line - if (drag_lines.length === 0 && mousedown_port_type !== null) { - if (d3.event.shiftKey) { - // Get all the wires we need to detach. - var links = []; - var existingLinks = []; - if (selectedLinks.length() > 0) { - selectedLinks.forEach(function(link) { - if (((mousedown_port_type === PORT_TYPE_OUTPUT && - link.source === mousedown_node && - link.sourcePort === mousedown_port_index - ) || - (mousedown_port_type === PORT_TYPE_INPUT && - link.target === mousedown_node - ))) { - existingLinks.push(link); - } - }) - } else { - var filter; - if (mousedown_port_type === PORT_TYPE_OUTPUT) { - filter = { - source:mousedown_node, - sourcePort: mousedown_port_index - } - } else { - filter = { - target: mousedown_node - } - } - existingLinks = RED.nodes.filterLinks(filter); - } - for (i=0;i 3 && !dblClickPrimed) || (dblClickPrimed && d > 10)) { - mouse_mode = RED.state.MOVING_ACTIVE; - clickElapsed = 0; - spliceActive = false; - if (movingSet.length() === 1) { - node = movingSet.get(0); - spliceActive = node.n.hasOwnProperty("_def") && - ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) && - ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) && - RED.nodes.filterLinks({ source: node.n }).length === 0 && - RED.nodes.filterLinks({ target: node.n }).length === 0; - } - } - } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) { - mousePos = mouse_position; - var minX = 0; - var minY = 0; - var maxX = space_width; - var maxY = space_height; - if(RED.view.DEBUG) { console.log(`mousemove - MOVING_ACTIVE ${JSON.stringify(mousePos)}`)} - for (var n = 0; n 0) { - var i = 0; - - // Prefer to snap nodes to the grid if there is one in the selection - do { - node = movingSet.get(i++); - } while(i 0) { - historyEvent = { - t:"delete", - links: removedLinks, - dirty:RED.nodes.dirty() - }; - RED.history.push(historyEvent); - RED.nodes.dirty(true); - } - hideDragLines(); - } - if (lasso) { - var x = parseInt(lasso.attr("x")); - var y = parseInt(lasso.attr("y")); - var x2 = x+parseInt(lasso.attr("width")); - var y2 = y+parseInt(lasso.attr("height")); - var ag = activeGroup; - if (!d3.event.shiftKey) { - clearSelection(); - if (ag) { - if (x < ag.x+ag.w && x2 > ag.x && y < ag.y+ag.h && y2 > ag.y) { - // There was an active group and the lasso intersects with it, - // so reenter the group - enterActiveGroup(ag); - activeGroup.selected = true; - } - } - } - activeGroups.forEach(function(g) { - if (!g.selected) { - if (g.x > x && g.x+g.w < x2 && g.y > y && g.y+g.h < y2) { - if (!activeGroup || RED.group.contains(activeGroup,g)) { - while (g.g && (!activeGroup || g.g !== activeGroup.id)) { - g = RED.nodes.group(g.g); - } - if (!g.selected) { - selectGroup(g,true); - } - } - } - } - }) - - activeNodes.forEach(function(n) { - if (!n.selected) { - if (n.x > x && n.x < x2 && n.y > y && n.y < y2) { - if (!activeGroup || RED.group.contains(activeGroup,n)) { - if (n.g && (!activeGroup || n.g !== activeGroup.id)) { - var group = RED.nodes.group(n.g); - while (group.g && (!activeGroup || group.g !== activeGroup.id)) { - group = RED.nodes.group(group.g); - } - if (!group.selected) { - selectGroup(group,true); - } - } else { - n.selected = true; - n.dirty = true; - movingSet.add(n); - } - } - } - } - }); - activeJunctions.forEach(function(n) { - if (!n.selected) { - if (n.x > x && n.x < x2 && n.y > y && n.y < y2) { - n.selected = true; - n.dirty = true; - movingSet.add(n); - } - } - }) - - - - // var selectionChanged = false; - // do { - // selectionChanged = false; - // selectedGroups.forEach(function(g) { - // if (g.g && g.selected && RED.nodes.group(g.g).selected) { - // g.selected = false; - // selectionChanged = true; - // } - // }) - // } while(selectionChanged); - - if (activeSubflow) { - activeSubflow.in.forEach(function(n) { - n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2); - if (n.selected) { - n.dirty = true; - movingSet.add(n); - } - }); - activeSubflow.out.forEach(function(n) { - n.selected = (n.x > x && n.x < x2 && n.y > y && n.y < y2); - if (n.selected) { - n.dirty = true; - movingSet.add(n); - } - }); - if (activeSubflow.status) { - activeSubflow.status.selected = (activeSubflow.status.x > x && activeSubflow.status.x < x2 && activeSubflow.status.y > y && activeSubflow.status.y < y2); - if (activeSubflow.status.selected) { - activeSubflow.status.dirty = true; - movingSet.add(activeSubflow.status); - } - } - } - updateSelection(); - lasso.remove(); - lasso = null; - } else if (mouse_mode == RED.state.DEFAULT && mousedown_link == null && !d3.event.ctrlKey && !d3.event.metaKey ) { - clearSelection(); - updateSelection(); - } else if (mouse_mode == RED.state.SLICING) { - deleteSelection(); - slicePath.remove(); - slicePath = null; - RED.view.redraw(true); - } else if (mouse_mode == RED.state.SLICING_JUNCTION) { - var removedLinks = new Set() - var addedLinks = [] - var addedJunctions = [] - - var groupedLinks = {} - selectedLinks.forEach(function(l) { - var sourceId = l.source.id+":"+l.sourcePort - groupedLinks[sourceId] = groupedLinks[sourceId] || [] - groupedLinks[sourceId].push(l) - - groupedLinks[l.target.id] = groupedLinks[l.target.id] || [] - groupedLinks[l.target.id].push(l) - }); - var linkGroups = Object.keys(groupedLinks) - linkGroups.sort(function(A,B) { - return groupedLinks[B].length - groupedLinks[A].length - }) - linkGroups.forEach(function(gid) { - var links = groupedLinks[gid] - var junction = { - _def: {defaults:{}}, - type: 'junction', - z: RED.workspaces.active(), - id: RED.nodes.id(), - x: 0, - y: 0, - w: 0, h: 0, - outputs: 1, - inputs: 1, - dirty: true - } - links = links.filter(function(l) { return !removedLinks.has(l) }) - if (links.length === 0) { - return - } - links.forEach(function(l) { - junction.x += l._sliceLocation.x - junction.y += l._sliceLocation.y - }) - junction.x = Math.round(junction.x/links.length) - junction.y = Math.round(junction.y/links.length) - if (snapGrid) { - junction.x = (gridSize*Math.round(junction.x/gridSize)); - junction.y = (gridSize*Math.round(junction.y/gridSize)); - } - - var nodeGroups = new Set() - - RED.nodes.addJunction(junction) - addedJunctions.push(junction) - let newLink - if (gid === links[0].source.id+":"+links[0].sourcePort) { - newLink = { - source: links[0].source, - sourcePort: links[0].sourcePort, - target: junction - } - } else { - newLink = { - source: junction, - sourcePort: 0, - target: links[0].target - } - } - addedLinks.push(newLink) - RED.nodes.addLink(newLink) - links.forEach(function(l) { - removedLinks.add(l) - RED.nodes.removeLink(l) - let newLink - if (gid === l.target.id) { - newLink = { - source: l.source, - sourcePort: l.sourcePort, - target: junction - } - } else { - newLink = { - source: junction, - sourcePort: 0, - target: l.target - } - } - addedLinks.push(newLink) - RED.nodes.addLink(newLink) - nodeGroups.add(l.source.g || "__NONE__") - nodeGroups.add(l.target.g || "__NONE__") - }) - if (nodeGroups.size === 1) { - var group = nodeGroups.values().next().value - if (group !== "__NONE__") { - RED.group.addToGroup(RED.nodes.group(group), junction) - } - } - }) - slicePath.remove(); - slicePath = null; - - if (addedJunctions.length > 0) { - RED.history.push({ - t: 'add', - links: addedLinks, - junctions: addedJunctions, - removedLinks: Array.from(removedLinks) - }) - RED.nodes.dirty(true) - } - RED.view.redraw(true); - } - if (mouse_mode == RED.state.MOVING_ACTIVE) { - if (movingSet.length() > 0) { - var addedToGroup = null; - if (activeHoverGroup) { - for (var j=0;j 0 && mouse_mode == RED.state.MOVING_ACTIVE) { - historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}; - if (activeSpliceLink) { - // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp - var spliceLink = d3.select(activeSpliceLink).data()[0]; - RED.nodes.removeLink(spliceLink); - var link1 = { - source:spliceLink.source, - sourcePort:spliceLink.sourcePort, - target: movingSet.get(0).n - }; - var link2 = { - source:movingSet.get(0).n, - sourcePort:0, - target: spliceLink.target - }; - RED.nodes.addLink(link1); - RED.nodes.addLink(link2); - historyEvent.links = [link1,link2]; - historyEvent.removedLinks = [spliceLink]; - updateActiveNodes(); - } - if (addedToGroup) { - historyEvent.addToGroup = addedToGroup; - } - RED.nodes.dirty(true); - RED.history.push(historyEvent); - } - } - } - // if (mouse_mode === RED.state.MOVING && mousedown_node && mousedown_node.g) { - // if (mousedown_node.gSelected) { - // delete mousedown_node.gSelected - // } else { - // if (!d3.event.ctrlKey && !d3.event.metaKey) { - // clearSelection(); - // } - // RED.nodes.group(mousedown_node.g).selected = true; - // mousedown_node.selected = true; - // mousedown_node.dirty = true; - // movingSet.add(mousedown_node); - // } - // } - if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.DETACHED_DRAGGING) { - // if (mousedown_node) { - // delete mousedown_node.gSelected; - // } - if (mouse_mode === RED.state.DETACHED_DRAGGING) { - var ns = []; - for (var j=0;j 0.3) { - zoomView(scaleFactor-0.1); - } - } - function zoomZero() { zoomView(1); } - function searchFlows() { RED.actions.invoke("core:search", $(this).data("term")); } - function searchPrev() { RED.actions.invoke("core:search-previous"); } - function searchNext() { RED.actions.invoke("core:search-next"); } - - - function zoomView(factor) { - var screenSize = [chart.width(),chart.height()]; - var scrollPos = [chart.scrollLeft(),chart.scrollTop()]; - var center = [(scrollPos[0] + screenSize[0]/2)/scaleFactor,(scrollPos[1] + screenSize[1]/2)/scaleFactor]; - scaleFactor = factor; - var newCenter = [(scrollPos[0] + screenSize[0]/2)/scaleFactor,(scrollPos[1] + screenSize[1]/2)/scaleFactor]; - var delta = [(newCenter[0]-center[0])*scaleFactor,(newCenter[1]-center[1])*scaleFactor] - chart.scrollLeft(scrollPos[0]-delta[0]); - chart.scrollTop(scrollPos[1]-delta[1]); - - RED.view.navigator.resize(); - redraw(); - if (RED.settings.get("editor.view.view-store-zoom")) { - RED.settings.setLocal('zoom-level', factor.toFixed(1)) - } - } - - function selectNone() { - if (mouse_mode === RED.state.MOVING || mouse_mode === RED.state.MOVING_ACTIVE) { - return; - } - if (mouse_mode === RED.state.DETACHED_DRAGGING) { - for (var j=0;j 0) { - activeFlowLinks.push({ - refresh: Math.floor(Math.random()*10000), - node: linkNode, - links: offFlowLinks//offFlows.map(function(i) { return {id:i,links:offFlowLinks[i]};}) - }); - } - } - } - if (activeFlowLinks.length === 0 && selectedLinks.length() > 0) { - selectedLinks.forEach(function(link) { - if (link.link) { - activeLinks.push(link); - activeLinkNodes[link.source.id] = link.source; - link.source.dirty = true; - activeLinkNodes[link.target.id] = link.target; - link.target.dirty = true; - } - }) - } - } else { - selection.flows = workspaceSelection; - } - } - var selectionJSON = activeWorkspace+":"+JSON.stringify(selection,function(key,value) { - if (key === 'nodes' || key === 'flows') { - return value.map(function(n) { return n.id }) - } else if (key === 'link') { - return value.source.id+":"+value.sourcePort+":"+value.target.id; - } else if (key === 'links') { - return value.map(function(link) { - return link.source.id+":"+link.sourcePort+":"+link.target.id; - }); - } - return value; - }); - if (selectionJSON !== lastSelection) { - lastSelection = selectionJSON; - RED.events.emit("view:selection-changed",selection); - } - } - - function editSelection() { - if (movingSet.length() > 0) { - var node = movingSet.get(0).n; - if (node.type === "subflow") { - RED.editor.editSubflow(activeSubflow); - } else if (node.type === "group") { - RED.editor.editGroup(node); - } else { - RED.editor.edit(node); - } - } - } - function deleteSelection(reconnectWires) { - if (mouse_mode === RED.state.SELECTING_NODE) { - return; - } - if (portLabelHover) { - portLabelHover.remove(); - portLabelHover = null; - } - var workspaceSelection = RED.workspaces.selection(); - if (workspaceSelection.length > 0) { - var workspaceCount = 0; - workspaceSelection.forEach(function(ws) { if (ws.type === 'tab') { workspaceCount++ } }); - if (workspaceCount === RED.workspaces.count()) { - // Cannot delete all workspaces - return; - } - var historyEvent = { - t: 'delete', - dirty: RED.nodes.dirty(), - nodes: [], - links: [], - groups: [], - junctions: [], - workspaces: [], - subflows: [] - } - var workspaceOrder = RED.nodes.getWorkspaceOrder().slice(0); - - for (var i=0;i 0 || selectedLinks.length() > 0) { - var result; - var node; - var removedNodes = []; - var removedLinks = []; - var removedGroups = []; - var removedJunctions = []; - var removedSubflowOutputs = []; - var removedSubflowInputs = []; - var removedSubflowStatus; - var subflowInstances = []; - var historyEvents = []; - var addToRemovedLinks = function(links) { - if(!links) { return; } - var _links = Array.isArray(links) ? links : [links]; - _links.forEach(function(l) { - removedLinks.push(l); - selectedLinks.remove(l); - }) - } - if (reconnectWires) { - var reconnectResult = RED.nodes.detachNodes(movingSet.nodes()) - var addedLinks = reconnectResult.newLinks; - if (addedLinks.length > 0) { - historyEvents.push({ t:'add', links: addedLinks }) - } - addToRemovedLinks(reconnectResult.removedLinks) - } - - var startDirty = RED.nodes.dirty(); - var startChanged = false; - var selectedGroups = []; - if (movingSet.length() > 0) { - - for (var i=0;i=0; i--) { - var g = selectedGroups[i]; - removedGroups.push(g); - RED.nodes.removeGroup(g); - } - if (removedSubflowOutputs.length > 0) { - result = RED.subflow.removeOutput(removedSubflowOutputs); - if (result) { - addToRemovedLinks(result.links); - } - } - // Assume 0/1 inputs - if (removedSubflowInputs.length == 1) { - result = RED.subflow.removeInput(); - if (result) { - addToRemovedLinks(result.links); - } - } - if (removedSubflowStatus) { - result = RED.subflow.removeStatus(); - if (result) { - addToRemovedLinks(result.links); - } - } - - var instances = RED.subflow.refresh(true); - if (instances) { - subflowInstances = instances.instances; - } - movingSet.clear(); - if (removedNodes.length > 0 || removedSubflowOutputs.length > 0 || removedSubflowInputs.length > 0 || removedSubflowStatus || removedGroups.length > 0 || removedJunctions.length > 0) { - RED.nodes.dirty(true); - } - } - - if (selectedLinks.length() > 0) { - selectedLinks.forEach(function(link) { - if (link.link) { - var sourceId = link.source.id; - var targetId = link.target.id; - var sourceIdIndex = link.target.links.indexOf(sourceId); - var targetIdIndex = link.source.links.indexOf(targetId); - historyEvents.push({ - t: "edit", - node: link.source, - changed: link.source.changed, - changes: { - links: $.extend(true,{},{v:link.source.links}).v - } - }) - historyEvents.push({ - t: "edit", - node: link.target, - changed: link.target.changed, - changes: { - links: $.extend(true,{},{v:link.target.links}).v - } - }) - link.source.changed = true; - link.target.changed = true; - link.target.links.splice(sourceIdIndex,1); - link.source.links.splice(targetIdIndex,1); - link.source.dirty = true; - link.target.dirty = true; - - } else { - RED.nodes.removeLink(link); - removedLinks.push(link); - } - }) - } - RED.nodes.dirty(true); - var historyEvent = { - t:"delete", - nodes:removedNodes, - links:removedLinks, - groups: removedGroups, - junctions: removedJunctions, - subflowOutputs:removedSubflowOutputs, - subflowInputs:removedSubflowInputs, - subflow: { - id: activeSubflow?activeSubflow.id:undefined, - instances: subflowInstances - }, - dirty:startDirty - }; - if (removedSubflowStatus) { - historyEvent.subflow.status = removedSubflowStatus; - } - if (historyEvents.length > 0) { - historyEvents.unshift(historyEvent); - RED.history.push({ - t:"multi", - events: historyEvents - }) - } else { - RED.history.push(historyEvent); - } - - selectedLinks.clear(); - updateActiveNodes(); - updateSelection(); - redraw(); - } - } - - function copySelection() { - if (mouse_mode === RED.state.SELECTING_NODE) { - return; - } - var nodes = []; - var selection = RED.workspaces.selection(); - if (selection.length > 0) { - nodes = []; - selection.forEach(function(n) { - if (n.type === 'tab') { - nodes.push(n); - nodes = nodes.concat(RED.nodes.groups(n.id)); - nodes = nodes.concat(RED.nodes.filterNodes({z:n.id})); - } - }); - } else { - selection = RED.view.selection(); - if (selection.nodes) { - selection.nodes.forEach(function(n) { - nodes.push(n); - if (n.type === 'group') { - nodes = nodes.concat(RED.group.getNodes(n,true)); - } - }) - } - } - - if (nodes.length > 0) { - var nns = []; - var nodeCount = 0; - var groupCount = 0; - var junctionCount = 0; - var handled = {}; - for (var n=0;n 0) { - RED.notify(RED._("clipboard.nodeCopied",{count:nodeCount}),{id:"clipboard"}); - } else if (groupCount > 0) { - RED.notify(RED._("clipboard.groupCopied",{count:groupCount}),{id:"clipboard"}); - } - } - } - - - function detachSelectedNodes() { - var selection = RED.view.selection(); - if (selection.nodes) { - const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes); - if (removedLinks.length || newLinks.length) { - RED.history.push({ - t: "multi", - events: [ - { t:'delete', links: removedLinks }, - { t:'add', links: newLinks } - ], - dirty: RED.nodes.dirty() - }) - RED.nodes.dirty(true) - } - prepareDrag([selection.nodes[0].x,selection.nodes[0].y]); - mouse_mode = RED.state.DETACHED_DRAGGING; - RED.view.redraw(true); - } - } - - function calculateTextWidth(str, className) { - var result = convertLineBreakCharacter(str); - var width = 0; - for (var i=0;i 1) { - var i=0; - for (i=0;i 0) { - if (drag_lines[0].node === d) { - // Cannot quick-join to self - return - } - if (drag_lines[0].virtualLink && - ( - (drag_lines[0].node.type === 'link in' && d.type !== 'link out') || - (drag_lines[0].node.type === 'link out' && d.type !== 'link in') - ) - ) { - return - } - } - document.body.style.cursor = ""; - if (mouse_mode == RED.state.JOINING || mouse_mode == RED.state.QUICK_JOINING) { - if (typeof TouchEvent != "undefined" && evt instanceof TouchEvent) { - var found = false; - RED.nodes.eachNode(function(n) { - if (n.z == RED.workspaces.active()) { - var hw = n.w/2; - var hh = n.h/2; - if (n.x-hw mouse_position[0] && - n.y-hhmouse_position[1]) { - found = true; - mouseup_node = n; - portType = mouseup_node.inputs>0?PORT_TYPE_INPUT:PORT_TYPE_OUTPUT; - portIndex = 0; - } - } - }); - if (!found && activeSubflow) { - var subflowPorts = []; - if (activeSubflow.status) { - subflowPorts.push(activeSubflow.status) - } - if (activeSubflow.in) { - subflowPorts = subflowPorts.concat(activeSubflow.in) - } - if (activeSubflow.out) { - subflowPorts = subflowPorts.concat(activeSubflow.out) - } - for (var i=0;i mouse_position[0] && - n.y-hhmouse_position[1]) { - found = true; - mouseup_node = n; - portType = mouseup_node.direction === "in"?PORT_TYPE_OUTPUT:PORT_TYPE_INPUT; - portIndex = 0; - break; - } - } - } - } else { - mouseup_node = d; - } - var addedLinks = []; - var removedLinks = []; - var modifiedNodes = []; // joining link nodes - - var select_link = null; - - for (i=0;i 0 || removedLinks.length > 0 || modifiedNodes.length > 0) { - // console.log(addedLinks); - // console.log(removedLinks); - // console.log(modifiedNodes); - var historyEvent; - if (modifiedNodes.length > 0) { - historyEvent = { - t:"multi", - events: linkEditEvents, - dirty:RED.nodes.dirty() - }; - } else { - historyEvent = { - t:"add", - links:addedLinks, - removedLinks: removedLinks, - dirty:RED.nodes.dirty() - }; - } - if (activeSubflow) { - var subflowRefresh = RED.subflow.refresh(true); - if (subflowRefresh) { - historyEvent.subflow = { - id:activeSubflow.id, - changed: activeSubflow.changed, - instances: subflowRefresh.instances - } - } - } - RED.history.push(historyEvent); - updateActiveNodes(); - RED.nodes.dirty(true); - } - if (mouse_mode === RED.state.QUICK_JOINING) { - if (addedLinks.length > 0 || modifiedNodes.length > 0) { - hideDragLines(); - if (portType === PORT_TYPE_INPUT && d.outputs > 0) { - showDragLines([{node:d,port:0,portType:PORT_TYPE_OUTPUT}]); - } else if (portType === PORT_TYPE_OUTPUT && d.inputs > 0) { - showDragLines([{node:d,port:0,portType:PORT_TYPE_INPUT}]); - } else { - resetMouseVars(); - } - mousedown_link = select_link; - if (select_link) { - selectedLinks.clear(); - selectedLinks.add(select_link); - updateSelection(); - } else { - selectedLinks.clear(); - } - } - redraw(); - return; - } - - resetMouseVars(); - hideDragLines(); - if (select_link) { - selectedLinks.clear(); - selectedLinks.add(select_link); - } - mousedown_link = select_link; - if (select_link) { - updateSelection(); - } - redraw(); - } - } - - var portLabelHoverTimeout = null; - var portLabelHover = null; - - - function getElementPosition(node) { - var d3Node = d3.select(node); - if (d3Node.attr('class') === 'red-ui-workspace-chart-event-layer') { - return [0,0]; - } - var result = []; - var localPos = [0,0]; - if (node.nodeName.toLowerCase() === 'g') { - var transform = d3Node.attr("transform"); - if (transform) { - localPos = d3.transform(transform).translate; - } - } else { - localPos = [d3Node.attr("x")||0,d3Node.attr("y")||0]; - } - var parentPos = getElementPosition(node.parentNode); - return [localPos[0]+parentPos[0],localPos[1]+parentPos[1]] - - } - - function getPortLabel(node,portType,portIndex) { - var result; - var nodePortLabels = (portType === PORT_TYPE_INPUT)?node.inputLabels:node.outputLabels; - if (nodePortLabels && nodePortLabels[portIndex]) { - return nodePortLabels[portIndex]; - } - var portLabels = (portType === PORT_TYPE_INPUT)?node._def.inputLabels:node._def.outputLabels; - if (typeof portLabels === 'string') { - result = portLabels; - } else if (typeof portLabels === 'function') { - try { - result = portLabels.call(node,portIndex); - } catch(err) { - console.log("Definition error: "+node.type+"."+((portType === PORT_TYPE_INPUT)?"inputLabels":"outputLabels"),err); - result = null; - } - } else if ($.isArray(portLabels)) { - result = portLabels[portIndex]; - } - return result; - } - function showTooltip(x,y,content,direction) { - var tooltip = eventLayer.append("g") - .attr("transform","translate("+x+","+y+")") - .attr("class","red-ui-flow-port-tooltip"); - - // First check for a user-provided newline - "\\n " - var newlineIndex = content.indexOf("\\n "); - if (newlineIndex > -1 && content[newlineIndex-1] !== '\\') { - content = content.substring(0,newlineIndex)+"..."; - } - - var lines = content.split("\n"); - var labelWidth = 6; - var labelHeight = 12; - var labelHeights = []; - var lineHeight = 0; - lines.forEach(function(l,i) { - var labelDimensions = calculateTextDimensions(l||" ", "red-ui-flow-port-tooltip-label"); - labelWidth = Math.max(labelWidth,labelDimensions[0] + 14); - labelHeights.push(labelDimensions[1]); - if (i === 0) { - lineHeight = labelDimensions[1]; - } - labelHeight += labelDimensions[1]; - }); - var labelWidth1 = (labelWidth/2)-5-2; - var labelWidth2 = labelWidth - 4; - - var labelHeight1 = (labelHeight/2)-5-2; - var labelHeight2 = labelHeight - 4; - var path; - var lx; - var ly = -labelHeight/2; - var anchor; - if (direction === "left") { - path = "M0 0 l -5 -5 v -"+(labelHeight1)+" q 0 -2 -2 -2 h -"+labelWidth+" q -2 0 -2 2 v "+(labelHeight2)+" q 0 2 2 2 h "+labelWidth+" q 2 0 2 -2 v -"+(labelHeight1)+" l 5 -5"; - lx = -14; - anchor = "end"; - } else if (direction === "right") { - path = "M0 0 l 5 -5 v -"+(labelHeight1)+" q 0 -2 2 -2 h "+labelWidth+" q 2 0 2 2 v "+(labelHeight2)+" q 0 2 -2 2 h -"+labelWidth+" q -2 0 -2 -2 v -"+(labelHeight1)+" l -5 -5" - lx = 14; - anchor = "start"; - } else if (direction === "top") { - path = "M0 0 l 5 -5 h "+(labelWidth1)+" q 2 0 2 -2 v -"+labelHeight+" q 0 -2 -2 -2 h -"+(labelWidth2)+" q -2 0 -2 2 v "+labelHeight+" q 0 2 2 2 h "+(labelWidth1)+" l 5 5" - lx = -labelWidth/2 + 6; - ly = -labelHeight-lineHeight+12; - anchor = "start"; - } - tooltip.append("path").attr("d",path); - lines.forEach(function(l,i) { - ly += labelHeights[i]; - // tooltip.append("path").attr("d","M "+(lx-10)+" "+ly+" l 20 0 m -10 -5 l 0 10 ").attr('r',2).attr("stroke","#f00").attr("stroke-width","1").attr("fill","none") - tooltip.append("svg:text").attr("class","red-ui-flow-port-tooltip-label") - .attr("x", lx) - .attr("y", ly) - .attr("text-anchor",anchor) - .text(l||" ") - }); - return tooltip; - } - - function portMouseOver(port,d,portType,portIndex) { - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - clearTimeout(portLabelHoverTimeout); - var active = (mouse_mode!=RED.state.JOINING && mouse_mode != RED.state.QUICK_JOINING) || // Not currently joining - all ports active - ( - drag_lines.length > 0 && // Currently joining - drag_lines[0].portType !== portType && // INPUT->OUTPUT OUTPUT->INPUT - ( - !drag_lines[0].virtualLink || // Not a link wire - (drag_lines[0].node.type === 'link in' && d.type === 'link out') || - (drag_lines[0].node.type === 'link out' && d.type === 'link in') - ) - ) - - if (active && ((portType === PORT_TYPE_INPUT && ((d._def && d._def.inputLabels)||d.inputLabels)) || (portType === PORT_TYPE_OUTPUT && ((d._def && d._def.outputLabels)||d.outputLabels)))) { - portLabelHoverTimeout = setTimeout(function() { - var tooltip = getPortLabel(d,portType,portIndex); - if (!tooltip) { - return; - } - var pos = getElementPosition(port.node()); - portLabelHoverTimeout = null; - portLabelHover = showTooltip( - (pos[0]+(portType===PORT_TYPE_INPUT?-2:12)), - (pos[1]+5), - tooltip, - portType===PORT_TYPE_INPUT?"left":"right" - ); - },500); - } - port.classed("red-ui-flow-port-hovered",active); - } - function portMouseOut(port,d,portType,portIndex) { - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - clearTimeout(portLabelHoverTimeout); - if (portLabelHover) { - portLabelHover.remove(); - portLabelHover = null; - } - port.classed("red-ui-flow-port-hovered",false); - } - - function junctionMouseOver(junction, d, portType) { - var active = (portType === undefined) || - (mouse_mode !== RED.state.JOINING && mouse_mode !== RED.state.QUICK_JOINING) || - (drag_lines.length > 0 && drag_lines[0].portType !== portType && !drag_lines[0].virtualLink) - junction.classed("red-ui-flow-junction-hovered", active); - } - function junctionMouseOut(junction, d) { - junction.classed("red-ui-flow-junction-hovered",false); - } - - function prepareDrag(mouse) { - mouse_mode = RED.state.MOVING; - // Called when movingSet should be prepared to be dragged - for (i=0;i 0 && clickElapsed < dblClickInterval) { - mouse_mode = RED.state.DEFAULT; - if (d.type != "subflow") { - if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) { - RED.workspaces.show(d.type.substring(8)); - } else { - RED.editor.edit(d); - } - } else { - RED.editor.editSubflow(activeSubflow); - } - clickElapsed = 0; - d3.event.stopPropagation(); - return; - } - if (mouse_mode === RED.state.MOVING) { - // Moving primed, but not active. - if (!groupNodeSelectPrimed && !d.selected && d.g && RED.nodes.group(d.g).selected) { - clearSelection(); - - selectGroup(RED.nodes.group(d.g), false); - enterActiveGroup(RED.nodes.group(d.g)) - - mousedown_node.selected = true; - movingSet.add(mousedown_node); - var mouse = d3.touches(this)[0]||d3.mouse(this); - mouse[0] += d.x-d.w/2; - mouse[1] += d.y-d.h/2; - prepareDrag(mouse); - updateSelection(); - return; - } - } - - groupNodeSelectPrimed = false; - - var direction = d._def? (d.inputs > 0 ? 1: 0) : (d.direction == "in" ? 0: 1) - var wasJoining = false; - if (mouse_mode === RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) { - wasJoining = true; - if (drag_lines.length > 0) { - if (drag_lines[0].virtualLink) { - if (d.type === 'link in') { - direction = 1; - } else if (d.type === 'link out') { - direction = 0; - } - } else { - if (drag_lines[0].portType === 1) { - direction = PORT_TYPE_OUTPUT; - } else { - direction = PORT_TYPE_INPUT; - } - } - } - } - - portMouseUp(d, direction, 0); - if (wasJoining) { - d3.selectAll(".red-ui-flow-port-hovered").classed("red-ui-flow-port-hovered",false); - } - } - - - document.addEventListener('pointerlockchange', changeCallback, false); - document.addEventListener('mozpointerlockchange', changeCallback, false); - document.addEventListener('webkitpointerlockchange', changeCallback, false); - document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock; - function getPosition(canvas, event) { - var x, y; - - if (event.x != undefined && event.y != undefined) { - x = event.x; - y = event.y; - } else // Firefox method to get the position - { - x = event.clientX + document.body.scrollLeft + - document.documentElement.scrollLeft; - y = event.clientY + document.body.scrollTop + - document.documentElement.scrollTop; - } - - x -= canvas.offsetLeft == null ? canvas.clientLeft : canvas.offsetLeft; - y -= canvas.offsetTop == null ? canvas.clientTop : canvas.offsetTop; - - return {x:x, y:y}; - } - //temporary proxy for when mouse is locked - function canvasMouseMove_locked(e) { - - var canvas = $(eventLayer[0])[0]; - - - // if we enter this for the first time, get the initial position - if (entryCoordinates.x == -1) { - entryCoordinates = getPosition(canvas, e); - } - - - //get a reference to the canvas - var movementX = e.movementX || - e.mozMovementX || - e.webkitMovementX || - 0; - - var movementY = e.movementY || - e.mozMovementY || - e.webkitMovementY || - 0; - - - // calculate the new coordinates where we should draw the ship - entryCoordinates.x = entryCoordinates.x + movementX; - entryCoordinates.y = entryCoordinates.y + movementY; - - if (entryCoordinates.x > chart.width() -65) { - entryCoordinates.x = chart.width()-65; - } else if (entryCoordinates.x < 0) { - entryCoordinates.x = 0; - } - - if (entryCoordinates.y > chart.height() - 85) { - entryCoordinates.y = chart.height() - 85; - } else if (entryCoordinates.y < 0) { - entryCoordinates.y = 0; - } - - - // determine the direction - var direction = 0; - if (movementX > 0) { - direction = 1; - } else if (movementX < 0) { - direction = -1; - } - // console.log(entryCoordinates) - - - d3.event = e - canvasMouseMove.call(this,e); - //canvasMouseMove.call(document.querySelector("g.red-ui-workspace-chart-event-layer")); - } - function changeCallback(e) { - var canvas = $(eventLayer[0])[0] - // var workspaceChart = $("g.red-ui-workspace-chart-event-layer")[0]; - // var workspaceChart = $("#red-ui-workspace-chart")[0]; - if (document.pointerLockElement === canvas || - document.mozPointerLockElement === canvas || - document.webkitPointerLockElement === canvas) { - - // we've got a pointerlock for our element, add a mouselistener - canvas.addEventListener("mousemove", canvasMouseMove_locked, false); - // outer.on("mousemove", canvasMouseMove_locked) - // document.addEventListener("mousemove", canvasMouseMove_locked, false); - } else { - - // pointer lock is no longer active, remove the callback - canvas.removeEventListener("mousemove", canvasMouseMove_locked, false); - // document.removeEventListener("mousemove", canvasMouseMove_locked, false); - // if(outer.off) {outer.off("mousemove", canvasMouseMove_locked)} - // if(outer.removeEventListener) {outer.removeEventListener("mousemove", canvasMouseMove_locked)} - // and reset the entry coordinates - entryCoordinates = {x:-1, y:-1}; - } - } - - function nodeMouseDown(d) { - if (RED.view.DEBUG) { console.warn("nodeMouseDown", mouse_mode,d); } - try { - // workspaceChart = $("g.red-ui-workspace-chart-event-layer")[0] - var canvas = $(eventLayer[0])[0] - canvas.requestPointerLock = canvas.requestPointerLock || - canvas.mozRequestPointerLock || - canvas.webkitRequestPointerLock; - canvas.requestPointerLock() - console.log("got pointer lock") - } catch (error) { - console.error(error) - } - - focusView(); - if (d3.event.button === 1) { - return; - } - //var touch0 = d3.event; - //var pos = [touch0.pageX,touch0.pageY]; - //RED.touch.radialMenu.show(d3.select(this),pos); - if (mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) { - var historyEvent = RED.history.peek(); - if (activeSpliceLink) { - // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp - var spliceLink = d3.select(activeSpliceLink).data()[0]; - RED.nodes.removeLink(spliceLink); - var link1 = { - source:spliceLink.source, - sourcePort:spliceLink.sourcePort, - target: movingSet.get(0).n - }; - var link2 = { - source:movingSet.get(0).n, - sourcePort:0, - target: spliceLink.target - }; - RED.nodes.addLink(link1); - RED.nodes.addLink(link2); - - historyEvent.links = [link1,link2]; - historyEvent.removedLinks = [spliceLink]; - updateActiveNodes(); - } - - if (activeHoverGroup) { - for (var j=0;j 30 ? 25 : (mousedown_node.w > 0 ? 8 : 3); - if (edgeDelta < targetEdgeDelta) { - if (clickPosition < 0) { - cnodes = [mousedown_node].concat(RED.nodes.getAllUpstreamNodes(mousedown_node)); - } else { - cnodes = [mousedown_node].concat(RED.nodes.getAllDownstreamNodes(mousedown_node)); - } - } else { - cnodes = RED.nodes.getAllFlowNodes(mousedown_node); - } - for (var n=0;n 0) { - var selectClass; - var portType; - if ((drag_lines[0].virtualLink && drag_lines[0].portType === PORT_TYPE_INPUT) || drag_lines[0].portType === PORT_TYPE_OUTPUT) { - selectClass = ".red-ui-flow-port-input .red-ui-flow-port"; - portType = PORT_TYPE_INPUT; - } else { - selectClass = ".red-ui-flow-port-output .red-ui-flow-port"; - portType = PORT_TYPE_OUTPUT; - } - portMouseOver(d3.select(this.parentNode).selectAll(selectClass),d,portType,0); - } - } - } - function nodeMouseOut(d) { - if (RED.view.DEBUG) { console.warn("nodeMouseOut", mouse_mode,d); } - this.parentNode.classList.remove("red-ui-flow-node-hovered"); - clearTimeout(portLabelHoverTimeout); - if (portLabelHover) { - portLabelHover.remove(); - portLabelHover = null; - } - if (mouse_mode === RED.state.JOINING || mouse_mode === RED.state.QUICK_JOINING) { - if (drag_lines.length > 0) { - var selectClass; - var portType; - if ((drag_lines[0].virtualLink && drag_lines[0].portType === PORT_TYPE_INPUT) || drag_lines[0].portType === PORT_TYPE_OUTPUT) { - selectClass = ".red-ui-flow-port-input .red-ui-flow-port"; - portType = PORT_TYPE_INPUT; - } else { - selectClass = ".red-ui-flow-port-output .red-ui-flow-port"; - portType = PORT_TYPE_OUTPUT; - } - portMouseOut(d3.select(this.parentNode).selectAll(selectClass),d,portType,0); - } - } - } - - function portMouseDownProxy(e) { portMouseDown(this.__data__,this.__portType__,this.__portIndex__, e); } - function portTouchStartProxy(e) { portMouseDown(this.__data__,this.__portType__,this.__portIndex__, e); e.preventDefault() } - function portMouseUpProxy(e) { portMouseUp(this.__data__,this.__portType__,this.__portIndex__, e); } - function portTouchEndProxy(e) { portMouseUp(this.__data__,this.__portType__,this.__portIndex__, e); e.preventDefault() } - function portMouseOverProxy(e) { portMouseOver(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); } - function portMouseOutProxy(e) { portMouseOut(d3.select(this), this.__data__,this.__portType__,this.__portIndex__, e); } - - function junctionMouseOverProxy(e) { junctionMouseOver(d3.select(this), this.__data__, this.__portType__) } - function junctionMouseOutProxy(e) { junctionMouseOut(d3.select(this), this.__data__) } - - function linkMouseDown(d) { - if (RED.view.DEBUG) { - console.warn("linkMouseDown", { mouse_mode, point: d3.mouse(this), event: d3.event }); - } - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - if (d3.event.button === 2) { - return - } - mousedown_link = d; - - if (!(d3.event.metaKey || d3.event.ctrlKey)) { - clearSelection(); - } - if (d3.event.metaKey || d3.event.ctrlKey) { - if (!selectedLinks.has(mousedown_link)) { - selectedLinks.add(mousedown_link); - } else { - if (selectedLinks.length() !== 1) { - selectedLinks.remove(mousedown_link); - } - } - } else { - selectedLinks.add(mousedown_link); - } - updateSelection(); - redraw(); - focusView(); - d3.event.stopPropagation(); - if (!mousedown_link.link && movingSet.length() === 0 && (d3.event.touches || d3.event.button === 0) && selectedLinks.length() === 1 && selectedLinks.has(mousedown_link) && (d3.event.metaKey || d3.event.ctrlKey)) { - d3.select(this).classed("red-ui-flow-link-splice",true); - var point = d3.mouse(this); - var clickedGroup = getGroupAt(point[0],point[1]); - showQuickAddDialog({position:point, splice:mousedown_link, group:clickedGroup}); - } - } - function linkTouchStart(d) { - if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - mousedown_link = d; - clearSelection(); - selectedLinks.clear(); - selectedLinks.add(mousedown_link); - updateSelection(); - redraw(); - focusView(); - d3.event.stopPropagation(); - - var obj = d3.select(document.body); - var touch0 = d3.event.touches.item(0); - var pos = [touch0.pageX,touch0.pageY]; - touchStartTime = setTimeout(function() { - touchStartTime = null; - showTouchMenu(obj,pos); - },touchLongPressTimeout); - d3.event.preventDefault(); - } - - function groupMouseUp(g) { - if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) { - mouse_mode = RED.state.DEFAULT; - RED.editor.editGroup(g); - d3.event.stopPropagation(); - return; - } - - } - - function groupMouseDown(g) { - var mouse = d3.touches(this.parentNode)[0]||d3.mouse(this.parentNode); - // if (! (mouse[0] < g.x+10 || mouse[0] > g.x+g.w-10 || mouse[1] < g.y+10 || mouse[1] > g.y+g.h-10) ) { - // return - // } - - focusView(); - if (d3.event.button === 1) { - return; - } - - if (mouse_mode == RED.state.QUICK_JOINING) { - d3.event.stopPropagation(); - return; - } else if (mouse_mode === RED.state.SELECTING_NODE) { - d3.event.stopPropagation(); - return; - } - - mousedown_group = g; - - var now = Date.now(); - clickElapsed = now-clickTime; - clickTime = now; - - dblClickPrimed = ( - lastClickNode == g && - (d3.event.touches || d3.event.button === 0) && - !d3.event.shiftKey && !d3.event.metaKey && !d3.event.altKey && !d3.event.ctrlKey && - clickElapsed < dblClickInterval - ); - lastClickNode = g; - - if (g.selected && (d3.event.ctrlKey||d3.event.metaKey)) { - if (g === activeGroup) { - exitActiveGroup(); - } - deselectGroup(g); - d3.event.stopPropagation(); - } else { - if (!g.selected) { - if (!d3.event.ctrlKey && !d3.event.metaKey) { - var ag = activeGroup; - clearSelection(); - if (ag && g.g === ag.id) { - enterActiveGroup(ag); - activeGroup.selected = true; - } - } - if (activeGroup) { - if (!RED.group.contains(activeGroup,g)) { - // Clicked on a group that is outside the activeGroup - exitActiveGroup(); - } else { - } - } - selectGroup(g,true);//!wasSelected); - } else if (activeGroup && g.g !== activeGroup.id){ - exitActiveGroup(); - } - - - if (d3.event.button != 2) { - var d = g.nodes[0]; - prepareDrag(mouse); - mousedown_group.dx = mousedown_group.x - mouse[0]; - mousedown_group.dy = mousedown_group.y - mouse[1]; - } - } - - updateSelection(); - redraw(); - d3.event.stopPropagation(); - } - - function selectGroup(g, includeNodes, addToMovingSet) { - if (!g.selected) { - g.selected = true; - g.dirty = true; - } - if (addToMovingSet !== false) { - movingSet.add(g); - } - if (includeNodes) { - var currentSet = new Set(movingSet.nodes()); - var allNodes = RED.group.getNodes(g,true); - allNodes.forEach(function(n) { - if (!currentSet.has(n)) { - movingSet.add(n) - // n.selected = true; - } - n.dirty = true; - }) - } - } - function enterActiveGroup(group) { - if (activeGroup) { - exitActiveGroup(); - } - group.active = true; - group.dirty = true; - activeGroup = group; - movingSet.remove(group); - } - function exitActiveGroup() { - if (activeGroup) { - activeGroup.active = false; - activeGroup.dirty = true; - deselectGroup(activeGroup); - selectGroup(activeGroup,true); - activeGroup = null; - } - } - function deselectGroup(g) { - if (g.selected) { - g.selected = false; - g.dirty = true; - } - var nodeSet = new Set(g.nodes); - nodeSet.add(g); - for (var i = movingSet.length()-1; i >= 0; i -= 1) { - var msn = movingSet.get(i); - if (nodeSet.has(msn.n) || msn.n === g) { - msn.n.selected = false; - msn.n.dirty = true; - movingSet.remove(msn.n,i) - } - } - } - function getGroupAt(x,y) { - // x,y expected to be in node-co-ordinate space - var candidateGroups = {}; - for (var i=0;i= g.x && x <= g.x + g.w && y >= g.y && y <= g.y + g.h) { - candidateGroups[g.id] = g; - } - } - var ids = Object.keys(candidateGroups); - if (ids.length > 1) { - ids.forEach(function(id) { - if (candidateGroups[id] && candidateGroups[id].g) { - delete candidateGroups[candidateGroups[id].g] - } - }) - ids = Object.keys(candidateGroups); - } - if (ids.length === 0) { - return null; - } else { - return candidateGroups[ids[ids.length-1]] - } - } - - function isButtonEnabled(d) { - var buttonEnabled = true; - var ws = RED.nodes.workspace(RED.workspaces.active()); - if (ws && !ws.disabled && !d.d) { - if (d._def.button.hasOwnProperty('enabled')) { - if (typeof d._def.button.enabled === "function") { - buttonEnabled = d._def.button.enabled.call(d); - } else { - buttonEnabled = d._def.button.enabled; - } - } - } else { - buttonEnabled = false; - } - return buttonEnabled; - } - - function nodeButtonClicked(d) { - if (mouse_mode === RED.state.SELECTING_NODE) { - if (d3.event) { - d3.event.stopPropagation(); - } - return; - } - var activeWorkspace = RED.workspaces.active(); - var ws = RED.nodes.workspace(activeWorkspace); - if (ws && !ws.disabled && !d.d) { - if (d._def.button.toggle) { - d[d._def.button.toggle] = !d[d._def.button.toggle]; - d.dirty = true; - } - if (d._def.button.onclick) { - try { - d._def.button.onclick.call(d); - } catch(err) { - console.log("Definition error: "+d.type+".onclick",err); - } - } - if (d.dirty) { - redraw(); - } - } else { - if (activeSubflow) { - RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.nodeActionDisabledSubflow")}),"warning"); - } else { - RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.nodeActionDisabled")}),"warning"); - } - } - if (d3.event) { - d3.event.preventDefault(); - } - } - - function showTouchMenu(obj,pos) { - var mdn = mousedown_node; - var options = []; - options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}}); - options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection();deleteSelection();}}); - options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}}); - options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}}); - options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}}); - options.push({name:"select",onselect:function() {selectAll();}}); - options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}}); - options.push({name:"add",onselect:function() { - chartPos = chart.offset(); - showQuickAddDialog({ - position:[pos[0]-chartPos.left+chart.scrollLeft(),pos[1]-chartPos.top+chart.scrollTop()], - touchTrigger:true - }) - }}); - - RED.touch.radialMenu.show(obj,pos,options); - resetMouseVars(); - } - - function createIconAttributes(iconUrl, icon_group, d) { - var fontAwesomeUnicode = null; - if (iconUrl.indexOf("font-awesome/") === 0) { - var iconName = iconUrl.substr(13); - var fontAwesomeUnicode = RED.nodes.fontAwesome.getIconUnicode(iconName); - if (!fontAwesomeUnicode) { - var iconPath = RED.utils.getDefaultNodeIcon(d._def, d); - iconUrl = RED.settings.apiRootUrl+"icons/"+iconPath.module+"/"+iconPath.file; - } - } - if (fontAwesomeUnicode) { - // Since Node-RED workspace uses SVG, i tag cannot be used for font-awesome icon. - // On SVG, use text tag as an alternative. - icon_group.append("text") - .attr("xlink:href",iconUrl) - .attr("class","fa-lg") - .attr("x",15) - .text(fontAwesomeUnicode); - } else { - var icon = icon_group.append("image") - .style("display","none") - .attr("xlink:href",iconUrl) - .attr("class","red-ui-flow-node-icon") - .attr("x",0) - .attr("width","30") - .attr("height","30"); - - var img = new Image(); - img.src = iconUrl; - img.onload = function() { - if (!iconUrl.match(/\.svg$/)) { - var largestEdge = Math.max(img.width,img.height); - var scaleFactor = 1; - if (largestEdge > 30) { - scaleFactor = 30/largestEdge; - } - var width = img.width * scaleFactor; - var height = img.height * scaleFactor; - icon.attr("width",width); - icon.attr("height",height); - icon.attr("x",15-width/2); - } - icon.attr("xlink:href",iconUrl); - icon.style("display",null); - //if ("right" == d._def.align) { - // icon.attr("x",function(d){return d.w-img.width-1-(d.outputs>0?5:0);}); - // icon_shade.attr("x",function(d){return d.w-30}); - // icon_shade_border.attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2);}); - //} - } - } - } - - function redrawStatus(d,nodeEl) { - if (d.z !== RED.workspaces.active()) { - return; - } - if (!nodeEl) { - nodeEl = document.getElementById(d.id); - } - if (nodeEl) { - // Do not show node status if: - // - global flag set - // - node has no status - // - node is disabled - if (!showStatus || !d.status || d.d === true) { - nodeEl.__statusGroup__.style.display = "none"; - } else { - nodeEl.__statusGroup__.style.display = "inline"; - var fill = status_colours[d.status.fill]; // Only allow our colours for now - if (d.status.shape == null && fill == null) { - nodeEl.__statusShape__.style.display = "none"; - nodeEl.__statusGroup__.setAttribute("transform","translate(-14,"+(d.h+3)+")"); - } else { - nodeEl.__statusGroup__.setAttribute("transform","translate(3,"+(d.h+3)+")"); - var statusClass = "red-ui-flow-node-status-"+(d.status.shape||"dot")+"-"+d.status.fill; - nodeEl.__statusShape__.style.display = "inline"; - nodeEl.__statusShape__.setAttribute("class","red-ui-flow-node-status "+statusClass); - } - if (d.status.hasOwnProperty('text')) { - nodeEl.__statusLabel__.textContent = d.status.text; - } else { - nodeEl.__statusLabel__.textContent = ""; - } - } - delete d.dirtyStatus; - } - } - - var pendingRedraw; - - function redraw() { - if (RED.view.DEBUG_SYNC_REDRAW) { - _redraw(); - } else { - if (pendingRedraw) { - cancelAnimationFrame(pendingRedraw); - } - pendingRedraw = requestAnimationFrame(_redraw); - } - } - - function _redraw() { - eventLayer.attr("transform","scale("+scaleFactor+")"); - outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor); - - // Don't bother redrawing nodes if we're drawing links - if (showAllLinkPorts !== -1 || mouse_mode != RED.state.JOINING) { - - var dirtyNodes = {}; - - if (activeSubflow) { - var subflowOutputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-output").data(activeSubflow.out,function(d,i){ return d.id;}); - subflowOutputs.exit().remove(); - var outGroup = subflowOutputs.enter().insert("svg:g").attr("class","red-ui-flow-node red-ui-flow-subflow-port-output") - outGroup.each(function(d,i) { - var node = d3.select(this); - var nodeContents = document.createDocumentFragment(); - - d.h = 40; - d.resize = true; - d.dirty = true; - - var mainRect = document.createElementNS("http://www.w3.org/2000/svg","rect"); - mainRect.__data__ = d; - mainRect.setAttribute("class", "red-ui-flow-subflow-port"); - mainRect.setAttribute("rx", 8); - mainRect.setAttribute("ry", 8); - mainRect.setAttribute("width", 40); - mainRect.setAttribute("height", 40); - node[0][0].__mainRect__ = mainRect; - d3.select(mainRect) - .on("mouseup",nodeMouseUp) - .on("mousedown",nodeMouseDown) - .on("touchstart",nodeTouchStart) - .on("touchend",nodeTouchEnd) - nodeContents.appendChild(mainRect); - - var output_groupEl = document.createElementNS("http://www.w3.org/2000/svg","g"); - output_groupEl.setAttribute("x",0); - output_groupEl.setAttribute("y",0); - node[0][0].__outputLabelGroup__ = output_groupEl; - - var output_output = document.createElementNS("http://www.w3.org/2000/svg","text"); - output_output.setAttribute("class","red-ui-flow-port-label"); - output_output.style["font-size"] = "10px"; - output_output.textContent = "output"; - output_groupEl.appendChild(output_output); - node[0][0].__outputOutput__ = output_output; - - var output_number = document.createElementNS("http://www.w3.org/2000/svg","text"); - output_number.setAttribute("class","red-ui-flow-port-label red-ui-flow-port-index"); - output_number.setAttribute("x",0); - output_number.setAttribute("y",0); - output_number.textContent = d.i+1; - output_groupEl.appendChild(output_number); - node[0][0].__outputNumber__ = output_number; - - var output_border = document.createElementNS("http://www.w3.org/2000/svg","path"); - output_border.setAttribute("d","M 40 1 l 0 38") - output_border.setAttribute("class", "red-ui-flow-node-icon-shade-border") - output_groupEl.appendChild(output_border); - node[0][0].__outputBorder__ = output_border; - - nodeContents.appendChild(output_groupEl); - - var text = document.createElementNS("http://www.w3.org/2000/svg","g"); - text.setAttribute("class","red-ui-flow-port-label"); - text.setAttribute("transform","translate(38,0)"); - text.setAttribute('style', 'fill : #888'); // hard coded here! - node[0][0].__textGroup__ = text; - nodeContents.append(text); - - var portEl = document.createElementNS("http://www.w3.org/2000/svg","g"); - portEl.setAttribute('transform','translate(-5,15)') - - var port = document.createElementNS("http://www.w3.org/2000/svg","rect"); - port.setAttribute("class","red-ui-flow-port"); - port.setAttribute("rx",3); - port.setAttribute("ry",3); - port.setAttribute("width",10); - port.setAttribute("height",10); - portEl.appendChild(port); - port.__data__ = d; - - d3.select(port) - .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} ) - .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);}) - .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);}) - .on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);}); - - node[0][0].__port__ = portEl - nodeContents.appendChild(portEl); - node[0][0].appendChild(nodeContents); - }); - - var subflowInputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-input").data(activeSubflow.in,function(d,i){ return d.id;}); - subflowInputs.exit().remove(); - var inGroup = subflowInputs.enter().insert("svg:g").attr("class","red-ui-flow-node red-ui-flow-subflow-port-input").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"}); - inGroup.each(function(d,i) { - d.w=40; - d.h=40; - }); - inGroup.append("rect").attr("class","red-ui-flow-subflow-port").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40) - // TODO: This is exactly the same set of handlers used for regular nodes - DRY - .on("mouseup",nodeMouseUp) - .on("mousedown",nodeMouseDown) - .on("touchstart",nodeTouchStart) - .on("touchend", nodeTouchEnd); - - inGroup.append("g").attr('transform','translate(35,15)').append("rect").attr("class","red-ui-flow-port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10) - .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_OUTPUT,i);} ) - .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_OUTPUT,i);d3.event.preventDefault();} ) - .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_OUTPUT,i);}) - .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_OUTPUT,i);d3.event.preventDefault();} ) - .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_OUTPUT,0);}) - .on("mouseout",function(d) {portMouseOut(d3.select(this),d,PORT_TYPE_OUTPUT,0);}); - - inGroup.append("svg:text").attr("class","red-ui-flow-port-label").attr("x",18).attr("y",20).style("font-size","10px").text("input"); - - var subflowStatus = nodeLayer.selectAll(".red-ui-flow-subflow-port-status").data(activeSubflow.status?[activeSubflow.status]:[],function(d,i){ return d.id;}); - subflowStatus.exit().remove(); - - var statusGroup = subflowStatus.enter().insert("svg:g").attr("class","red-ui-flow-node red-ui-flow-subflow-port-status").attr("transform",function(d) { return "translate("+(d.x-20)+","+(d.y-20)+")"}); - statusGroup.each(function(d,i) { - d.w=40; - d.h=40; - }); - statusGroup.append("rect").attr("class","red-ui-flow-subflow-port").attr("rx",8).attr("ry",8).attr("width",40).attr("height",40) - // TODO: This is exactly the same set of handlers used for regular nodes - DRY - .on("mouseup",nodeMouseUp) - .on("mousedown",nodeMouseDown) - .on("touchstart",nodeTouchStart) - .on("touchend", nodeTouchEnd); - - statusGroup.append("g").attr('transform','translate(-5,15)').append("rect").attr("class","red-ui-flow-port").attr("rx",3).attr("ry",3).attr("width",10).attr("height",10) - .on("mousedown", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);} ) - .on("touchstart", function(d,i){portMouseDown(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseup", function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);}) - .on("touchend",function(d,i){portMouseUp(d,PORT_TYPE_INPUT,0);d3.event.preventDefault();} ) - .on("mouseover",function(d){portMouseOver(d3.select(this),d,PORT_TYPE_INPUT,0);}) - .on("mouseout",function(d){portMouseOut(d3.select(this),d,PORT_TYPE_INPUT,0);}); - - statusGroup.append("svg:text").attr("class","red-ui-flow-port-label").attr("x",22).attr("y",20).style("font-size","10px").text("status"); - - subflowOutputs.each(function(d,i) { - if (d.dirty) { - - var port_height = 40; - - var self = this; - var thisNode = d3.select(this); - - dirtyNodes[d.id] = d; - - var label = getPortLabel(activeSubflow, PORT_TYPE_OUTPUT, d.i) || ""; - var hideLabel = (label.length < 1) - - var labelParts; - if (d.resize || this.__hideLabel__ !== hideLabel || this.__label__ !== label) { - labelParts = getLabelParts(label, "red-ui-flow-node-label"); - if (labelParts.lines.length !== this.__labelLineCount__ || this.__label__ !== label) { - d.resize = true; - } - this.__label__ = label; - this.__labelLineCount__ = labelParts.lines.length; - - if (hideLabel) { - d.h = Math.max(port_height,(d.outputs || 0) * 15); - } else { - d.h = Math.max(6+24*labelParts.lines.length,(d.outputs || 0) * 15, port_height); - } - this.__hideLabel__ = hideLabel; - } - - if (d.resize) { - var ow = d.w; - if (hideLabel) { - d.w = port_height; - } else { - d.w = Math.max(port_height,20*(Math.ceil((labelParts.width+50+7)/20)) ); - } - if (ow !== undefined) { - d.x += (d.w-ow)/2; - } - d.resize = false; - } - - this.setAttribute("transform", "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"); - // This might be the first redraw after a node has been click-dragged to start a move. - // So its selected state might have changed since the last redraw. - this.classList.toggle("red-ui-flow-node-selected", !!d.selected ) - if (mouse_mode != RED.state.MOVING_ACTIVE) { - this.classList.toggle("red-ui-flow-node-disabled", d.d === true); - this.__mainRect__.setAttribute("width", d.w) - this.__mainRect__.setAttribute("height", d.h) - this.__mainRect__.classList.toggle("red-ui-flow-node-highlighted",!!d.highlighted ); - - if (labelParts) { - // The label has changed - var sa = labelParts.lines; - var sn = labelParts.lines.length; - var textLines = this.__textGroup__.childNodes; - while(textLines.length > sn) { - textLines[textLines.length-1].remove(); - } - for (var i=0; i0?7:0))/20)) ); - } - if (ow !== undefined) { - d.x += (d.w-ow)/2; - } - d.resize = false; - } - if (d._colorChanged) { - var newColor = RED.utils.getNodeColor(d.type,d._def); - this.__mainRect__.setAttribute("fill",newColor); - if (this.__buttonGroupButton__) { - this.__buttonGroupButton__.settAttribute("fill",newColor); - } - delete d._colorChanged; - } - //thisNode.selectAll(".centerDot").attr({"cx":function(d) { return d.w/2;},"cy":function(d){return d.h/2}}); - this.setAttribute("transform", "translate(" + (d.x-d.w/2) + "," + (d.y-d.h/2) + ")"); - // This might be the first redraw after a node has been click-dragged to start a move. - // So its selected state might have changed since the last redraw. - this.classList.toggle("red-ui-flow-node-selected", !!d.selected ) - if (mouse_mode != RED.state.MOVING_ACTIVE) { - this.classList.toggle("red-ui-flow-node-disabled", d.d === true); - this.__mainRect__.setAttribute("width", d.w) - this.__mainRect__.setAttribute("height", d.h) - this.__mainRect__.classList.toggle("red-ui-flow-node-highlighted",!!d.highlighted ); - - if (labelParts) { - // The label has changed - var sa = labelParts.lines; - var sn = labelParts.lines.length; - var textLines = this.__textGroup__.childNodes; - while(textLines.length > sn) { - textLines[textLines.length-1].remove(); - } - for (var i=0; i numOutputs) { - var port = this.__outputs__.pop(); - RED.hooks.trigger("viewRemovePort",{ - node:d, - el:this, - port:port, - portType: "output", - portIndex: this.__outputs__.length - }) - port.remove(); - } - for(var portIndex = 0; portIndex < numOutputs; portIndex++ ) { - var portGroup; - if (portIndex === this.__outputs__.length) { - portGroup = document.createElementNS("http://www.w3.org/2000/svg","g"); - portGroup.setAttribute("class","red-ui-flow-port-output"); - var portPort; - if (d.type === "link out") { - portPort = document.createElementNS("http://www.w3.org/2000/svg","circle"); - portPort.setAttribute("cx",11); - portPort.setAttribute("cy",5); - portPort.setAttribute("r",5); - portPort.setAttribute("class","red-ui-flow-port red-ui-flow-link-port"); - } else { - portPort = document.createElementNS("http://www.w3.org/2000/svg","rect"); - portPort.setAttribute("rx",3); - portPort.setAttribute("ry",3); - portPort.setAttribute("width",10); - portPort.setAttribute("height",10); - portPort.setAttribute("class","red-ui-flow-port"); - } - portGroup.appendChild(portPort); - portGroup.__port__ = portPort; - portPort.__data__ = this.__data__; - portPort.__portType__ = PORT_TYPE_OUTPUT; - portPort.__portIndex__ = portIndex; - portPort.addEventListener("mousedown", portMouseDownProxy); - portPort.addEventListener("touchstart", portTouchStartProxy); - portPort.addEventListener("mouseup", portMouseUpProxy); - portPort.addEventListener("touchend", portTouchEndProxy); - portPort.addEventListener("mouseover", portMouseOverProxy); - portPort.addEventListener("mouseout", portMouseOutProxy); - - this.appendChild(portGroup); - this.__outputs__.push(portGroup); - RED.hooks.trigger("viewAddPort",{node:d,el: this, port: portGroup, portType: "output", portIndex: portIndex}) - } else { - portGroup = this.__outputs__[portIndex]; - } - var x = d.w - 5; - var y = (d.h/2)-((numOutputs-1)/2)*13; - portGroup.setAttribute("transform","translate("+x+","+((y+13*portIndex)-5)+")") - } - if (d._def.icon) { - var icon = thisNode.select(".red-ui-flow-node-icon"); - var faIcon = thisNode.select(".fa-lg"); - var current_url; - if (!icon.empty()) { - current_url = icon.attr("xlink:href"); - } else { - current_url = faIcon.attr("xlink:href"); - } - var new_url = RED.utils.getNodeIcon(d._def,d); - if (new_url !== current_url) { - if (!icon.empty()) { - icon.remove(); - } else { - faIcon.remove(); - } - var iconGroup = thisNode.select(".red-ui-flow-node-icon-group"); - createIconAttributes(new_url, iconGroup, d); - icon = thisNode.select(".red-ui-flow-node-icon"); - faIcon = thisNode.select(".fa-lg"); - } - - icon.attr("y",function(){return (d.h-d3.select(this).attr("height"))/2;}); - this.__iconShade__.setAttribute("height", d.h ); - this.__iconShadeBorder__.setAttribute("d", - "M " + (((!d._def.align && d.inputs !== 0 && d.outputs === 0) || "right" === d._def.align) ? 0 : 30) + " 1 l 0 " + (d.h - 2) - ); - faIcon.attr("y",(d.h+13)/2); - } - // this.__changeBadge__.setAttribute("transform", "translate("+(d.w-10)+", -2)"); - // this.__changeBadge__.classList.toggle("hide", !(d.changed||d.moved)); - // this.__errorBadge__.setAttribute("transform", "translate("+(d.w-10-((d.changed||d.moved)?14:0))+", -2)"); - // this.__errorBadge__.classList.toggle("hide", d.valid); - - thisNode.selectAll(".red-ui-flow-port-input").each(function(d,i) { - var port = d3.select(this); - port.attr("transform",function(d){return "translate(-5,"+((d.h/2)-5)+")";}) - }); - - if (d._def.button) { - var buttonEnabled = isButtonEnabled(d); - this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-disabled", !buttonEnabled); - if(RED.runtime && Object.hasOwn(RED.runtime,'started')) { - this.__buttonGroup__.classList.toggle("red-ui-flow-node-button-stopped", !RED.runtime.started); - } - - var x = d._def.align == "right"?d.w-6:-25; - if (d._def.button.toggle && !d[d._def.button.toggle]) { - x = x - (d._def.align == "right"?8:-8); - } - this.__buttonGroup__.setAttribute("transform", "translate("+x+",2)"); - - if (d._def.button.toggle) { - this.__buttonGroupButton__.setAttribute("fill-opacity",d[d._def.button.toggle]?1:0.2) - this.__buttonGroupBackground__.setAttribute("fill-opacity",d[d._def.button.toggle]?1:0.2) - } - - if (typeof d._def.button.visible === "function") { // is defined and a function... - if (d._def.button.visible.call(d) === false) { - this.__buttonGroup__.style.display = "none"; - } - else { - this.__buttonGroup__.style.display = "inherit"; - } - } - } - // thisNode.selectAll(".node_badge_group").attr("transform",function(d){return "translate("+(d.w-40)+","+(d.h+3)+")";}); - // thisNode.selectAll("text.node_badge_label").text(function(d,i) { - // if (d._def.badge) { - // if (typeof d._def.badge == "function") { - // try { - // return d._def.badge.call(d); - // } catch(err) { - // console.log("Definition error: "+d.type+".badge",err); - // return ""; - // } - // } else { - // return d._def.badge; - // } - // } - // return ""; - // }); - } - - if (d.dirtyStatus) { - redrawStatus(d,this); - } - d.dirty = false; - if (d.g) { - if (!dirtyGroups[d.g]) { - var gg = d.g; - while (gg && !dirtyGroups[gg]) { - dirtyGroups[gg] = RED.nodes.group(gg); - gg = dirtyGroups[gg].g; - } - } - } - } - - RED.hooks.trigger("viewRedrawNode",{node:d,el:this}) - }); - - if (nodesReordered) { - node.sort(function(a,b) { - return a._index - b._index; - }) - } - - var junction = junctionLayer.selectAll(".red-ui-flow-junction").data( - activeJunctions, - d => d.id - ) - var junctionEnter = junction.enter().insert("svg:g").attr("class","red-ui-flow-junction") - junctionEnter.each(function(d,i) { - var junction = d3.select(this); - var contents = document.createDocumentFragment(); - // d.added = true; - var junctionBack = document.createElementNS("http://www.w3.org/2000/svg","rect"); - junctionBack.setAttribute("class","red-ui-flow-junction-background"); - junctionBack.setAttribute("x",-5); - junctionBack.setAttribute("y",-5); - junctionBack.setAttribute("width",10); - junctionBack.setAttribute("height",10); - junctionBack.setAttribute("rx",3); - junctionBack.setAttribute("ry",3); - junctionBack.__data__ = d; - this.__junctionBack__ = junctionBack; - contents.appendChild(junctionBack); - - var junctionInput = document.createElementNS("http://www.w3.org/2000/svg","rect"); - junctionInput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-input"); - junctionInput.setAttribute("x",-5); - junctionInput.setAttribute("y",-5); - junctionInput.setAttribute("width",10); - junctionInput.setAttribute("height",10); - junctionInput.setAttribute("rx",3); - junctionInput.setAttribute("ry",3); - junctionInput.__data__ = d; - junctionInput.__portType__ = PORT_TYPE_INPUT; - junctionInput.__portIndex__ = 0; - this.__junctionInput__ = junctionOutput; - contents.appendChild(junctionInput); - junctionInput.addEventListener("mouseup", portMouseUpProxy); - junctionInput.addEventListener("mousedown", portMouseDownProxy); - - - this.__junctionInput__ = junctionInput; - contents.appendChild(junctionInput); - var junctionOutput = document.createElementNS("http://www.w3.org/2000/svg","rect"); - junctionOutput.setAttribute("class","red-ui-flow-junction-port red-ui-flow-junction-port-output"); - junctionOutput.setAttribute("x",-5); - junctionOutput.setAttribute("y",-5); - junctionOutput.setAttribute("width",10); - junctionOutput.setAttribute("height",10); - junctionOutput.setAttribute("rx",3); - junctionOutput.setAttribute("ry",3); - junctionOutput.__data__ = d; - junctionOutput.__portType__ = PORT_TYPE_OUTPUT; - junctionOutput.__portIndex__ = 0; - this.__junctionOutput__ = junctionOutput; - contents.appendChild(junctionOutput); - junctionOutput.addEventListener("mouseup", portMouseUpProxy); - junctionOutput.addEventListener("mousedown", portMouseDownProxy); - - junctionOutput.addEventListener("mouseover", junctionMouseOverProxy); - junctionOutput.addEventListener("mouseout", junctionMouseOutProxy); - junctionInput.addEventListener("mouseover", junctionMouseOverProxy); - junctionInput.addEventListener("mouseout", junctionMouseOutProxy); - junctionBack.addEventListener("mouseover", junctionMouseOverProxy); - junctionBack.addEventListener("mouseout", junctionMouseOutProxy); - - // These handlers expect to be registered as d3 events - d3.select(junctionBack).on("mousedown", nodeMouseDown).on("mouseup", nodeMouseUp); - - junction[0][0].appendChild(contents); - }) - junction.exit().remove(); - junction.each(function(d) { - var junction = d3.select(this); - this.setAttribute("transform", "translate(" + (d.x) + "," + (d.y) + ")"); - if (d.dirty) { - junction.classed("red-ui-flow-junction-dragging", mouse_mode === RED.state.MOVING_ACTIVE && movingSet.has(d)) - junction.classed("selected", !!d.selected) - dirtyNodes[d.id] = d; - - if (d.g) { - if (!dirtyGroups[d.g]) { - var gg = d.g; - while (gg && !dirtyGroups[gg]) { - dirtyGroups[gg] = RED.nodes.group(gg); - gg = dirtyGroups[gg].g; - } - } - } - - } - - }) - - var link = linkLayer.selectAll(".red-ui-flow-link").data( - activeLinks, - function(d) { - return d.source.id+":"+d.sourcePort+":"+d.target.id+":"+d.target.i; - } - ); - var linkEnter = link.enter().insert("g",".red-ui-flow-node").attr("class","red-ui-flow-link"); - - linkEnter.each(function(d,i) { - var l = d3.select(this); - var pathContents = document.createDocumentFragment(); - - d.added = true; - var pathBack = document.createElementNS("http://www.w3.org/2000/svg","path"); - pathBack.__data__ = d; - pathBack.setAttribute("class","red-ui-flow-link-background red-ui-flow-link-path"+(d.link?" red-ui-flow-link-link":"")); - this.__pathBack__ = pathBack; - pathContents.appendChild(pathBack); - d3.select(pathBack) - .on("mousedown",linkMouseDown) - .on("touchstart",linkTouchStart) - .on("mousemove", function(d) { - if (mouse_mode === RED.state.SLICING) { - - selectedLinks.add(d) - l.classed("red-ui-flow-link-splice",true) - redraw() - } else if (mouse_mode === RED.state.SLICING_JUNCTION && !d.link) { - if (!l.classed("red-ui-flow-link-splice")) { - // Find intersection point - var lineLength = pathLine.getTotalLength(); - var pos; - var delta = Infinity; - for (var i = 0; i < lineLength; i++) { - var linePos = pathLine.getPointAtLength(i); - var posDeltaX = Math.abs(linePos.x-d3.event.offsetX) - var posDeltaY = Math.abs(linePos.y-d3.event.offsetY) - var posDelta = posDeltaX*posDeltaX + posDeltaY*posDeltaY - if (posDelta < delta) { - pos = linePos - delta = posDelta - } - } - d._sliceLocation = pos - selectedLinks.add(d) - l.classed("red-ui-flow-link-splice",true) - redraw() - } - } - }) - - var pathOutline = document.createElementNS("http://www.w3.org/2000/svg","path"); - pathOutline.__data__ = d; - pathOutline.setAttribute("class","red-ui-flow-link-outline red-ui-flow-link-path"); - this.__pathOutline__ = pathOutline; - pathContents.appendChild(pathOutline); - - var pathLine = document.createElementNS("http://www.w3.org/2000/svg","path"); - pathLine.__data__ = d; - pathLine.setAttribute("class","red-ui-flow-link-line red-ui-flow-link-path"+ - (d.link?" red-ui-flow-link-link":(activeSubflow?" red-ui-flow-subflow-link":""))); - this.__pathLine__ = pathLine; - pathContents.appendChild(pathLine); - - l[0][0].appendChild(pathContents); - }); - - link.exit().remove(); - link.each(function(d) { - var link = d3.select(this); - if (d.added || d.selected || dirtyNodes[d.source.id] || dirtyNodes[d.target.id]) { - var numOutputs = d.source.outputs || 1; - var sourcePort = d.sourcePort || 0; - var y = -((numOutputs-1)/2)*13 +13*sourcePort; - d.x1 = d.source.x+(d.source.w/2||0); - d.y1 = d.source.y+y; - d.x2 = d.target.x-(d.target.w/2||0); - d.y2 = d.target.y; - - // return "M "+d.x1+" "+d.y1+ - // " C "+(d.x1+scale*node_width)+" "+(d.y1+scaleY*node_height)+" "+ - // (d.x2-scale*node_width)+" "+(d.y2-scaleY*node_height)+" "+ - // d.x2+" "+d.y2; - var path = generateLinkPath(d.x1,d.y1,d.x2,d.y2,1); - if (/NaN/.test(path)) { - path = "" - } - this.__pathBack__.setAttribute("d",path); - this.__pathOutline__.setAttribute("d",path); - this.__pathLine__.setAttribute("d",path); - this.__pathLine__.classList.toggle("red-ui-flow-node-disabled",!!(d.source.d || d.target.d)); - this.__pathLine__.classList.toggle("red-ui-flow-subflow-link", !d.link && activeSubflow); - } - - this.classList.toggle("red-ui-flow-link-selected", !!d.selected); - - var connectedToUnknown = !!(d.target.type == "unknown" || d.source.type == "unknown"); - this.classList.toggle("red-ui-flow-link-unknown",!!(d.target.type == "unknown" || d.source.type == "unknown")) - delete d.added; - }) - var offLinks = linkLayer.selectAll(".red-ui-flow-link-off-flow").data( - activeFlowLinks, - function(d) { - return d.node.id+":"+d.refresh - } - ); - - var offLinksEnter = offLinks.enter().insert("g",".red-ui-flow-node").attr("class","red-ui-flow-link-off-flow"); - offLinksEnter.each(function(d,i) { - var g = d3.select(this); - var s = 1; - var labelAnchor = "start"; - if (d.node.type === "link in") { - s = -1; - labelAnchor = "end"; - } - var stemLength = s*30; - var branchLength = s*20; - var l = g.append("svg:path").attr("class","red-ui-flow-link-link").attr("d","M 0 0 h "+stemLength); - var links = d.links; - var flows = Object.keys(links); - var tabOrder = RED.nodes.getWorkspaceOrder(); - flows.sort(function(A,B) { - return tabOrder.indexOf(A) - tabOrder.indexOf(B); - }); - var linkWidth = 10; - var h = node_height; - var y = -(flows.length-1)*h/2; - var linkGroups = g.selectAll(".red-ui-flow-link-group").data(flows); - var enterLinkGroups = linkGroups.enter().append("g").attr("class","red-ui-flow-link-group") - .on('mouseover', function() { if (mouse_mode !== 0) { return } d3.select(this).classed('red-ui-flow-link-group-active',true)}) - .on('mouseout', function() {if (mouse_mode !== 0) { return } d3.select(this).classed('red-ui-flow-link-group-active',false)}) - .on('mousedown', function() { d3.event.preventDefault(); d3.event.stopPropagation(); }) - .on('mouseup', function(f) { - if (mouse_mode !== 0) { - return - } - d3.event.stopPropagation(); - var targets = d.links[f]; - RED.workspaces.show(f); - targets.forEach(function(n) { - n.selected = true; - n.dirty = true; - movingSet.add(n); - if (targets.length === 1) { - RED.view.reveal(n.id); - } - }); - updateSelection(); - redraw(); - }); - enterLinkGroups.each(function(f) { - var linkG = d3.select(this); - linkG.append("svg:path") - .attr("class","red-ui-flow-link-link") - .attr("d", - "M "+stemLength+" 0 "+ - "C "+(stemLength+(1.7*branchLength))+" "+0+ - " "+(stemLength+(0.1*branchLength))+" "+y+" "+ - (stemLength+branchLength*1.5)+" "+y+" " - ); - linkG.append("svg:path") - .attr("class","red-ui-flow-link-port") - .attr("d", - "M "+(stemLength+branchLength*1.5+s*(linkWidth+7))+" "+(y-12)+" "+ - "h "+(-s*linkWidth)+" "+ - "a 3 3 45 0 "+(s===1?"0":"1")+" "+(s*-3)+" 3 "+ - "v 18 "+ - "a 3 3 45 0 "+(s===1?"0":"1")+" "+(s*3)+" 3 "+ - "h "+(s*linkWidth) - ); - linkG.append("svg:path") - .attr("class","red-ui-flow-link-port") - .attr("d", - "M "+(stemLength+branchLength*1.5+s*(linkWidth+10))+" "+(y-12)+" "+ - "h "+(s*(linkWidth*3))+" "+ - "M "+(stemLength+branchLength*1.5+s*(linkWidth+10))+" "+(y+12)+" "+ - "h "+(s*(linkWidth*3)) - ).style("stroke-dasharray","12 3 8 4 3"); - linkG.append("rect").attr("class","red-ui-flow-port red-ui-flow-link-port") - .attr("x",stemLength+branchLength*1.5-4+(s*4)) - .attr("y",y-4) - .attr("rx",2) - .attr("ry",2) - .attr("width",8) - .attr("height",8); - linkG.append("rect") - .attr("x",stemLength+branchLength*1.5-(s===-1?node_width:0)) - .attr("y",y-12) - .attr("width",node_width) - .attr("height",24) - .style("stroke","none") - .style("fill","transparent") - var tab = RED.nodes.workspace(f); - var label; - if (tab) { - label = tab.label || tab.id; - } - linkG.append("svg:text") - .attr("class","red-ui-flow-port-label") - .attr("x",stemLength+branchLength*1.5+(s*15)) - .attr("y",y+1) - .style("font-size","10px") - .style("text-anchor",labelAnchor) - .text(label); - - y += h; - }); - linkGroups.exit().remove(); - }); - offLinks.exit().remove(); - offLinks = linkLayer.selectAll(".red-ui-flow-link-off-flow"); - offLinks.each(function(d) { - var s = 1; - if (d.node.type === "link in") { - s = -1; - } - var link = d3.select(this); - link.attr("transform", function(d) { return "translate(" + (d.node.x+(s*d.node.w/2)) + "," + (d.node.y) + ")"; }); - - }) - - var group = groupLayer.selectAll(".red-ui-flow-group").data(activeGroups,function(d) { return d.id }); - group.exit().each(function(d,i) { - document.getElementById("group_select_"+d.id).remove() - }).remove(); - var groupEnter = group.enter().insert("svg:g").attr("class", "red-ui-flow-group") - var addedGroups = false; - groupEnter.each(function(d,i) { - addedGroups = true; - var g = d3.select(this); - g.attr("id",d.id); - - var groupBorderRadius = 4; - - var selectGroup = groupSelectLayer.append('g').attr("class", "red-ui-flow-group").attr("id","group_select_"+d.id); - selectGroup.append('rect').classed("red-ui-flow-group-outline-select",true) - .classed("red-ui-flow-group-outline-select-background",true) - .attr('rx',groupBorderRadius).attr('ry',groupBorderRadius) - .attr("x",-4) - .attr("y",-4); - - - selectGroup.append('rect').classed("red-ui-flow-group-outline-select",true) - .attr('rx',groupBorderRadius).attr('ry',groupBorderRadius) - .attr("x",-4) - .attr("y",-4) - selectGroup.on("mousedown", function() {groupMouseDown.call(g[0][0],d)}); - selectGroup.on("mouseup", function() {groupMouseUp.call(g[0][0],d)}); - selectGroup.on("touchstart", function() {groupMouseDown.call(g[0][0],d); d3.event.preventDefault();}); - selectGroup.on("touchend", function() {groupMouseUp.call(g[0][0],d); d3.event.preventDefault();}); - - g.append('rect').classed("red-ui-flow-group-outline",true).attr('rx',0.5).attr('ry',0.5); - - g.append('rect').classed("red-ui-flow-group-body",true) - .attr('rx',groupBorderRadius).attr('ry',groupBorderRadius).style({ - "fill":d.fill||"none", - "stroke": d.stroke||"none", - }) - g.on("mousedown",groupMouseDown).on("mouseup",groupMouseUp) - g.on("touchstart", function() {groupMouseDown.call(g[0][0],d); d3.event.preventDefault();}); - g.on("touchend", function() {groupMouseUp.call(g[0][0],d); d3.event.preventDefault();}); - - g.append('svg:text').attr("class","red-ui-flow-group-label"); - d.dirty = true; - }); - if (addedGroups) { - group.sort(function(a,b) { - if (a._root === b._root) { - return a._depth - b._depth; - } else { - return a._index - b._index; - } - }) - } - group[0].reverse(); - var groupOpCount=0; - group.each(function(d,i) { - groupOpCount++ - if (d.resize) { - d.minWidth = 0; - delete d.resize; - } - if (d.dirty || dirtyGroups[d.id]) { - var g = d3.select(this); - var recalculateLabelOffsets = false; - if (d.nodes.length > 0) { - // If the group was just moved, all of its contents was - // also moved - so no need to recalculate its bounding box - if (!d.groupMoved) { - var minX = Number.POSITIVE_INFINITY; - var minY = Number.POSITIVE_INFINITY; - var maxX = 0; - var maxY = 0; - var margin = 26; - d.nodes.forEach(function(n) { - groupOpCount++ - if (n.type !== "group") { - minX = Math.min(minX,n.x-n.w/2-margin-((n._def.button && n._def.align!=="right")?20:0)); - minY = Math.min(minY,n.y-n.h/2-margin); - maxX = Math.max(maxX,n.x+n.w/2+margin+((n._def.button && n._def.align=="right")?20:0)); - maxY = Math.max(maxY,n.y+n.h/2+margin); - } else { - minX = Math.min(minX,n.x-margin) - minY = Math.min(minY,n.y-margin) - maxX = Math.max(maxX,n.x+n.w+margin) - maxY = Math.max(maxY,n.y+n.h+margin) - } - }); - - d.x = minX; - d.y = minY; - d.w = maxX - minX; - d.h = maxY - minY; - recalculateLabelOffsets = true; - // if set explicitly to false, this group has just been - // imported so needed this initial resize calculation. - // Now that's done, delete the flag so the normal - // logic kicks in. - if (d.groupMoved === false) { - delete d.groupMoved; - } - } else { - delete d.groupMoved; - } - } else { - d.w = 40; - d.h = 40; - recalculateLabelOffsets = true; - } - if (recalculateLabelOffsets) { - if (!d.minWidth) { - if (d.style.label && d.name) { - var labelParts = getLabelParts(d.name||"","red-ui-flow-group-label"); - d.minWidth = labelParts.width + 8; - d.labels = labelParts.lines; - } else { - d.minWidth = 40; - d.labels = []; - } - } - d.w = Math.max(d.minWidth,d.w); - if (d.style.label && d.labels.length > 0) { - var labelPos = d.style["label-position"] || "nw"; - var h = (d.labels.length-1) * 16; - if (labelPos[0] === "s") { - h += 8; - } - d.h += h; - if (labelPos[0] === "n") { - if (d.nodes.length > 0) { - d.y -= h; - } - } - } - } - - g.attr("transform","translate("+d.x+","+d.y+")") - g.selectAll(".red-ui-flow-group-outline") - .attr("width",d.w) - .attr("height",d.h) - - - var selectGroup = document.getElementById("group_select_"+d.id); - selectGroup.setAttribute("transform","translate("+d.x+","+d.y+")"); - if (d.hovered) { - selectGroup.classList.add("red-ui-flow-group-hovered") - } else { - selectGroup.classList.remove("red-ui-flow-group-hovered") - } - var selectGroupRect = selectGroup.children[0]; - selectGroupRect.setAttribute("width",d.w+8) - selectGroupRect.setAttribute("height",d.h+8) - selectGroupRect.style.strokeOpacity = (d.active || d.selected || d.highlighted)?0.8:0; - selectGroupRect.style.strokeDasharray = (d.active)?"10 4":""; - selectGroupRect = selectGroup.children[1]; - selectGroupRect.setAttribute("width",d.w+8) - selectGroupRect.setAttribute("height",d.h+8) - selectGroupRect.style.strokeOpacity = (d.active || d.selected || d.highlighted)?0.8:0; - selectGroupRect.style.strokeDasharray = (d.active)?"10 4":""; - - if (d.highlighted) { - selectGroup.classList.add("red-ui-flow-node-highlighted"); - } else { - selectGroup.classList.remove("red-ui-flow-node-highlighted"); - } - - - g.selectAll(".red-ui-flow-group-body") - .attr("width",d.w) - .attr("height",d.h) - .style("stroke", d.style.stroke || "") - .style("stroke-opacity", d.style.hasOwnProperty('stroke-opacity') ? d.style['stroke-opacity'] : "") - .style("fill", d.style.fill || "") - .style("fill-opacity", d.style.hasOwnProperty('fill-opacity') ? d.style['fill-opacity'] : "") - - var label = g.selectAll(".red-ui-flow-group-label"); - label.classed("hide",!!!d.style.label) - if (d.style.label) { - var labelPos = d.style["label-position"] || "nw"; - var labelX = 0; - var labelY = 0; - - if (labelPos[0] === 'n') { - labelY = 0+15; // Allow for font-height - } else { - labelY = d.h - 5 -(d.labels.length -1) * 16; - } - if (labelPos[1] === 'w') { - labelX = 5; - labelAnchor = "start" - } else if (labelPos[1] === 'e') { - labelX = d.w-5; - labelAnchor = "end" - } else { - labelX = d.w/2; - labelAnchor = "middle" - } - if (d.style.hasOwnProperty('color')) { - label.style("fill",d.style.color) - } else { - label.style("fill",null) - } - label.attr("transform","translate("+labelX+","+labelY+")") - .attr("text-anchor",labelAnchor); - if (d.labels) { - var ypos = 0; - g.selectAll(".red-ui-flow-group-label-text").remove(); - d.labels.forEach(function (name) { - label.append("tspan") - .classed("red-ui-flow-group-label-text", true) - .text(name) - .attr("x", 0) - .attr("y", ypos); - ypos += 16; - }); - } else { - g.selectAll(".red-ui-flow-group-label-text").remove(); - } - } - - delete dirtyGroups[d.id]; - delete d.dirty; - } - }) - } else { - // JOINING - unselect any selected links - linkLayer.selectAll(".red-ui-flow-link-selected").data( - activeLinks, - function(d) { - return d.source.id+":"+d.sourcePort+":"+d.target.id+":"+d.target.i; - } - ).classed("red-ui-flow-link-selected", false); - } - RED.view.navigator.refresh(); - if (d3.event) { - d3.event.preventDefault(); - } - } - - function focusView() { - try { - // Workaround for browser unexpectedly scrolling iframe into full - // view - record the parent scroll position and restore it after - // setting the focus - var scrollX = window.parent.window.scrollX; - var scrollY = window.parent.window.scrollY; - chart.trigger("focus"); - window.parent.window.scrollTo(scrollX,scrollY); - } catch(err) { - // In case we're iframed into a page of a different origin, just focus - // the view following the inevitable DOMException - chart.trigger("focus"); - } - } - - - /** - * Imports a new collection of nodes from a JSON String. - * - * - all get new IDs assigned - * - all "selected" - * - attached to mouse for placing - "IMPORT_DRAGGING" - * @param {String/Array} newNodesObj nodes to import - * @param {Object} options options object - * - * Options: - * - addFlow - whether to import nodes to a new tab - * - touchImport - whether this is a touch import. If not, imported nodes are - * attachedto mouse for placing - "IMPORT_DRAGGING" state - * - generateIds - whether to automatically generate new ids for all imported nodes - * - generateDefaultNames - whether to automatically update any nodes with clashing - * default names - */ - function importNodes(newNodesObj,options) { - options = options || { - addFlow: false, - touchImport: false, - generateIds: false, - generateDefaultNames: false - } - var addNewFlow = options.addFlow - var touchImport = options.touchImport; - - if (mouse_mode === RED.state.SELECTING_NODE) { - return; - } - - var nodesToImport; - if (typeof newNodesObj === "string") { - if (newNodesObj === "") { - return; - } - try { - nodesToImport = JSON.parse(newNodesObj); - } catch(err) { - var e = new Error(RED._("clipboard.invalidFlow",{message:err.message})); - e.code = "NODE_RED"; - throw e; - } - } else { - nodesToImport = newNodesObj; - } - - if (!$.isArray(nodesToImport)) { - nodesToImport = [nodesToImport]; - } - if (options.generateDefaultNames) { - RED.actions.invoke("core:generate-node-names", nodesToImport, { - renameBlank: false, - renameClash: true, - generateHistory: false - }) - } - - try { - var activeSubflowChanged; - if (activeSubflow) { - activeSubflowChanged = activeSubflow.changed; - } - var result = RED.nodes.import(nodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap}); - if (result) { - var new_nodes = result.nodes; - var new_links = result.links; - var new_groups = result.groups; - var new_junctions = result.junctions; - var new_workspaces = result.workspaces; - var new_subflows = result.subflows; - var removedNodes = result.removedNodes; - var new_default_workspace = result.missingWorkspace; - if (addNewFlow && new_default_workspace) { - RED.workspaces.show(new_default_workspace.id); - } - var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() }); - new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()})) - new_ms = new_ms.concat(new_junctions.filter(function(j) { return j.z === RED.workspaces.active()})) - var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; }); - - clearSelection(); - movingSet.clear(); - movingSet.add(new_ms); - - - // TODO: pick a more sensible root node - if (movingSet.length() > 0) { - if (mouse_position == null) { - mouse_position = [0,0]; - } - - var dx = mouse_position[0]; - var dy = mouse_position[1]; - if (movingSet.length() > 0) { - var root_node = movingSet.get(0).n; - dx = root_node.x; - dy = root_node.y; - } - - var minX = 0; - var minY = 0; - var i; - var node,group; - var l =movingSet.length(); - for (i=0;i 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) && - ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) - - - } - } - - } - - var historyEvent = { - t:"add", - nodes:new_node_ids, - links:new_links, - groups:new_groups, - junctions: new_junctions, - workspaces:new_workspaces, - subflows:new_subflows, - dirty:RED.nodes.dirty() - }; - if (movingSet.length() === 0) { - RED.nodes.dirty(true); - } - if (activeSubflow) { - var subflowRefresh = RED.subflow.refresh(true); - if (subflowRefresh) { - historyEvent.subflow = { - id:activeSubflow.id, - changed: activeSubflowChanged, - instances: subflowRefresh.instances - } - } - } - if (removedNodes) { - var replaceEvent = { - t: "replace", - config: removedNodes - } - historyEvent = { - t:"multi", - events: [ - replaceEvent, - historyEvent - ] - } - } - - RED.history.push(historyEvent); - - updateActiveNodes(); - redraw(); - - var counts = []; - var newNodeCount = 0; - var newConfigNodeCount = 0; - new_nodes.forEach(function(n) { - if (n.hasOwnProperty("x") && n.hasOwnProperty("y")) { - newNodeCount++; - } else { - newConfigNodeCount++; - } - }) - var newGroupCount = new_groups.length; - var newJunctionCount = new_junctions.length; - if (new_workspaces.length > 0) { - counts.push(RED._("clipboard.flow",{count:new_workspaces.length})); - } - if (newNodeCount > 0) { - counts.push(RED._("clipboard.node",{count:newNodeCount})); - } - if (newGroupCount > 0) { - counts.push(RED._("clipboard.group",{count:newGroupCount})); - } - if (newConfigNodeCount > 0) { - counts.push(RED._("clipboard.configNode",{count:newConfigNodeCount})); - } - if (new_subflows.length > 0) { - counts.push(RED._("clipboard.subflow",{count:new_subflows.length})); - } - if (removedNodes && removedNodes.length > 0) { - counts.push(RED._("clipboard.replacedNodes",{count:removedNodes.length})); - } - if (counts.length > 0) { - var countList = "
  • "+counts.join("
  • ")+"
"; - RED.notify("

"+RED._("clipboard.nodesImported")+"

"+countList,{id:"clipboard"}); - } - - } - } catch(error) { - if (error.code === "import_conflict") { - // Pass this up for the called to resolve - throw error; - } else if (error.code != "NODE_RED") { - console.log(error.stack); - RED.notify(RED._("notification.error",{message:error.toString()}),"error"); - } else { - RED.notify(RED._("notification.error",{message:error.message}),"error"); - } - } - } - - function toggleShowGrid(state) { - if (state) { - gridLayer.style("visibility","visible"); - } else { - gridLayer.style("visibility","hidden"); - } - } - function toggleSnapGrid(state) { - snapGrid = state; - redraw(); - } - function toggleStatus(s) { - showStatus = s; - RED.nodes.eachNode(function(n) { n.dirtyStatus = true; n.dirty = true;}); - //TODO: subscribe/unsubscribe here - redraw(); - } - function setSelectedNodeState(isDisabled) { - if (mouse_mode === RED.state.SELECTING_NODE) { - return; - } - var workspaceSelection = RED.workspaces.selection(); - var changed = false; - if (workspaceSelection.length > 0) { - // TODO: toggle workspace state - } else if (movingSet.length() > 0) { - var historyEvents = []; - for (var i=0;i 0) { - RED.history.push({ - t:"multi", - events: historyEvents, - dirty:RED.nodes.dirty() - }) - RED.nodes.dirty(true) - } - } - RED.view.redraw(); - - } - function getSelection() { - var selection = {}; - - var allNodes = new Set(); - - if (movingSet.length() > 0) { - movingSet.forEach(function(n) { - if (n.n.type !== 'group') { - allNodes.add(n.n); - } - }); - } - var selectedGroups = activeGroups.filter(function(g) { return g.selected && !g.active }); - if (selectedGroups.length > 0) { - if (selectedGroups.length === 1 && selectedGroups[0].active) { - // Let nodes be nodes - } else { - selectedGroups.forEach(function(g) { - var groupNodes = RED.group.getNodes(g,true); - groupNodes.forEach(function(n) { - allNodes.delete(n); - }); - allNodes.add(g); - }); - } - } - if (allNodes.size > 0) { - selection.nodes = Array.from(allNodes); - } - if (selectedLinks.length() > 0) { - selection.links = selectedLinks.toArray(); - selection.link = selection.links[0]; - } - return selection; - } - - /** - * Create a node from a type string. - * **NOTE:** Can throw on error - use `try` `catch` block when calling - * @param {string} type The node type to create - * @param {number} [x] (optional) The horizontal position on the workspace - * @param {number} [y] (optional)The vertical on the workspace - * @param {string} [z] (optional) The flow tab this node will belong to. Defaults to active workspace. - * @returns An object containing the `node` and a `historyEvent` - * @private - */ - function createNode(type, x, y, z) { - var m = /^subflow:(.+)$/.exec(type); - var activeSubflow = z ? RED.nodes.subflow(z) : null; - if (activeSubflow && m) { - var subflowId = m[1]; - if (subflowId === activeSubflow.id) { - throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddSubflowToItself") })) - } - if (RED.nodes.subflowContains(m[1], activeSubflow.id)) { - throw new Error(RED._("notification.error", { message: RED._("notification.errors.cannotAddCircularReference") })) - } - } - - var nn = { id: RED.nodes.id(), z: z || RED.workspaces.active() }; - - nn.type = type; - nn._def = RED.nodes.getType(nn.type); - - if (!m) { - nn.inputs = nn._def.inputs || 0; - nn.outputs = nn._def.outputs; - - for (var d in nn._def.defaults) { - if (nn._def.defaults.hasOwnProperty(d)) { - if (nn._def.defaults[d].value !== undefined) { - nn[d] = JSON.parse(JSON.stringify(nn._def.defaults[d].value)); - } - } - } - - if (nn._def.onadd) { - try { - nn._def.onadd.call(nn); - } catch (err) { - console.log("Definition error: " + nn.type + ".onadd:", err); - } - } - } else { - var subflow = RED.nodes.subflow(m[1]); - nn.name = ""; - nn.inputs = subflow.in.length; - nn.outputs = subflow.out.length; - } - - nn.changed = true; - nn.moved = true; - - nn.w = RED.view.node_width; - nn.h = Math.max(RED.view.node_height, (nn.outputs || 0) * 15); - nn.resize = true; - if (x != null && typeof x == "number" && x >= 0) { - nn.x = x; - } - if (y != null && typeof y == "number" && y >= 0) { - nn.y = y; - } - var historyEvent = { - t: "add", - nodes: [nn.id], - dirty: RED.nodes.dirty() - } - if (activeSubflow) { - var subflowRefresh = RED.subflow.refresh(true); - if (subflowRefresh) { - historyEvent.subflow = { - id: activeSubflow.id, - changed: activeSubflow.changed, - instances: subflowRefresh.instances - } - } - } - return { - node: nn, - historyEvent: historyEvent - } - } - - function calculateNodeDimensions(node) { - var result = [node_width,node_height]; - try { - var isLink = (node.type === "link in" || node.type === "link out") - var hideLabel = node.hasOwnProperty('l')?!node.l : isLink; - var label = RED.utils.getNodeLabel(node, node.type); - var labelParts = getLabelParts(label, "red-ui-flow-node-label"); - if (hideLabel) { - result[1] = Math.max(node_height,(node.outputs || 0) * 15); - } else { - result[1] = Math.max(6+24*labelParts.lines.length,(node.outputs || 0) * 15, 30); - } - if (hideLabel) { - result[0] = node_height; - } else { - result[0] = Math.max(node_width,20*(Math.ceil((labelParts.width+50+(node._def.inputs>0?7:0))/20)) ); - } - }catch(err) { - console.log("Error",node); - } - return result; - } - - - function flashNode(n) { - let node = n; - if(typeof node === "string") { node = RED.nodes.node(n); } - if(!node) { return; } - - const flashingNode = flashingNodeId && RED.nodes.node(flashingNodeId); - if(flashingNode) { - //cancel current flashing node before flashing new node - clearInterval(flashingNode.__flashTimer); - delete flashingNode.__flashTimer; - flashingNode.dirty = true; - flashingNode.highlighted = false; - } - node.__flashTimer = setInterval(function(flashEndTime, n) { - n.dirty = true; - if (flashEndTime >= Date.now()) { - n.highlighted = !n.highlighted; - } else { - clearInterval(n.__flashTimer); - delete n.__flashTimer; - flashingNodeId = null; - n.highlighted = false; - } - RED.view.redraw(); - }, 100, Date.now() + 2200, node) - flashingNodeId = node.id; - node.highlighted = true; - RED.view.redraw(); - } - return { - init: init, - state:function(state) { - if (state == null) { - return mouse_mode - } else { - mouse_mode = state; - } - }, - - updateActive: updateActiveNodes, - redraw: function(updateActive, syncRedraw) { - if (updateActive) { - updateActiveNodes(); - updateSelection(); - } - if (syncRedraw) { - _redraw(); - } else { - redraw(); - } - }, - focus: focusView, - importNodes: importNodes, - calculateTextWidth: calculateTextWidth, - select: function(selection) { - if (typeof selection !== "undefined") { - clearSelection(); - if (typeof selection == "string") { - var selectedNode = RED.nodes.node(selection); - if (selectedNode) { - selectedNode.selected = true; - selectedNode.dirty = true; - movingSet.clear(); - movingSet.add(selectedNode); - } - } else if (selection) { - if (selection.nodes) { - updateActiveNodes(); - movingSet.clear(); - // TODO: this selection group span groups - // - if all in one group -> activate the group - // - if in multiple groups (or group/no-group) - // -> select the first 'set' of things in the same group/no-group - selection.nodes.forEach(function(n) { - if (n.type !== "group") { - n.selected = true; - n.dirty = true; - movingSet.add(n); - } else { - selectGroup(n,true); - } - }) - } - } - } - updateSelection(); - redraw(true); - }, - selection: getSelection, - clearSelection: clearSelection, - createNode: createNode, - /** default node width */ - get node_width() { - return node_width; - }, - /** default node height */ - get node_height() { - return node_height; - }, - /** snap to grid option state */ - get snapGrid() { - return snapGrid; - }, - /** gets the current scale factor */ - scale: function() { - return scaleFactor; - }, - getLinksAtPoint: function(x,y) { - // x,y must be in SVG co-ordinate space - // if they come from a node.x/y, they will need to be scaled using - // scaleFactor first. - var result = []; - var links = outer.selectAll(".red-ui-flow-link-background")[0]; - for (var i=0;i= bb.x && y >= bb.y && x <= bb.x+bb.width && y <= bb.y+bb.height) { - result.push(links[i]) - } - } - return result; - }, - getGroupAtPoint: getGroupAt, - getActiveGroup: function() { return activeGroup }, - reveal: function(id,triggerHighlight) { - if (RED.nodes.workspace(id) || RED.nodes.subflow(id)) { - RED.workspaces.show(id, null, null, true); - } else { - var node = RED.nodes.node(id) || RED.nodes.group(id); - if (node) { - if (node.z && (node.type === "group" || node._def.category !== 'config')) { - node.dirty = true; - RED.workspaces.show(node.z); - - var screenSize = [chart[0].clientWidth/scaleFactor,chart[0].clientHeight/scaleFactor]; - var scrollPos = [chart.scrollLeft()/scaleFactor,chart.scrollTop()/scaleFactor]; - var cx = node.x; - var cy = node.y; - if (node.type === "group") { - cx += node.w/2; - cy += node.h/2; - } - if (cx < scrollPos[0] || cy < scrollPos[1] || cx > screenSize[0]+scrollPos[0] || cy > screenSize[1]+scrollPos[1]) { - var deltaX = '-='+(((scrollPos[0] - cx) + screenSize[0]/2)*scaleFactor); - var deltaY = '-='+(((scrollPos[1] - cy) + screenSize[1]/2)*scaleFactor); - chart.animate({ - scrollLeft: deltaX, - scrollTop: deltaY - },200); - } - if (triggerHighlight !== false) { - flashNode(node); - } - } else if (node._def.category === 'config') { - RED.sidebar.config.show(id); - } - } - } - }, - gridSize: function(v) { - if (v === undefined) { - return gridSize; - } else { - gridSize = Math.max(5,v); - updateGrid(); - } - }, - getActiveNodes: function() { - return activeNodes; - }, - getSubflowPorts: function() { - var result = []; - if (activeSubflow) { - var subflowOutputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-output").data(activeSubflow.out,function(d,i){ return d.id;}); - subflowOutputs.each(function(d,i) { result.push(d) }) - var subflowInputs = nodeLayer.selectAll(".red-ui-flow-subflow-port-input").data(activeSubflow.in,function(d,i){ return d.id;}); - subflowInputs.each(function(d,i) { result.push(d) }) - var subflowStatus = nodeLayer.selectAll(".red-ui-flow-subflow-port-status").data(activeSubflow.status?[activeSubflow.status]:[],function(d,i){ return d.id;}); - subflowStatus.each(function(d,i) { result.push(d) }) - } - return result; - }, - selectNodes: function(options) { - $("#red-ui-workspace-tabs-shade").show(); - $("#red-ui-palette-shade").show(); - $("#red-ui-sidebar-shade").show(); - $("#red-ui-header-shade").show(); - $("#red-ui-workspace").addClass("red-ui-workspace-select-mode"); - - mouse_mode = RED.state.SELECTING_NODE; - clearSelection(); - if (options.selected) { - options.selected.forEach(function(id) { - var n = RED.nodes.node(id); - if (n) { - n.selected = true; - n.dirty = true; - movingSet.add(n); - } - }) - } - redraw(); - selectNodesOptions = options||{}; - var closeNotification = function() { - clearSelection(); - $("#red-ui-workspace-tabs-shade").hide(); - $("#red-ui-palette-shade").hide(); - $("#red-ui-sidebar-shade").hide(); - $("#red-ui-header-shade").hide(); - $("#red-ui-workspace").removeClass("red-ui-workspace-select-mode"); - resetMouseVars(); - notification.close(); - } - selectNodesOptions.done = function(selection) { - closeNotification(); - if (selectNodesOptions.onselect) { - selectNodesOptions.onselect(selection); - } - } - var buttons = [{ - text: RED._("common.label.cancel"), - click: function(e) { - closeNotification(); - if (selectNodesOptions.oncancel) { - selectNodesOptions.oncancel(); - } - } - }]; - if (!selectNodesOptions.single) { - buttons.push({ - text: RED._("common.label.done"), - class: "primary", - click: function(e) { - var selection = movingSet.nodes() - selectNodesOptions.done(selection); - } - }); - } - var notification = RED.notify(selectNodesOptions.prompt || RED._("workspace.selectNodes"),{ - modal: false, - fixed: true, - type: "compact", - buttons: buttons - }) - }, - scroll: function(x,y) { - chart.scrollLeft(chart.scrollLeft()+x); - chart.scrollTop(chart.scrollTop()+y) - }, - clickNodeButton: function(n) { - if (n._def.button) { - nodeButtonClicked(n); - } - }, - clipboard: function() { - return clipboard - }, - redrawStatus: redrawStatus, - showQuickAddDialog:showQuickAddDialog, - calculateNodeDimensions: calculateNodeDimensions, - getElementPosition:getElementPosition, - showTooltip:showTooltip - }; -})();