mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	Link nodes show hidden wires when selected
☕️
			
			
This commit is contained in:
		@@ -622,7 +622,6 @@ RED.editor = (function() {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        editing_node.dirty = true;
 | 
					                        editing_node.dirty = true;
 | 
				
			||||||
                        validateNode(editing_node);
 | 
					                        validateNode(editing_node);
 | 
				
			||||||
                        RED.view.redraw(true);
 | 
					 | 
				
			||||||
                        RED.tray.close();
 | 
					                        RED.tray.close();
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -676,6 +675,7 @@ RED.editor = (function() {
 | 
				
			|||||||
                    RED.sidebar.info.refresh(editing_node);
 | 
					                    RED.sidebar.info.refresh(editing_node);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                RED.workspaces.refresh();
 | 
					                RED.workspaces.refresh();
 | 
				
			||||||
 | 
					                RED.view.redraw(true);
 | 
				
			||||||
                editStack.pop();
 | 
					                editStack.pop();
 | 
				
			||||||
                if (editStack.length === 0) {
 | 
					                if (editStack.length === 0) {
 | 
				
			||||||
                    RED.view.focus();
 | 
					                    RED.view.focus();
 | 
				
			||||||
@@ -1010,7 +1010,7 @@ RED.editor = (function() {
 | 
				
			|||||||
                        validateNode(user);
 | 
					                        validateNode(user);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    RED.nodes.dirty(true);
 | 
					                    RED.nodes.dirty(true);
 | 
				
			||||||
                    RED.view.redraw();
 | 
					                    RED.view.redraw(true);
 | 
				
			||||||
                    RED.history.push(historyEvent);
 | 
					                    RED.history.push(historyEvent);
 | 
				
			||||||
                    RED.tray.close(function() {
 | 
					                    RED.tray.close(function() {
 | 
				
			||||||
                        updateConfigNodeSelect(configProperty,configType,"",prefix);
 | 
					                        updateConfigNodeSelect(configProperty,configType,"",prefix);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,7 @@ RED.view = (function() {
 | 
				
			|||||||
    var activeSubflow = null;
 | 
					    var activeSubflow = null;
 | 
				
			||||||
    var activeNodes = [];
 | 
					    var activeNodes = [];
 | 
				
			||||||
    var activeLinks = [];
 | 
					    var activeLinks = [];
 | 
				
			||||||
 | 
					    var activeFlowLinks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var selected_link = null,
 | 
					    var selected_link = null,
 | 
				
			||||||
        mousedown_link = null,
 | 
					        mousedown_link = null,
 | 
				
			||||||
@@ -81,9 +82,9 @@ RED.view = (function() {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var vis = outer
 | 
					    var vis = outer
 | 
				
			||||||
        .append('svg:g')
 | 
					        .append("svg:g")
 | 
				
			||||||
        .on("dblclick.zoom", null)
 | 
					        .on("dblclick.zoom", null)
 | 
				
			||||||
        .append('svg:g')
 | 
					        .append("svg:g")
 | 
				
			||||||
        .on("mousemove", canvasMouseMove)
 | 
					        .on("mousemove", canvasMouseMove)
 | 
				
			||||||
        .on("mousedown", canvasMouseDown)
 | 
					        .on("mousedown", canvasMouseDown)
 | 
				
			||||||
        .on("mouseup", canvasMouseUp)
 | 
					        .on("mouseup", canvasMouseUp)
 | 
				
			||||||
@@ -107,18 +108,18 @@ RED.view = (function() {
 | 
				
			|||||||
                d3.event.preventDefault();
 | 
					                d3.event.preventDefault();
 | 
				
			||||||
                touch0 = d3.event.touches.item(0);
 | 
					                touch0 = d3.event.touches.item(0);
 | 
				
			||||||
                var touch1 = d3.event.touches.item(1);
 | 
					                var touch1 = d3.event.touches.item(1);
 | 
				
			||||||
                var a = touch0['pageY']-touch1['pageY'];
 | 
					                var a = touch0["pageY"]-touch1["pageY"];
 | 
				
			||||||
                var b = touch0['pageX']-touch1['pageX'];
 | 
					                var b = touch0["pageX"]-touch1["pageX"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var offset = $("#chart").offset();
 | 
					                var offset = $("#chart").offset();
 | 
				
			||||||
                var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
 | 
					                var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
 | 
				
			||||||
                startTouchCenter = [
 | 
					                startTouchCenter = [
 | 
				
			||||||
                    (touch1['pageX']+(b/2)-offset.left+scrollPos[0])/scaleFactor,
 | 
					                    (touch1["pageX"]+(b/2)-offset.left+scrollPos[0])/scaleFactor,
 | 
				
			||||||
                    (touch1['pageY']+(a/2)-offset.top+scrollPos[1])/scaleFactor
 | 
					                    (touch1["pageY"]+(a/2)-offset.top+scrollPos[1])/scaleFactor
 | 
				
			||||||
                ];
 | 
					                ];
 | 
				
			||||||
                moveTouchCenter = [
 | 
					                moveTouchCenter = [
 | 
				
			||||||
                    touch1['pageX']+(b/2),
 | 
					                    touch1["pageX"]+(b/2),
 | 
				
			||||||
                    touch1['pageY']+(a/2)
 | 
					                    touch1["pageY"]+(a/2)
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
                startTouchDistance = Math.sqrt((a*a)+(b*b));
 | 
					                startTouchDistance = Math.sqrt((a*a)+(b*b));
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -131,7 +132,7 @@ RED.view = (function() {
 | 
				
			|||||||
                touchStartTime = setTimeout(function() {
 | 
					                touchStartTime = setTimeout(function() {
 | 
				
			||||||
                    touchStartTime = null;
 | 
					                    touchStartTime = null;
 | 
				
			||||||
                    showTouchMenu(obj,pos);
 | 
					                    showTouchMenu(obj,pos);
 | 
				
			||||||
                    //lasso = vis.append('rect')
 | 
					                    //lasso = vis.append("rect")
 | 
				
			||||||
                    //    .attr("ox",point[0])
 | 
					                    //    .attr("ox",point[0])
 | 
				
			||||||
                    //    .attr("oy",point[1])
 | 
					                    //    .attr("oy",point[1])
 | 
				
			||||||
                    //    .attr("rx",2)
 | 
					                    //    .attr("rx",2)
 | 
				
			||||||
@@ -168,14 +169,14 @@ RED.view = (function() {
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    touch0 = d3.event.touches.item(0);
 | 
					                    touch0 = d3.event.touches.item(0);
 | 
				
			||||||
                    var touch1 = d3.event.touches.item(1);
 | 
					                    var touch1 = d3.event.touches.item(1);
 | 
				
			||||||
                    var a = touch0['pageY']-touch1['pageY'];
 | 
					                    var a = touch0["pageY"]-touch1["pageY"];
 | 
				
			||||||
                    var b = touch0['pageX']-touch1['pageX'];
 | 
					                    var b = touch0["pageX"]-touch1["pageX"];
 | 
				
			||||||
                    var offset = $("#chart").offset();
 | 
					                    var offset = $("#chart").offset();
 | 
				
			||||||
                    var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
 | 
					                    var scrollPos = [$("#chart").scrollLeft(),$("#chart").scrollTop()];
 | 
				
			||||||
                    var moveTouchDistance = Math.sqrt((a*a)+(b*b));
 | 
					                    var moveTouchDistance = Math.sqrt((a*a)+(b*b));
 | 
				
			||||||
                    var touchCenter = [
 | 
					                    var touchCenter = [
 | 
				
			||||||
                        touch1['pageX']+(b/2),
 | 
					                        touch1["pageX"]+(b/2),
 | 
				
			||||||
                        touch1['pageY']+(a/2)
 | 
					                        touch1["pageY"]+(a/2)
 | 
				
			||||||
                    ];
 | 
					                    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (!isNaN(moveTouchDistance)) {
 | 
					                    if (!isNaN(moveTouchDistance)) {
 | 
				
			||||||
@@ -197,13 +198,13 @@ RED.view = (function() {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var outer_background = vis.append('svg:rect')
 | 
					    var outer_background = vis.append("svg:rect")
 | 
				
			||||||
        .attr('width', space_width)
 | 
					        .attr("width", space_width)
 | 
				
			||||||
        .attr('height', space_height)
 | 
					        .attr("height", space_height)
 | 
				
			||||||
        .attr('fill','#fff');
 | 
					        .attr("fill","#fff");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var gridScale = d3.scale.linear().range([0,space_width]).domain([0,space_width]);
 | 
					    var gridScale = d3.scale.linear().range([0,space_width]).domain([0,space_width]);
 | 
				
			||||||
    var grid = vis.append('g');
 | 
					    var grid = vis.append("g");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    grid.selectAll("line.horizontal").data(gridScale.ticks(space_width/gridSize)).enter()
 | 
					    grid.selectAll("line.horizontal").data(gridScale.ticks(space_width/gridSize)).enter()
 | 
				
			||||||
       .append("line")
 | 
					       .append("line")
 | 
				
			||||||
@@ -235,7 +236,7 @@ RED.view = (function() {
 | 
				
			|||||||
           });
 | 
					           });
 | 
				
			||||||
    grid.style("visibility","hidden");
 | 
					    grid.style("visibility","hidden");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var dragGroup = vis.append('g');
 | 
					    var dragGroup = vis.append("g");
 | 
				
			||||||
    var drag_lines = [];
 | 
					    var drag_lines = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function showDragLines(nodes) {
 | 
					    function showDragLines(nodes) {
 | 
				
			||||||
@@ -301,10 +302,10 @@ RED.view = (function() {
 | 
				
			|||||||
            redraw();
 | 
					            redraw();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $('#btn-zoom-out').click(function() {zoomOut();});
 | 
					        $("#btn-zoom-out").click(function() {zoomOut();});
 | 
				
			||||||
        $('#btn-zoom-zero').click(function() {zoomZero();});
 | 
					        $("#btn-zoom-zero").click(function() {zoomZero();});
 | 
				
			||||||
        $('#btn-zoom-in').click(function() {zoomIn();});
 | 
					        $("#btn-zoom-in").click(function() {zoomIn();});
 | 
				
			||||||
        $("#chart").on('DOMMouseScroll mousewheel', function (evt) {
 | 
					        $("#chart").on("DOMMouseScroll mousewheel", function (evt) {
 | 
				
			||||||
            if ( evt.altKey ) {
 | 
					            if ( evt.altKey ) {
 | 
				
			||||||
                evt.preventDefault();
 | 
					                evt.preventDefault();
 | 
				
			||||||
                evt.stopPropagation();
 | 
					                evt.stopPropagation();
 | 
				
			||||||
@@ -368,7 +369,7 @@ RED.view = (function() {
 | 
				
			|||||||
                nn.h = Math.max(node_height,(nn.outputs||0) * 15);
 | 
					                nn.h = Math.max(node_height,(nn.outputs||0) * 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var historyEvent = {
 | 
					                var historyEvent = {
 | 
				
			||||||
                    t:'add',
 | 
					                    t:"add",
 | 
				
			||||||
                    nodes:[nn.id],
 | 
					                    nodes:[nn.id],
 | 
				
			||||||
                    dirty:RED.nodes.dirty()
 | 
					                    dirty:RED.nodes.dirty()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -398,7 +399,7 @@ RED.view = (function() {
 | 
				
			|||||||
                nn.x = mousePos[0];
 | 
					                nn.x = mousePos[0];
 | 
				
			||||||
                nn.y = mousePos[1];
 | 
					                nn.y = mousePos[1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var spliceLink = $(ui.helper).data('splice');
 | 
					                var spliceLink = $(ui.helper).data("splice");
 | 
				
			||||||
                if (spliceLink) {
 | 
					                if (spliceLink) {
 | 
				
			||||||
                    // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
 | 
					                    // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
 | 
				
			||||||
                    RED.nodes.removeLink(spliceLink);
 | 
					                    RED.nodes.removeLink(spliceLink);
 | 
				
			||||||
@@ -471,7 +472,7 @@ RED.view = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (!touchStartTime) {
 | 
					            if (!touchStartTime) {
 | 
				
			||||||
                var point = d3.mouse(this);
 | 
					                var point = d3.mouse(this);
 | 
				
			||||||
                lasso = vis.append('rect')
 | 
					                lasso = vis.append("rect")
 | 
				
			||||||
                    .attr("ox",point[0])
 | 
					                    .attr("ox",point[0])
 | 
				
			||||||
                    .attr("oy",point[1])
 | 
					                    .attr("oy",point[1])
 | 
				
			||||||
                    .attr("rx",1)
 | 
					                    .attr("rx",1)
 | 
				
			||||||
@@ -678,12 +679,12 @@ RED.view = (function() {
 | 
				
			|||||||
                                svgRect.height = 1;
 | 
					                                svgRect.height = 1;
 | 
				
			||||||
                                nodes = outer[0][0].getIntersectionList(svgRect, outer[0][0]);
 | 
					                                nodes = outer[0][0].getIntersectionList(svgRect, outer[0][0]);
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                // Firefox doesn't do getIntersectionList and that
 | 
					                                // Firefox doesn"t do getIntersectionList and that
 | 
				
			||||||
                                // makes us sad
 | 
					                                // makes us sad
 | 
				
			||||||
                                nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
 | 
					                                nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            for (var i=0;i<nodes.length;i++) {
 | 
					                            for (var i=0;i<nodes.length;i++) {
 | 
				
			||||||
                                if (d3.select(nodes[i]).classed('link_background')) {
 | 
					                                if (d3.select(nodes[i]).classed("link_background")) {
 | 
				
			||||||
                                    var length = nodes[i].getTotalLength();
 | 
					                                    var length = nodes[i].getTotalLength();
 | 
				
			||||||
                                    for (var j=0;j<length;j+=10) {
 | 
					                                    for (var j=0;j<length;j+=10) {
 | 
				
			||||||
                                        var p = nodes[i].getPointAtLength(j);
 | 
					                                        var p = nodes[i].getPointAtLength(j);
 | 
				
			||||||
@@ -696,12 +697,12 @@ RED.view = (function() {
 | 
				
			|||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if (activeSpliceLink && activeSpliceLink !== bestLink) {
 | 
					                            if (activeSpliceLink && activeSpliceLink !== bestLink) {
 | 
				
			||||||
                                d3.select(activeSpliceLink.parentNode).classed('link_splice',false);
 | 
					                                d3.select(activeSpliceLink.parentNode).classed("link_splice",false);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if (bestLink) {
 | 
					                            if (bestLink) {
 | 
				
			||||||
                                d3.select(bestLink.parentNode).classed('link_splice',true)
 | 
					                                d3.select(bestLink.parentNode).classed("link_splice",true)
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                d3.select('.link_splice').classed('link_splice',false);
 | 
					                                d3.select(".link_splice").classed("link_splice",false);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            activeSpliceLink = bestLink;
 | 
					                            activeSpliceLink = bestLink;
 | 
				
			||||||
                            spliceTimer = null;
 | 
					                            spliceTimer = null;
 | 
				
			||||||
@@ -728,7 +729,7 @@ RED.view = (function() {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            historyEvent = {
 | 
					            historyEvent = {
 | 
				
			||||||
                t:'delete',
 | 
					                t:"delete",
 | 
				
			||||||
                links: removedLinks,
 | 
					                links: removedLinks,
 | 
				
			||||||
                dirty:RED.nodes.dirty()
 | 
					                dirty:RED.nodes.dirty()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
@@ -781,7 +782,7 @@ RED.view = (function() {
 | 
				
			|||||||
                for (var j=0;j<moving_set.length;j++) {
 | 
					                for (var j=0;j<moving_set.length;j++) {
 | 
				
			||||||
                    ns.push({n:moving_set[j].n,ox:moving_set[j].ox,oy:moving_set[j].oy});
 | 
					                    ns.push({n:moving_set[j].n,ox:moving_set[j].ox,oy:moving_set[j].oy});
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                historyEvent = {t:'move',nodes:ns,dirty:RED.nodes.dirty()};
 | 
					                historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()};
 | 
				
			||||||
                if (activeSpliceLink) {
 | 
					                if (activeSpliceLink) {
 | 
				
			||||||
                    // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
 | 
					                    // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
 | 
				
			||||||
                    var spliceLink = d3.select(activeSpliceLink).data()[0];
 | 
					                    var spliceLink = d3.select(activeSpliceLink).data()[0];
 | 
				
			||||||
@@ -889,6 +890,70 @@ RED.view = (function() {
 | 
				
			|||||||
        if (selected_link != null) {
 | 
					        if (selected_link != null) {
 | 
				
			||||||
            selection.link = selected_link;
 | 
					            selection.link = selected_link;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        var activeWorkspace = RED.workspaces.active();
 | 
				
			||||||
 | 
					        activeLinks = RED.nodes.filterLinks({
 | 
				
			||||||
 | 
					            source:{z:activeWorkspace},
 | 
				
			||||||
 | 
					            target:{z:activeWorkspace}
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        var tabOrder = RED.nodes.getWorkspaceOrder();
 | 
				
			||||||
 | 
					        var currentLinks = activeLinks;
 | 
				
			||||||
 | 
					        var addedLinkLinks = {};
 | 
				
			||||||
 | 
					        activeFlowLinks = [];
 | 
				
			||||||
 | 
					        for (var i=0;i<moving_set.length;i++) {
 | 
				
			||||||
 | 
					            if (moving_set[i].n.type === "link out" || moving_set[i].n.type === "link in") {
 | 
				
			||||||
 | 
					                var linkNode = moving_set[i].n;
 | 
				
			||||||
 | 
					                var offFlowLinks = {};
 | 
				
			||||||
 | 
					                linkNode.links.forEach(function(id) {
 | 
				
			||||||
 | 
					                    var target = RED.nodes.node(id);
 | 
				
			||||||
 | 
					                    if (target) {
 | 
				
			||||||
 | 
					                        if (linkNode.type === "link out") {
 | 
				
			||||||
 | 
					                            if (target.z === linkNode.z) {
 | 
				
			||||||
 | 
					                                if (!addedLinkLinks[linkNode.id+":"+target.id]) {
 | 
				
			||||||
 | 
					                                    activeLinks.push({
 | 
				
			||||||
 | 
					                                        source:linkNode,
 | 
				
			||||||
 | 
					                                        sourcePort:0,
 | 
				
			||||||
 | 
					                                        target: target,
 | 
				
			||||||
 | 
					                                        link: true
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					                                    addedLinkLinks[linkNode.id+":"+target.id] = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                offFlowLinks[target.z] = offFlowLinks[target.z]||[];
 | 
				
			||||||
 | 
					                                offFlowLinks[target.z].push(target);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            if (target.z === linkNode.z) {
 | 
				
			||||||
 | 
					                                if (!addedLinkLinks[target.id+":"+linkNode.id]) {
 | 
				
			||||||
 | 
					                                    activeLinks.push({
 | 
				
			||||||
 | 
					                                        source:target,
 | 
				
			||||||
 | 
					                                        sourcePort:0,
 | 
				
			||||||
 | 
					                                        target: linkNode,
 | 
				
			||||||
 | 
					                                        link: true
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					                                    addedLinkLinks[target.id+":"+linkNode.id] = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                offFlowLinks[target.z] = offFlowLinks[target.z]||[];
 | 
				
			||||||
 | 
					                                offFlowLinks[target.z].push(target);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                var offFlows = Object.keys(offFlowLinks);
 | 
				
			||||||
 | 
					                // offFlows.sort(function(A,B) {
 | 
				
			||||||
 | 
					                //     return tabOrder.indexOf(A) - tabOrder.indexOf(B);
 | 
				
			||||||
 | 
					                // });
 | 
				
			||||||
 | 
					                if (offFlows.length > 0) {
 | 
				
			||||||
 | 
					                    activeFlowLinks.push({
 | 
				
			||||||
 | 
					                        refresh: Math.floor(Math.random()*10000),
 | 
				
			||||||
 | 
					                        node: linkNode,
 | 
				
			||||||
 | 
					                        links: offFlowLinks//offFlows.map(function(i) { return {id:i,links:offFlowLinks[i]};})
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RED.events.emit("view:selection-changed",selection);
 | 
					        RED.events.emit("view:selection-changed",selection);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -900,7 +965,7 @@ RED.view = (function() {
 | 
				
			|||||||
                delete moving_set[i].ox;
 | 
					                delete moving_set[i].ox;
 | 
				
			||||||
                delete moving_set[i].oy;
 | 
					                delete moving_set[i].oy;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            RED.history.push({t:'move',nodes:ns,dirty:RED.nodes.dirty()});
 | 
					            RED.history.push({t:"move",nodes:ns,dirty:RED.nodes.dirty()});
 | 
				
			||||||
            RED.nodes.dirty(true);
 | 
					            RED.nodes.dirty(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -994,7 +1059,7 @@ RED.view = (function() {
 | 
				
			|||||||
                RED.nodes.dirty(true);
 | 
					                RED.nodes.dirty(true);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            var historyEvent = {
 | 
					            var historyEvent = {
 | 
				
			||||||
                t:'delete',
 | 
					                t:"delete",
 | 
				
			||||||
                nodes:removedNodes,
 | 
					                nodes:removedNodes,
 | 
				
			||||||
                links:removedLinks,
 | 
					                links:removedLinks,
 | 
				
			||||||
                subflowOutputs:removedSubflowOutputs,
 | 
					                subflowOutputs:removedSubflowOutputs,
 | 
				
			||||||
@@ -1019,7 +1084,7 @@ RED.view = (function() {
 | 
				
			|||||||
            for (var n=0;n<moving_set.length;n++) {
 | 
					            for (var n=0;n<moving_set.length;n++) {
 | 
				
			||||||
                var node = moving_set[n].n;
 | 
					                var node = moving_set[n].n;
 | 
				
			||||||
                // The only time a node.type == subflow can be selected is the
 | 
					                // The only time a node.type == subflow can be selected is the
 | 
				
			||||||
                // input/output 'proxy' nodes. They cannot be copied.
 | 
					                // input/output "proxy" nodes. They cannot be copied.
 | 
				
			||||||
                if (node.type != "subflow") {
 | 
					                if (node.type != "subflow") {
 | 
				
			||||||
                    for (var d in node._def.defaults) {
 | 
					                    for (var d in node._def.defaults) {
 | 
				
			||||||
                        if (node._def.defaults.hasOwnProperty(d)) {
 | 
					                        if (node._def.defaults.hasOwnProperty(d)) {
 | 
				
			||||||
@@ -1061,7 +1126,7 @@ RED.view = (function() {
 | 
				
			|||||||
        mousedown_port_type = 0;
 | 
					        mousedown_port_type = 0;
 | 
				
			||||||
        activeSpliceLink = null;
 | 
					        activeSpliceLink = null;
 | 
				
			||||||
        spliceActive = false;
 | 
					        spliceActive = false;
 | 
				
			||||||
        d3.select('.link_splice').classed('link_splice',false);
 | 
					        d3.select(".link_splice").classed("link_splice",false);
 | 
				
			||||||
        if (spliceTimer) {
 | 
					        if (spliceTimer) {
 | 
				
			||||||
            clearTimeout(spliceTimer);
 | 
					            clearTimeout(spliceTimer);
 | 
				
			||||||
            spliceTimer = null;
 | 
					            spliceTimer = null;
 | 
				
			||||||
@@ -1132,7 +1197,7 @@ RED.view = (function() {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            if (addedLinks.length > 0 || removedLinks.length > 0) {
 | 
					            if (addedLinks.length > 0 || removedLinks.length > 0) {
 | 
				
			||||||
                var historyEvent = {
 | 
					                var historyEvent = {
 | 
				
			||||||
                    t:'add',
 | 
					                    t:"add",
 | 
				
			||||||
                    links:addedLinks,
 | 
					                    links:addedLinks,
 | 
				
			||||||
                    removedLinks: removedLinks,
 | 
					                    removedLinks: removedLinks,
 | 
				
			||||||
                    dirty:RED.nodes.dirty()
 | 
					                    dirty:RED.nodes.dirty()
 | 
				
			||||||
@@ -1357,8 +1422,8 @@ RED.view = (function() {
 | 
				
			|||||||
                    .on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1)));})
 | 
					                    .on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 1)));})
 | 
				
			||||||
                    .on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
 | 
					                    .on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                outGroup.append("svg:text").attr('class','port_label').attr('x',20).attr('y',8).style("font-size","10px").text("output");
 | 
					                outGroup.append("svg:text").attr("class","port_label").attr("x",20).attr("y",8).style("font-size","10px").text("output");
 | 
				
			||||||
                outGroup.append("svg:text").attr('class','port_label port_index').attr('x',20).attr('y',24).text(function(d,i){ return i+1});
 | 
					                outGroup.append("svg:text").attr("class","port_label port_index").attr("x",20).attr("y",24).text(function(d,i){ return i+1});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var subflowInputs = vis.selectAll(".subflowinput").data(activeSubflow.in,function(d,i){ return d.id;});
 | 
					                var subflowInputs = vis.selectAll(".subflowinput").data(activeSubflow.in,function(d,i){ return d.id;});
 | 
				
			||||||
                subflowInputs.exit().remove();
 | 
					                subflowInputs.exit().remove();
 | 
				
			||||||
@@ -1399,7 +1464,7 @@ RED.view = (function() {
 | 
				
			|||||||
                    .on("touchend",function(d,i){portMouseUp(d,0,i);} )
 | 
					                    .on("touchend",function(d,i){portMouseUp(d,0,i);} )
 | 
				
			||||||
                    .on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
 | 
					                    .on("mouseover",function(d,i) { var port = d3.select(this); port.classed("port_hovered",(mouse_mode!=RED.state.JOINING || (drag_lines.length > 0 && drag_lines[0].portType !== 0) ));})
 | 
				
			||||||
                    .on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
 | 
					                    .on("mouseout",function(d,i) { var port = d3.select(this); port.classed("port_hovered",false);});
 | 
				
			||||||
                inGroup.append("svg:text").attr('class','port_label').attr('x',18).attr('y',20).style("font-size","10px").text("input");
 | 
					                inGroup.append("svg:text").attr("class","port_label").attr("x",18).attr("y",20).style("font-size","10px").text("input");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1432,10 +1497,12 @@ RED.view = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var nodeEnter = node.enter().insert("svg:g")
 | 
					            var nodeEnter = node.enter().insert("svg:g")
 | 
				
			||||||
                .attr("class", "node nodegroup")
 | 
					                .attr("class", "node nodegroup")
 | 
				
			||||||
                .classed("node_subflow",function(d) { return activeSubflow != null; });
 | 
					                .classed("node_subflow",function(d) { return activeSubflow != null; })
 | 
				
			||||||
 | 
					                .classed("node_link",function(d) { return d.type === "link in" || d.type === "link out" });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            nodeEnter.each(function(d,i) {
 | 
					            nodeEnter.each(function(d,i) {
 | 
				
			||||||
                    var node = d3.select(this);
 | 
					                    var node = d3.select(this);
 | 
				
			||||||
 | 
					                    var isLink = d.type === "link in" || d.type === "link out";
 | 
				
			||||||
                    node.attr("id",d.id);
 | 
					                    node.attr("id",d.id);
 | 
				
			||||||
                    var l = d._def.label;
 | 
					                    var l = d._def.label;
 | 
				
			||||||
                    try {
 | 
					                    try {
 | 
				
			||||||
@@ -1444,13 +1511,18 @@ RED.view = (function() {
 | 
				
			|||||||
                        console.log("Definition error: "+d.type+".label",err);
 | 
					                        console.log("Definition error: "+d.type+".label",err);
 | 
				
			||||||
                        l = d.type;
 | 
					                        l = d.type;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    d.w = Math.max(node_width,gridSize*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/gridSize)) );
 | 
					
 | 
				
			||||||
 | 
					                    if (isLink) {
 | 
				
			||||||
 | 
					                        d.w = node_height;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        d.w = Math.max(node_width,gridSize*(Math.ceil((calculateTextWidth(l, "node_label", 50)+(d._def.inputs>0?7:0))/gridSize)) );
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    d.h = Math.max(node_height,(d.outputs||0) * 15);
 | 
					                    d.h = Math.max(node_height,(d.outputs||0) * 15);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (d._def.badge) {
 | 
					                    if (d._def.badge) {
 | 
				
			||||||
                        var badge = node.append("svg:g").attr("class","node_badge_group");
 | 
					                        var badge = node.append("svg:g").attr("class","node_badge_group");
 | 
				
			||||||
                        var badgeRect = badge.append("rect").attr("class","node_badge").attr("rx",5).attr("ry",5).attr("width",40).attr("height",15);
 | 
					                        var badgeRect = badge.append("rect").attr("class","node_badge").attr("rx",5).attr("ry",5).attr("width",40).attr("height",15);
 | 
				
			||||||
                        badge.append("svg:text").attr("class","node_badge_label").attr("x",35).attr("y",11).attr('text-anchor','end').text(d._def.badge());
 | 
					                        badge.append("svg:text").attr("class","node_badge_label").attr("x",35).attr("y",11).attr("text-anchor","end").text(d._def.badge());
 | 
				
			||||||
                        if (d._def.onbadgeclick) {
 | 
					                        if (d._def.onbadgeclick) {
 | 
				
			||||||
                            badgeRect.attr("cursor","pointer")
 | 
					                            badgeRect.attr("cursor","pointer")
 | 
				
			||||||
                                .on("click",function(d) { d._def.onbadgeclick.call(d);d3.event.preventDefault();});
 | 
					                                .on("click",function(d) { d._def.onbadgeclick.call(d);d3.event.preventDefault();});
 | 
				
			||||||
@@ -1458,16 +1530,16 @@ RED.view = (function() {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (d._def.button) {
 | 
					                    if (d._def.button) {
 | 
				
			||||||
                        var nodeButtonGroup = node.append('svg:g')
 | 
					                        var nodeButtonGroup = node.append("svg:g")
 | 
				
			||||||
                            .attr("transform",function(d) { return "translate("+((d._def.align == "right") ? 94 : -25)+",2)"; })
 | 
					                            .attr("transform",function(d) { return "translate("+((d._def.align == "right") ? 94 : -25)+",2)"; })
 | 
				
			||||||
                            .attr("class",function(d) { return "node_button "+((d._def.align == "right") ? "node_right_button" : "node_left_button"); });
 | 
					                            .attr("class",function(d) { return "node_button "+((d._def.align == "right") ? "node_right_button" : "node_left_button"); });
 | 
				
			||||||
                        nodeButtonGroup.append('rect')
 | 
					                        nodeButtonGroup.append("rect")
 | 
				
			||||||
                            .attr("rx",5)
 | 
					                            .attr("rx",5)
 | 
				
			||||||
                            .attr("ry",5)
 | 
					                            .attr("ry",5)
 | 
				
			||||||
                            .attr("width",32)
 | 
					                            .attr("width",32)
 | 
				
			||||||
                            .attr("height",node_height-4)
 | 
					                            .attr("height",node_height-4)
 | 
				
			||||||
                            .attr("fill","#eee");//function(d) { return d._def.color;})
 | 
					                            .attr("fill","#eee");//function(d) { return d._def.color;})
 | 
				
			||||||
                        nodeButtonGroup.append('rect')
 | 
					                        nodeButtonGroup.append("rect")
 | 
				
			||||||
                            .attr("class","node_button_button")
 | 
					                            .attr("class","node_button_button")
 | 
				
			||||||
                            .attr("x",function(d) { return d._def.align == "right"? 11:5})
 | 
					                            .attr("x",function(d) { return d._def.align == "right"? 11:5})
 | 
				
			||||||
                            .attr("y",4)
 | 
					                            .attr("y",4)
 | 
				
			||||||
@@ -1563,11 +1635,11 @@ RED.view = (function() {
 | 
				
			|||||||
                            .attr("stroke-width","1");
 | 
					                            .attr("stroke-width","1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if ("right" == d._def.align) {
 | 
					                        if ("right" == d._def.align) {
 | 
				
			||||||
                            icon_group.attr('class','node_icon_group node_icon_group_'+d._def.align);
 | 
					                            icon_group.attr("class","node_icon_group node_icon_group_"+d._def.align);
 | 
				
			||||||
                            icon_shade_border.attr("d",function(d) { return "M 0 1 l 0 "+(d.h-2)})
 | 
					                            icon_shade_border.attr("d",function(d) { return "M 0 1 l 0 "+(d.h-2)})
 | 
				
			||||||
                            //icon.attr('class','node_icon node_icon_'+d._def.align);
 | 
					                            //icon.attr("class","node_icon node_icon_"+d._def.align);
 | 
				
			||||||
                            //icon.attr('class','node_icon_shade node_icon_shade_'+d._def.align);
 | 
					                            //icon.attr("class","node_icon_shade node_icon_shade_"+d._def.align);
 | 
				
			||||||
                            //icon.attr('class','node_icon_shade_border node_icon_shade_border_'+d._def.align);
 | 
					                            //icon.attr("class","node_icon_shade_border node_icon_shade_border_"+d._def.align);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        //if (d.inputs > 0 && d._def.align == null) {
 | 
					                        //if (d.inputs > 0 && d._def.align == null) {
 | 
				
			||||||
@@ -1595,24 +1667,25 @@ RED.view = (function() {
 | 
				
			|||||||
                        //icon.style("pointer-events","none");
 | 
					                        //icon.style("pointer-events","none");
 | 
				
			||||||
                        icon_group.style("pointer-events","none");
 | 
					                        icon_group.style("pointer-events","none");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    var text = node.append('svg:text').attr('class','node_label').attr('x', 38).attr('dy', '.35em').attr('text-anchor','start');
 | 
					                    if (!isLink) {
 | 
				
			||||||
                    if (d._def.align) {
 | 
					                        var text = node.append("svg:text").attr("class","node_label").attr("x", 38).attr("dy", ".35em").attr("text-anchor","start");
 | 
				
			||||||
                        text.attr('class','node_label node_label_'+d._def.align);
 | 
					                        if (d._def.align) {
 | 
				
			||||||
                        if (d._def.align === "right") {
 | 
					                            text.attr("class","node_label node_label_"+d._def.align);
 | 
				
			||||||
                            text.attr('text-anchor','end');
 | 
					                            if (d._def.align === "right") {
 | 
				
			||||||
 | 
					                                text.attr("text-anchor","end");
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var status = node.append("svg:g").attr("class","node_status_group").style("display","none");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var statusRect = status.append("rect").attr("class","node_status")
 | 
				
			||||||
 | 
					                                            .attr("x",6).attr("y",1).attr("width",9).attr("height",9)
 | 
				
			||||||
 | 
					                                            .attr("rx",2).attr("ry",2).attr("stroke-width","3");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var statusLabel = status.append("svg:text")
 | 
				
			||||||
 | 
					                            .attr("class","node_status_label")
 | 
				
			||||||
 | 
					                            .attr("x",20).attr("y",9);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var status = node.append("svg:g").attr("class","node_status_group").style("display","none");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var statusRect = status.append("rect").attr("class","node_status")
 | 
					 | 
				
			||||||
                                        .attr("x",6).attr("y",1).attr("width",9).attr("height",9)
 | 
					 | 
				
			||||||
                                        .attr("rx",2).attr("ry",2).attr("stroke-width","3");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    var statusLabel = status.append("svg:text")
 | 
					 | 
				
			||||||
                        .attr("class","node_status_label")
 | 
					 | 
				
			||||||
                        .attr('x',20).attr('y',9);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    //node.append("circle").attr({"class":"centerDot","cx":0,"cy":0,"r":5});
 | 
					                    //node.append("circle").attr({"class":"centerDot","cx":0,"cy":0,"r":5});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    //node.append("path").attr("class","node_error").attr("d","M 3,-3 l 10,0 l -5,-8 z");
 | 
					                    //node.append("path").attr("class","node_error").attr("d","M 3,-3 l 10,0 l -5,-8 z");
 | 
				
			||||||
@@ -1622,9 +1695,10 @@ RED.view = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            node.each(function(d,i) {
 | 
					            node.each(function(d,i) {
 | 
				
			||||||
                    if (d.dirty) {
 | 
					                    if (d.dirty) {
 | 
				
			||||||
 | 
					                        var isLink = d.type === "link in" || d.type === "link out";
 | 
				
			||||||
                        dirtyNodes[d.id] = d;
 | 
					                        dirtyNodes[d.id] = d;
 | 
				
			||||||
                        //if (d.x < -50) deleteSelection();  // Delete nodes if dragged back to palette
 | 
					                        //if (d.x < -50) deleteSelection();  // Delete nodes if dragged back to palette
 | 
				
			||||||
                        if (d.resize) {
 | 
					                        if (!isLink && d.resize) {
 | 
				
			||||||
                            var l = d._def.label;
 | 
					                            var l = d._def.label;
 | 
				
			||||||
                            try {
 | 
					                            try {
 | 
				
			||||||
                                l = (typeof l === "function" ? l.call(d) : l)||"";
 | 
					                                l = (typeof l === "function" ? l.call(d) : l)||"";
 | 
				
			||||||
@@ -1652,8 +1726,8 @@ RED.view = (function() {
 | 
				
			|||||||
                            //thisNode.selectAll(".node-gradient-top").attr("width",function(d){return d.w});
 | 
					                            //thisNode.selectAll(".node-gradient-top").attr("width",function(d){return d.w});
 | 
				
			||||||
                            //thisNode.selectAll(".node-gradient-bottom").attr("width",function(d){return d.w}).attr("y",function(d){return d.h-30});
 | 
					                            //thisNode.selectAll(".node-gradient-bottom").attr("width",function(d){return d.w}).attr("y",function(d){return d.h-30});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            thisNode.selectAll(".node_icon_group_right").attr('transform', function(d){return "translate("+(d.w-30)+",0)"});
 | 
					                            thisNode.selectAll(".node_icon_group_right").attr("transform", function(d){return "translate("+(d.w-30)+",0)"});
 | 
				
			||||||
                            thisNode.selectAll(".node_label_right").attr('x', function(d){return d.w-38});
 | 
					                            thisNode.selectAll(".node_label_right").attr("x", function(d){return d.w-38});
 | 
				
			||||||
                            //thisNode.selectAll(".node_icon_right").attr("x",function(d){return d.w-d3.select(this).attr("width")-1-(d.outputs>0?5:0);});
 | 
					                            //thisNode.selectAll(".node_icon_right").attr("x",function(d){return d.w-d3.select(this).attr("width")-1-(d.outputs>0?5:0);});
 | 
				
			||||||
                            //thisNode.selectAll(".node_icon_shade_right").attr("x",function(d){return d.w-30;});
 | 
					                            //thisNode.selectAll(".node_icon_shade_right").attr("x",function(d){return d.w-30;});
 | 
				
			||||||
                            //thisNode.selectAll(".node_icon_shade_border_right").attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2)});
 | 
					                            //thisNode.selectAll(".node_icon_shade_border_right").attr("d",function(d){return "M "+(d.w-30)+" 1 l 0 "+(d.h-2)});
 | 
				
			||||||
@@ -1698,7 +1772,7 @@ RED.view = (function() {
 | 
				
			|||||||
                                        port.attr("transform", function(d) { return "translate("+x+","+((y+13*i)-5)+")";});
 | 
					                                        port.attr("transform", function(d) { return "translate("+x+","+((y+13*i)-5)+")";});
 | 
				
			||||||
                                });
 | 
					                                });
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            thisNode.selectAll('text.node_label').text(function(d,i){
 | 
					                            thisNode.selectAll("text.node_label").text(function(d,i){
 | 
				
			||||||
                                    var l = "";
 | 
					                                    var l = "";
 | 
				
			||||||
                                    if (d._def.label) {
 | 
					                                    if (d._def.label) {
 | 
				
			||||||
                                        l = d._def.label;
 | 
					                                        l = d._def.label;
 | 
				
			||||||
@@ -1711,8 +1785,8 @@ RED.view = (function() {
 | 
				
			|||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    return l;
 | 
					                                    return l;
 | 
				
			||||||
                                })
 | 
					                                })
 | 
				
			||||||
                                .attr('y', function(d){return (d.h/2)-1;})
 | 
					                                .attr("y", function(d){return (d.h/2)-1;})
 | 
				
			||||||
                                .attr('class',function(d){
 | 
					                                .attr("class",function(d){
 | 
				
			||||||
                                    var s = "";
 | 
					                                    var s = "";
 | 
				
			||||||
                                    if (d._def.labelStyle) {
 | 
					                                    if (d._def.labelStyle) {
 | 
				
			||||||
                                        s = d._def.labelStyle;
 | 
					                                        s = d._def.labelStyle;
 | 
				
			||||||
@@ -1724,8 +1798,8 @@ RED.view = (function() {
 | 
				
			|||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        s = " "+s;
 | 
					                                        s = " "+s;
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    return 'node_label'+
 | 
					                                    return "node_label"+
 | 
				
			||||||
                                    (d._def.align?' node_label_'+d._def.align:'')+s;
 | 
					                                    (d._def.align?" node_label_"+d._def.align:"")+s;
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            if (d._def.icon) {
 | 
					                            if (d._def.icon) {
 | 
				
			||||||
@@ -1774,32 +1848,32 @@ RED.view = (function() {
 | 
				
			|||||||
                            thisNode.selectAll(".node_icon_shade").attr("height",function(d){return d.h;});
 | 
					                            thisNode.selectAll(".node_icon_shade").attr("height",function(d){return d.h;});
 | 
				
			||||||
                            thisNode.selectAll(".node_icon_shade_border").attr("d",function(d){ return "M "+(("right" == d._def.align) ?0:30)+" 1 l 0 "+(d.h-2)});
 | 
					                            thisNode.selectAll(".node_icon_shade_border").attr("d",function(d){ return "M "+(("right" == d._def.align) ?0:30)+" 1 l 0 "+(d.h-2)});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            thisNode.selectAll('.node_button').attr("opacity",function(d) {
 | 
					                            thisNode.selectAll(".node_button").attr("opacity",function(d) {
 | 
				
			||||||
                                return (activeSubflow||d.changed)?0.4:1
 | 
					                                return (activeSubflow||d.changed)?0.4:1
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            thisNode.selectAll('.node_button_button').attr("cursor",function(d) {
 | 
					                            thisNode.selectAll(".node_button_button").attr("cursor",function(d) {
 | 
				
			||||||
                                return (activeSubflow||d.changed)?"":"pointer";
 | 
					                                return (activeSubflow||d.changed)?"":"pointer";
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            thisNode.selectAll('.node_right_button').attr("transform",function(d){
 | 
					                            thisNode.selectAll(".node_right_button").attr("transform",function(d){
 | 
				
			||||||
                                    var x = d.w-6;
 | 
					                                    var x = d.w-6;
 | 
				
			||||||
                                    if (d._def.button.toggle && !d[d._def.button.toggle]) {
 | 
					                                    if (d._def.button.toggle && !d[d._def.button.toggle]) {
 | 
				
			||||||
                                        x = x - 8;
 | 
					                                        x = x - 8;
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    return "translate("+x+",2)";
 | 
					                                    return "translate("+x+",2)";
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            thisNode.selectAll('.node_right_button rect').attr("fill-opacity",function(d){
 | 
					                            thisNode.selectAll(".node_right_button rect").attr("fill-opacity",function(d){
 | 
				
			||||||
                                    if (d._def.button.toggle) {
 | 
					                                    if (d._def.button.toggle) {
 | 
				
			||||||
                                        return d[d._def.button.toggle]?1:0.2;
 | 
					                                        return d[d._def.button.toggle]?1:0.2;
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    return 1;
 | 
					                                    return 1;
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            //thisNode.selectAll('.node_right_button').attr("transform",function(d){return "translate("+(d.w - d._def.button.width.call(d))+","+0+")";}).attr("fill",function(d) {
 | 
					                            //thisNode.selectAll(".node_right_button").attr("transform",function(d){return "translate("+(d.w - d._def.button.width.call(d))+","+0+")";}).attr("fill",function(d) {
 | 
				
			||||||
                            //         return typeof d._def.button.color  === "function" ? d._def.button.color.call(d):(d._def.button.color != null ? d._def.button.color : d._def.color)
 | 
					                            //         return typeof d._def.button.color  === "function" ? d._def.button.color.call(d):(d._def.button.color != null ? d._def.button.color : d._def.color)
 | 
				
			||||||
                            //});
 | 
					                            //});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            thisNode.selectAll('.node_badge_group').attr("transform",function(d){return "translate("+(d.w-40)+","+(d.h+3)+")";});
 | 
					                            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) {
 | 
					                            thisNode.selectAll("text.node_badge_label").text(function(d,i) {
 | 
				
			||||||
                                if (d._def.badge) {
 | 
					                                if (d._def.badge) {
 | 
				
			||||||
                                    if (typeof d._def.badge == "function") {
 | 
					                                    if (typeof d._def.badge == "function") {
 | 
				
			||||||
                                        try {
 | 
					                                        try {
 | 
				
			||||||
@@ -1817,12 +1891,12 @@ RED.view = (function() {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (!showStatus || !d.status) {
 | 
					                        if (!showStatus || !d.status) {
 | 
				
			||||||
                            thisNode.selectAll('.node_status_group').style("display","none");
 | 
					                            thisNode.selectAll(".node_status_group").style("display","none");
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            thisNode.selectAll('.node_status_group').style("display","inline").attr("transform","translate(3,"+(d.h+3)+")");
 | 
					                            thisNode.selectAll(".node_status_group").style("display","inline").attr("transform","translate(3,"+(d.h+3)+")");
 | 
				
			||||||
                            var fill = status_colours[d.status.fill]; // Only allow our colours for now
 | 
					                            var fill = status_colours[d.status.fill]; // Only allow our colours for now
 | 
				
			||||||
                            if (d.status.shape == null && fill == null) {
 | 
					                            if (d.status.shape == null && fill == null) {
 | 
				
			||||||
                                thisNode.selectAll('.node_status').style("display","none");
 | 
					                                thisNode.selectAll(".node_status").style("display","none");
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                var style;
 | 
					                                var style;
 | 
				
			||||||
                                if (d.status.shape == null || d.status.shape == "dot") {
 | 
					                                if (d.status.shape == null || d.status.shape == "dot") {
 | 
				
			||||||
@@ -1834,22 +1908,23 @@ RED.view = (function() {
 | 
				
			|||||||
                                } else if (d.status.shape == "ring" ){
 | 
					                                } else if (d.status.shape == "ring" ){
 | 
				
			||||||
                                    style = {
 | 
					                                    style = {
 | 
				
			||||||
                                        display: "inline",
 | 
					                                        display: "inline",
 | 
				
			||||||
                                        fill: '#fff',
 | 
					                                        fill: "#fff",
 | 
				
			||||||
                                        stroke: fill
 | 
					                                        stroke: fill
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                thisNode.selectAll('.node_status').style(style);
 | 
					                                thisNode.selectAll(".node_status").style(style);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if (d.status.text) {
 | 
					                            if (d.status.text) {
 | 
				
			||||||
                                thisNode.selectAll('.node_status_label').text(d.status.text);
 | 
					                                thisNode.selectAll(".node_status_label").text(d.status.text);
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                thisNode.selectAll('.node_status_label').text("");
 | 
					                                thisNode.selectAll(".node_status_label").text("");
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        d.dirty = false;
 | 
					                        d.dirty = false;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var link = vis.selectAll(".link").data(
 | 
					            var link = vis.selectAll(".link").data(
 | 
				
			||||||
                activeLinks,
 | 
					                activeLinks,
 | 
				
			||||||
                function(d) {
 | 
					                function(d) {
 | 
				
			||||||
@@ -1890,7 +1965,8 @@ RED.view = (function() {
 | 
				
			|||||||
                    })
 | 
					                    })
 | 
				
			||||||
                l.append("svg:path").attr("class","link_outline link_path");
 | 
					                l.append("svg:path").attr("class","link_outline link_path");
 | 
				
			||||||
                l.append("svg:path").attr("class","link_line link_path")
 | 
					                l.append("svg:path").attr("class","link_line link_path")
 | 
				
			||||||
                    .classed("link_subflow", function(d) { return activeSubflow });
 | 
					                    .classed("link_link", function(d) { return d.link })
 | 
				
			||||||
 | 
					                    .classed("link_subflow", function(d) { return !d.link && activeSubflow });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            link.exit().remove();
 | 
					            link.exit().remove();
 | 
				
			||||||
@@ -1937,6 +2013,123 @@ RED.view = (function() {
 | 
				
			|||||||
                delete d.added;
 | 
					                delete d.added;
 | 
				
			||||||
                return d.target.type == "unknown" || d.source.type == "unknown"
 | 
					                return d.target.type == "unknown" || d.source.type == "unknown"
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					            var offLinks = vis.selectAll(".link_flow_link_g").data(
 | 
				
			||||||
 | 
					                activeFlowLinks,
 | 
				
			||||||
 | 
					                function(d) {
 | 
				
			||||||
 | 
					                    return d.node.id+":"+d.refresh
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var offLinksEnter = offLinks.enter().insert("g",".node").attr("class","link_flow_link_g");
 | 
				
			||||||
 | 
					            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","link_flow_link")
 | 
				
			||||||
 | 
					                        .attr("class","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(".link_group").data(flows);
 | 
				
			||||||
 | 
					                var enterLinkGroups = linkGroups.enter().append("g").attr("class","link_group")
 | 
				
			||||||
 | 
					                        .on('mouseover', function() { d3.select(this).classed('link_group_active',true)})
 | 
				
			||||||
 | 
					                        .on('mouseout', function() { d3.select(this).classed('link_group_active',false)})
 | 
				
			||||||
 | 
					                        .on('mousedown', function() { d3.event.preventDefault(); d3.event.stopPropagation(); })
 | 
				
			||||||
 | 
					                        .on('mouseup', function(f) {
 | 
				
			||||||
 | 
					                            d3.event.stopPropagation();
 | 
				
			||||||
 | 
					                            var targets = d.links[f];
 | 
				
			||||||
 | 
					                            RED.workspaces.show(f);
 | 
				
			||||||
 | 
					                            targets.forEach(function(n) {
 | 
				
			||||||
 | 
					                                n.selected = true;
 | 
				
			||||||
 | 
					                                n.dirty = true;
 | 
				
			||||||
 | 
					                                moving_set.push({n:n});
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            updateSelection();
 | 
				
			||||||
 | 
					                            redraw();
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                enterLinkGroups.each(function(f) {
 | 
				
			||||||
 | 
					                    var linkG = d3.select(this);
 | 
				
			||||||
 | 
					                    linkG.append("svg:path").attr("class","link_flow_link")
 | 
				
			||||||
 | 
					                        .attr("class","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","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","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","port 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","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 = vis.selectAll(".link_flow_link_g");
 | 
				
			||||||
 | 
					            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) + ")"; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // JOINING - unselect any selected links
 | 
					            // JOINING - unselect any selected links
 | 
				
			||||||
            vis.selectAll(".link_selected").data(
 | 
					            vis.selectAll(".link_selected").data(
 | 
				
			||||||
@@ -1961,8 +2154,8 @@ RED.view = (function() {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Imports a new collection of nodes from a JSON String.
 | 
					     * Imports a new collection of nodes from a JSON String.
 | 
				
			||||||
     *  - all get new IDs assigned
 | 
					     *  - all get new IDs assigned
 | 
				
			||||||
     *  - all 'selected'
 | 
					     *  - all "selected"
 | 
				
			||||||
     *  - attached to mouse for placing - 'IMPORT_DRAGGING'
 | 
					     *  - attached to mouse for placing - "IMPORT_DRAGGING"
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    function importNodes(newNodesStr,touchImport) {
 | 
					    function importNodes(newNodesStr,touchImport) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -1977,7 +2170,7 @@ RED.view = (function() {
 | 
				
			|||||||
                var new_workspaces = result[2];
 | 
					                var new_workspaces = result[2];
 | 
				
			||||||
                var new_subflows = result[3];
 | 
					                var new_subflows = result[3];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty('x') && n.hasOwnProperty('y') && n.z == RED.workspaces.active() }).map(function(n) { return {n:n};});
 | 
					                var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() }).map(function(n) { return {n:n};});
 | 
				
			||||||
                var new_node_ids = new_nodes.map(function(n){ return n.id; });
 | 
					                var new_node_ids = new_nodes.map(function(n){ return n.id; });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // TODO: pick a more sensible root node
 | 
					                // TODO: pick a more sensible root node
 | 
				
			||||||
@@ -2034,7 +2227,7 @@ RED.view = (function() {
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                var historyEvent = {
 | 
					                var historyEvent = {
 | 
				
			||||||
                    t:'add',
 | 
					                    t:"add",
 | 
				
			||||||
                    nodes:new_node_ids,
 | 
					                    nodes:new_node_ids,
 | 
				
			||||||
                    links:new_links,
 | 
					                    links:new_links,
 | 
				
			||||||
                    workspaces:new_workspaces,
 | 
					                    workspaces:new_workspaces,
 | 
				
			||||||
@@ -2079,6 +2272,7 @@ RED.view = (function() {
 | 
				
			|||||||
        redraw: function(updateActive) {
 | 
					        redraw: function(updateActive) {
 | 
				
			||||||
            if (updateActive) {
 | 
					            if (updateActive) {
 | 
				
			||||||
                updateActiveNodes();
 | 
					                updateActiveNodes();
 | 
				
			||||||
 | 
					                updateSelection();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            redraw();
 | 
					            redraw();
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,8 @@ $form-input-border-color:  #ccc;
 | 
				
			|||||||
$node-selected-color: #ff7f0e;
 | 
					$node-selected-color: #ff7f0e;
 | 
				
			||||||
$port-selected-color: #ff7f0e;
 | 
					$port-selected-color: #ff7f0e;
 | 
				
			||||||
$link-color: #888;
 | 
					$link-color: #888;
 | 
				
			||||||
 | 
					$link-link-color: #ccc;
 | 
				
			||||||
 | 
					$link-link-active-color: #ff7f0e;
 | 
				
			||||||
$link-subflow-color: #bbb;
 | 
					$link-subflow-color: #bbb;
 | 
				
			||||||
$link-unknown-color: #f00;
 | 
					$link-unknown-color: #f00;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,7 +207,25 @@
 | 
				
			|||||||
    fill: none;
 | 
					    fill: none;
 | 
				
			||||||
    pointer-events: none;
 | 
					    pointer-events: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.link_link {
 | 
				
			||||||
 | 
					    stroke-width: 2;
 | 
				
			||||||
 | 
					    stroke-dasharray: 10,5;
 | 
				
			||||||
 | 
					    stroke: $link-link-color;
 | 
				
			||||||
 | 
					    fill: none;
 | 
				
			||||||
 | 
					    stroke-dasharray: 15,2;
 | 
				
			||||||
 | 
					    pointer-events: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.link_port {
 | 
				
			||||||
 | 
					    fill: #fff;
 | 
				
			||||||
 | 
					    stroke: $link-link-color;
 | 
				
			||||||
 | 
					    stroke-width: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.link_group_active .link_port {
 | 
				
			||||||
 | 
					    stroke: $link-link-active-color;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.link_group:hover {
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.link_subflow {
 | 
					.link_subflow {
 | 
				
			||||||
    stroke: $link-subflow-color;
 | 
					    stroke: $link-subflow-color;
 | 
				
			||||||
    stroke-dasharray: 10,5;
 | 
					    stroke-dasharray: 10,5;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,7 +55,29 @@ ul.red-ui-tabs li a.red-ui-tab-label {
 | 
				
			|||||||
ul.red-ui-tabs li {
 | 
					ul.red-ui-tabs li {
 | 
				
			||||||
    position: relative;
 | 
					    position: relative;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					.red-ui-tabs-badges {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top:2px;
 | 
				
			||||||
 | 
					    right:2px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.red-ui-tab-closeable .red-ui-tabs-badges {
 | 
				
			||||||
 | 
					    right: 22px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.red-ui-tab.node_changed img.node_changed {
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.red-ui-tab.node_error img.node_error {
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.red-ui-tabs-badges img {
 | 
				
			||||||
 | 
					    width: 10px;
 | 
				
			||||||
 | 
					    height: 10px;
 | 
				
			||||||
 | 
					    margin-right: 2px;
 | 
				
			||||||
 | 
					    vertical-align: top;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
.red-ui-tab-close {
 | 
					.red-ui-tab-close {
 | 
				
			||||||
    background: $tab-background-inactive;
 | 
					    background: $tab-background-inactive;
 | 
				
			||||||
    opacity: 0.8;
 | 
					    opacity: 0.8;
 | 
				
			||||||
@@ -110,7 +132,7 @@ ul.red-ui-tabs li.active a {
 | 
				
			|||||||
    color: #333;
 | 
					    color: #333;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ul.red-ui-tabs li img {
 | 
					.red-ui-tab-icon {
 | 
				
			||||||
    margin-left: -8px;
 | 
					    margin-left: -8px;
 | 
				
			||||||
    margin-right: 3px;
 | 
					    margin-right: 3px;
 | 
				
			||||||
    margin-top: -2px;
 | 
					    margin-top: -2px;
 | 
				
			||||||
@@ -119,6 +141,6 @@ ul.red-ui-tabs li img {
 | 
				
			|||||||
    height: 20px;
 | 
					    height: 20px;
 | 
				
			||||||
    vertical-align: middle;
 | 
					    vertical-align: middle;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
ul.red-ui-tabs li.active img {
 | 
					ul.red-ui-tabs li.active .red-ui-tab-icon {
 | 
				
			||||||
    opacity: 0.2;
 | 
					    opacity: 0.2;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,59 +16,292 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script type="text/x-red" data-template-name="link in">
 | 
					<script type="text/x-red" data-template-name="link in">
 | 
				
			||||||
    <div class="form-row">
 | 
					    <div class="form-row">
 | 
				
			||||||
        <label for="node-input-event"><i class="fa fa-rss"></i> <span data-i18n="link.label.event"></span></label>
 | 
					        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
 | 
				
			||||||
        <input type="text" id="node-input-event" data-i18n="[placeholder]link.label.event">
 | 
					        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="form-row node-input-link-row"></div>
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<script type="text/x-red" data-template-name="link out">
 | 
				
			||||||
 | 
					    <div class="form-row">
 | 
				
			||||||
 | 
					        <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
 | 
				
			||||||
 | 
					        <input type="text" id="node-input-name" data-i18n="[placeholder]common.label.name">
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="form-row node-input-link-row"></div>
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<script type="text/x-red" data-help-name="link in">
 | 
					<script type="text/x-red" data-help-name="link in">
 | 
				
			||||||
    <p>Receive messages from any link nodes that send the specified event.</p>
 | 
					    <p>Receive messages from any link nodes that send the specified event.</p>
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					<script type="text/x-red" data-help-name="link out">
 | 
				
			||||||
 | 
					    <p>Send a message to any link input nodes listening for a given event.</p>
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					<style>
 | 
				
			||||||
 | 
					#node-input-link-container {
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#node-input-link-container li {
 | 
				
			||||||
 | 
					    padding: 2px 5px;
 | 
				
			||||||
 | 
					    background: none;
 | 
				
			||||||
 | 
					    font-size: 0.8em;
 | 
				
			||||||
 | 
					    margin:0;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#node-input-link-container li label {
 | 
				
			||||||
 | 
					    margin-bottom: 0;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#node-input-link-container li label input {
 | 
				
			||||||
 | 
					    vertical-align: top;
 | 
				
			||||||
 | 
					    width:15px;
 | 
				
			||||||
 | 
					    margin-right: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#node-input-link-container li:hover,
 | 
				
			||||||
 | 
					#node-input-link-container li:hover .node-input-target-node-sublabel {
 | 
				
			||||||
 | 
					    background: #f0f0f0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.node-input-link-node-sublabel {
 | 
				
			||||||
 | 
					    position:absolute;
 | 
				
			||||||
 | 
					    right: 0px;
 | 
				
			||||||
 | 
					    padding-right: 10px;
 | 
				
			||||||
 | 
					    padding-left: 10px;
 | 
				
			||||||
 | 
					    font-size: 0.8em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script type="text/javascript">
 | 
					<script type="text/javascript">
 | 
				
			||||||
 | 
					(function() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function sortNodeList(nodeList,sortOn,sortOnSecond) {
 | 
				
			||||||
 | 
					        var currentSort = nodeList.data('currentSort');
 | 
				
			||||||
 | 
					        var currentSortOrder = nodeList.data('currentSortOrder');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!currentSort) {
 | 
				
			||||||
 | 
					            currentSort = sortOn;
 | 
				
			||||||
 | 
					            currentSortOrder = 'a';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            if (currentSort === sortOn) {
 | 
				
			||||||
 | 
					                currentSortOrder = (currentSortOrder === 'a'?'d':'a');
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                currentSortOrder = 'a';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            currentSort = sortOn;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        nodeList.data('currentSort',currentSort);
 | 
				
			||||||
 | 
					        nodeList.data('currentSortOrder',currentSortOrder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $("#node-input-link-container-div .fa").hide();
 | 
				
			||||||
 | 
					        $(".node-input-link-sort-"+currentSort+"-"+currentSortOrder).show();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var items = nodeList.find("li").get();
 | 
				
			||||||
 | 
					        items.sort(function(a,b) {
 | 
				
			||||||
 | 
					            var labelA = $(a).find(".node-input-link-node-"+currentSort).text().toLowerCase();
 | 
				
			||||||
 | 
					            var labelB = $(b).find(".node-input-link-node-"+currentSort).text().toLowerCase();
 | 
				
			||||||
 | 
					            if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
 | 
				
			||||||
 | 
					            if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sortOnSecond) {
 | 
				
			||||||
 | 
					                labelA = $(a).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
 | 
				
			||||||
 | 
					                labelB = $(b).find(".node-input-link-node-"+sortOnSecond).text().toLowerCase();
 | 
				
			||||||
 | 
					                if (labelA < labelB) { return currentSortOrder==='a'?-1:1; }
 | 
				
			||||||
 | 
					                if (labelA > labelB) { return currentSortOrder==='a'?1:-1; }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        $.each(items, function(i, li){
 | 
				
			||||||
 | 
					            nodeList.append(li);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    function onEditPrepare(node,targetType) {
 | 
				
			||||||
 | 
					        if (!node.links) {
 | 
				
			||||||
 | 
					            node.links = [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        node.oldLinks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $('<div id="node-input-link-container-div" style="min-height: 100px;position: relative;   box-sizing: border-box; border-radius: 2px; height: 180px;  border: 1px solid #ccc;overflow:hidden; ">'+
 | 
				
			||||||
 | 
					            '    <div style="box-sizing: border-box; line-height: 20px; font-size: 0.8em; border-bottom: 1px solid #ddd; height: 20px;">'+
 | 
				
			||||||
 | 
					            '        <div style="display: inline-block;margin-left: 5px;"><a id="node-input-link-sort-label" href="#" data-i18n="[title]link.label.sortByLabel"><span data-i18n="link.label.node">name</span> <i class="node-input-link-sort-label-a fa fa-caret-down"></i><i class="node-input-link-sort-label-d fa fa-caret-up"></i></a></div>'+
 | 
				
			||||||
 | 
					            '        <div style="position: absolute; right: 10px; width: 50px; display: inline-block; text-align: right;"><a id="node-input-link-sort-type" href="#" data-i18n="[title]link.label.sortByFlow"><i class="node-input-link-sort-sublabel-a fa fa-caret-down"></i><i class="node-input-link-sort-sublabel-d fa fa-caret-up"></i> <span data-i18n="link.label.type">flow</span></a></div>'+
 | 
				
			||||||
 | 
					            '    </div>'+
 | 
				
			||||||
 | 
					            '    <div style="background: #fbfbfb; box-sizing: border-box; position:absolute; top:20px;bottom:0;left:0px;right:0px; overflow-y: scroll; overflow-x: hidden;">'+
 | 
				
			||||||
 | 
					            '        <ul id="node-input-link-container" style=" list-style-type:none; margin: 0;"></ul>'+
 | 
				
			||||||
 | 
					            '    </div>'+
 | 
				
			||||||
 | 
					            '</div>').appendTo('.node-input-link-row');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var nodeList = $("#node-input-link-container");
 | 
				
			||||||
 | 
					        var candidateNodes = RED.nodes.filterNodes({type:targetType});
 | 
				
			||||||
 | 
					        var inSubflow = (RED.nodes.subflow(node.z) != null);
 | 
				
			||||||
 | 
					        candidateNodes.forEach(function(n) {
 | 
				
			||||||
 | 
					            if (inSubflow) {
 | 
				
			||||||
 | 
					                if (n.z !== node.z) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (RED.nodes.subflow(n.z)!=null) {
 | 
				
			||||||
 | 
					                    return;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            var isChecked = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            isChecked = (node.links.indexOf(n.id) !== -1) || (n.links||[]).indexOf(node.id) !== -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isChecked) {
 | 
				
			||||||
 | 
					                node.oldLinks.push(n.id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var container = $('<li/>',{class:"node-input-link-node"});
 | 
				
			||||||
 | 
					            var row = $('<label/>',{for:"node-input-link-node-"+n.id}).appendTo(container);
 | 
				
			||||||
 | 
					            $('<input>',{type:"checkbox",class:"node-input-link-node-checkbox",id:"node-input-link-node-"+n.id})
 | 
				
			||||||
 | 
					                .data('node-id',n.id)
 | 
				
			||||||
 | 
					                .prop('checked', isChecked)
 | 
				
			||||||
 | 
					                .appendTo(row);
 | 
				
			||||||
 | 
					            container.on('mouseover',function(e) {
 | 
				
			||||||
 | 
					                n.highlighted = true;
 | 
				
			||||||
 | 
					                n.dirty = true;
 | 
				
			||||||
 | 
					                RED.view.redraw();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            container.on('mouseout',function(e) {
 | 
				
			||||||
 | 
					                n.highlighted = false;
 | 
				
			||||||
 | 
					                n.dirty = true;
 | 
				
			||||||
 | 
					                RED.view.redraw();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            var labelSpan = $('<span>');
 | 
				
			||||||
 | 
					            var label = n.name||n.id;
 | 
				
			||||||
 | 
					            var sublabel;
 | 
				
			||||||
 | 
					            var tab = RED.nodes.workspace(n.z);
 | 
				
			||||||
 | 
					            if (tab) {
 | 
				
			||||||
 | 
					                sublabel = tab.label||tab.id;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                tab = RED.nodes.subflow(n.z);
 | 
				
			||||||
 | 
					                sublabel = "subflow : "+tab.name;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            $('<span>',{class:"node-input-link-node-label",style:"white-space:nowrap"}).text(label).appendTo(row);
 | 
				
			||||||
 | 
					            if (sublabel) {
 | 
				
			||||||
 | 
					                $('<span>',{class:"node-input-link-node-sublabel"}).text(sublabel).appendTo(row);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            container.appendTo(nodeList);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sortNodeList(nodeList,'sublabel','label');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $("#node-input-link-sort-label").click(function(e) {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            sortNodeList(nodeList,'label');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $("#node-input-link-sort-type").click(function(e) {
 | 
				
			||||||
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					            sortNodeList(nodeList,'sublabel');
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function resizeNodeList() {
 | 
				
			||||||
 | 
					        var rows = $("#dialog-form>div:not(.node-input-link-row)");
 | 
				
			||||||
 | 
					        var height = $("#dialog-form").height();
 | 
				
			||||||
 | 
					        for (var i=0;i<rows.size();i++) {
 | 
				
			||||||
 | 
					            height -= $(rows[i]).outerHeight(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var editorRow = $("#dialog-form>div.node-input-link-row");
 | 
				
			||||||
 | 
					        height -= (parseInt(editorRow.css("marginTop"))+parseInt(editorRow.css("marginBottom")));
 | 
				
			||||||
 | 
					        $("#node-input-link-container-div").css("height",height+"px");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function onEditSave(node) {
 | 
				
			||||||
 | 
					        node.links = [];
 | 
				
			||||||
 | 
					        $(".node-input-link-node-checkbox").each(function(n) {
 | 
				
			||||||
 | 
					            if ($(this).prop("checked")) {
 | 
				
			||||||
 | 
					                node.links.push($(this).data('node-id'));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        node.oldLinks.sort();
 | 
				
			||||||
 | 
					        node.links.sort();
 | 
				
			||||||
 | 
					        var nodeMap = {};
 | 
				
			||||||
 | 
					        var length = Math.max(node.oldLinks.length,node.links.length);
 | 
				
			||||||
 | 
					        for (var i=0;i<length;i++) {
 | 
				
			||||||
 | 
					            if (i<node.oldLinks.length) {
 | 
				
			||||||
 | 
					                nodeMap[node.oldLinks[i]] = nodeMap[node.oldLinks[i]]||{};
 | 
				
			||||||
 | 
					                nodeMap[node.oldLinks[i]].old = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (i<node.links.length) {
 | 
				
			||||||
 | 
					                nodeMap[node.links[i]] = nodeMap[node.links[i]]||{};
 | 
				
			||||||
 | 
					                nodeMap[node.links[i]].new = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var n;
 | 
				
			||||||
 | 
					        for (var id in nodeMap) {
 | 
				
			||||||
 | 
					            if (nodeMap.hasOwnProperty(id)) {
 | 
				
			||||||
 | 
					                n = RED.nodes.node(id);
 | 
				
			||||||
 | 
					                if (n) {
 | 
				
			||||||
 | 
					                    if (nodeMap[id].old && !nodeMap[id].new) {
 | 
				
			||||||
 | 
					                        // Removed id
 | 
				
			||||||
 | 
					                        i = n.links.indexOf(node.id);
 | 
				
			||||||
 | 
					                        if (i > -1) {
 | 
				
			||||||
 | 
					                            n.links.splice(i,1);
 | 
				
			||||||
 | 
					                            n.changed = true;
 | 
				
			||||||
 | 
					                            n.dirty = true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else if (!nodeMap[id].old && nodeMap[id].new){
 | 
				
			||||||
 | 
					                        // Added id
 | 
				
			||||||
 | 
					                        i = n.links.indexOf(id);
 | 
				
			||||||
 | 
					                        if (i === -1) {
 | 
				
			||||||
 | 
					                            n.links.push(node.id);
 | 
				
			||||||
 | 
					                            n.changed = true;
 | 
				
			||||||
 | 
					                            n.dirty = true;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RED.nodes.registerType('link in',{
 | 
					    RED.nodes.registerType('link in',{
 | 
				
			||||||
        category: 'input',
 | 
					        category: 'input',
 | 
				
			||||||
        color:"#87D8CF",
 | 
					        color:"#ddd",//"#87D8CF",
 | 
				
			||||||
        defaults: {
 | 
					        defaults: {
 | 
				
			||||||
            event: {value:"",required: true}
 | 
					            name: {value:""},
 | 
				
			||||||
 | 
					            links: { value: [] }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        inputs:0,
 | 
					        inputs:0,
 | 
				
			||||||
        outputs:1,
 | 
					        outputs:1,
 | 
				
			||||||
        icon: "link-out.png",
 | 
					        icon: "link-out.png",
 | 
				
			||||||
        label: function() {
 | 
					        label: function() {
 | 
				
			||||||
            return this.event||this._("link.linkIn");
 | 
					            return this.name||this._("link.linkIn");
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        labelStyle: function() {
 | 
					        labelStyle: function() {
 | 
				
			||||||
            return this.event?"node_label_italic":"";
 | 
					            return this.name?"node_label_italic":"";
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        oneditprepare: function() {
 | 
				
			||||||
 | 
					            onEditPrepare(this,"link out");
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        oneditsave: function() {
 | 
				
			||||||
 | 
					            onEditSave(this);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        oneditresize: resizeNodeList
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script type="text/x-red" data-help-name="link out">
 | 
					 | 
				
			||||||
    <p>Send a message to any link input nodes listening for a given event.</p>
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script type="text/x-red" data-template-name="link out">
 | 
					 | 
				
			||||||
    <div class="form-row">
 | 
					 | 
				
			||||||
        <label for="node-input-event"><i class="fa fa-rss"></i> <span data-i18n="link.label.event"></span></label>
 | 
					 | 
				
			||||||
        <input type="text" id="node-input-event" data-i18n="[placeholder]link.label.event">
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script type="text/javascript">
 | 
					 | 
				
			||||||
    RED.nodes.registerType('link out',{
 | 
					    RED.nodes.registerType('link out',{
 | 
				
			||||||
        category: 'output',
 | 
					        category: 'output',
 | 
				
			||||||
        color:"#87D8CF",
 | 
					        color:"#ddd",//"#87D8CF",
 | 
				
			||||||
        defaults: {
 | 
					        defaults: {
 | 
				
			||||||
            event: {value:"",required: true}
 | 
					            name: {value:""},
 | 
				
			||||||
 | 
					            links: { value: []}
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        align:"right",
 | 
					        align:"right",
 | 
				
			||||||
        inputs:1,
 | 
					        inputs:1,
 | 
				
			||||||
        outputs:0,
 | 
					        outputs:0,
 | 
				
			||||||
        icon: "link-out.png",
 | 
					        icon: "link-out.png",
 | 
				
			||||||
        label: function() {
 | 
					        label: function() {
 | 
				
			||||||
            return this.event||this._("link.linkOut");
 | 
					            return this.name||this._("link.linkOut");
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        labelStyle: function() {
 | 
					        labelStyle: function() {
 | 
				
			||||||
            return this.event?"node_label_italic":"";
 | 
					            return this.name?"node_label_italic":"";
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        oneditprepare: function() {
 | 
				
			||||||
 | 
					            onEditPrepare(this,"link in");
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        oneditsave: function() {
 | 
				
			||||||
 | 
					            onEditSave(this);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        oneditresize: resizeNodeList
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,20 +20,18 @@ module.exports = function(RED) {
 | 
				
			|||||||
    function LinkInNode(n) {
 | 
					    function LinkInNode(n) {
 | 
				
			||||||
        RED.nodes.createNode(this,n);
 | 
					        RED.nodes.createNode(this,n);
 | 
				
			||||||
        var node = this;
 | 
					        var node = this;
 | 
				
			||||||
 | 
					        var event = "node:"+n.id;
 | 
				
			||||||
        if (n.event) {
 | 
					        var handler = function(msg) {
 | 
				
			||||||
            var handler = function(msg) {
 | 
					            msg._event = n.event;
 | 
				
			||||||
                msg._event = n.event;
 | 
					            node.receive(msg);
 | 
				
			||||||
                node.receive(msg);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            RED.events.on("node:"+n.event,handler);
 | 
					 | 
				
			||||||
            this.on("input", function(msg) {
 | 
					 | 
				
			||||||
                this.send(msg);
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            this.on("close",function() {
 | 
					 | 
				
			||||||
                RED.events.removeListener("node:"+n.event,handler);
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        RED.events.on(event,handler);
 | 
				
			||||||
 | 
					        this.on("input", function(msg) {
 | 
				
			||||||
 | 
					            this.send(msg);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.on("close",function() {
 | 
				
			||||||
 | 
					            RED.events.removeListener(event,handler);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RED.nodes.registerType("link in",LinkInNode);
 | 
					    RED.nodes.registerType("link in",LinkInNode);
 | 
				
			||||||
@@ -41,12 +39,12 @@ module.exports = function(RED) {
 | 
				
			|||||||
    function LinkOutNode(n) {
 | 
					    function LinkOutNode(n) {
 | 
				
			||||||
        RED.nodes.createNode(this,n);
 | 
					        RED.nodes.createNode(this,n);
 | 
				
			||||||
        var node = this;
 | 
					        var node = this;
 | 
				
			||||||
        if (n.event) {
 | 
					        var event = "node:"+n.id;
 | 
				
			||||||
            this.on("input", function(msg) {
 | 
					        this.on("input", function(msg) {
 | 
				
			||||||
                msg._event = n.event;
 | 
					            msg._event = event;
 | 
				
			||||||
                RED.events.emit("node:"+n.event,msg)
 | 
					            RED.events.emit(event,msg)
 | 
				
			||||||
            });
 | 
					            this.send(msg);
 | 
				
			||||||
        }
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    RED.nodes.registerType("link out",LinkOutNode);
 | 
					    RED.nodes.registerType("link out",LinkOutNode);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -457,7 +457,11 @@ function getFlow(id) {
 | 
				
			|||||||
        var nodeIds = Object.keys(flow.nodes);
 | 
					        var nodeIds = Object.keys(flow.nodes);
 | 
				
			||||||
        if (nodeIds.length > 0) {
 | 
					        if (nodeIds.length > 0) {
 | 
				
			||||||
            result.nodes = nodeIds.map(function(nodeId) {
 | 
					            result.nodes = nodeIds.map(function(nodeId) {
 | 
				
			||||||
                return clone(flow.nodes[nodeId]);
 | 
					                var node = clone(flow.nodes[nodeId]);
 | 
				
			||||||
 | 
					                if (node.type === 'link out') {
 | 
				
			||||||
 | 
					                    delete node.wires;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return node;
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,7 +67,8 @@ module.exports = {
 | 
				
			|||||||
                flow.subflows[n.id].instances = [];
 | 
					                flow.subflows[n.id].instances = [];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        var linkWires = {};
 | 
				
			||||||
 | 
					        var linkOutNodes = [];
 | 
				
			||||||
        config.forEach(function(n) {
 | 
					        config.forEach(function(n) {
 | 
				
			||||||
            if (n.type !== 'subflow' && n.type !== 'tab') {
 | 
					            if (n.type !== 'subflow' && n.type !== 'tab') {
 | 
				
			||||||
                var subflowDetails = subflowInstanceRE.exec(n.type);
 | 
					                var subflowDetails = subflowInstanceRE.exec(n.type);
 | 
				
			||||||
@@ -100,8 +101,28 @@ module.exports = {
 | 
				
			|||||||
                        flow.configs[n.id]._users = [];
 | 
					                        flow.configs[n.id]._users = [];
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                if (n.type === 'link in' && n.links) {
 | 
				
			||||||
 | 
					                    // Ensure wires are present in corresponding link out nodes
 | 
				
			||||||
 | 
					                    n.links.forEach(function(id) {
 | 
				
			||||||
 | 
					                        linkWires[id] = linkWires[id]||{};
 | 
				
			||||||
 | 
					                        linkWires[id][n.id] = true;
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                } else if (n.type === 'link out' && n.links) {
 | 
				
			||||||
 | 
					                    linkWires[n.id] = linkWires[n.id]||{};
 | 
				
			||||||
 | 
					                    n.links.forEach(function(id) {
 | 
				
			||||||
 | 
					                        linkWires[n.id][id] = true;
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    linkOutNodes.push(n);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        linkOutNodes.forEach(function(n) {
 | 
				
			||||||
 | 
					            var links = linkWires[n.id];
 | 
				
			||||||
 | 
					            var targets = Object.keys(links);
 | 
				
			||||||
 | 
					            n.wires = [targets];
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var addedTabs = {};
 | 
					        var addedTabs = {};
 | 
				
			||||||
        config.forEach(function(n) {
 | 
					        config.forEach(function(n) {
 | 
				
			||||||
            if (n.type !== 'subflow' && n.type !== 'tab') {
 | 
					            if (n.type !== 'subflow' && n.type !== 'tab') {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user