mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	@@ -684,6 +684,13 @@ RED.history = (function() {
 | 
			
		||||
        peek: function() {
 | 
			
		||||
            return undoHistory[undoHistory.length-1];
 | 
			
		||||
        },
 | 
			
		||||
        replace: function(ev) {
 | 
			
		||||
            if (undoHistory.length === 0) {
 | 
			
		||||
                RED.history.push(ev);
 | 
			
		||||
            } else {
 | 
			
		||||
                undoHistory[undoHistory.length-1] = ev;
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        clear: function() {
 | 
			
		||||
            undoHistory = [];
 | 
			
		||||
            redoHistory = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,9 @@
 | 
			
		||||
    },
 | 
			
		||||
    "red-ui-workspace": {
 | 
			
		||||
        "backspace": "core:delete-selection",
 | 
			
		||||
        "ctrl-backspace": "core:delete-selection-and-reconnect",
 | 
			
		||||
        "delete": "core:delete-selection",
 | 
			
		||||
        "ctrl-delete": "core:delete-selection-and-reconnect",
 | 
			
		||||
        "enter": "core:edit-selected-node",
 | 
			
		||||
        "ctrl-enter": "core:go-to-selection",
 | 
			
		||||
        "ctrl-c": "core:copy-selection-to-internal-clipboard",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,9 @@
 | 
			
		||||
 **/
 | 
			
		||||
RED.nodes = (function() {
 | 
			
		||||
 | 
			
		||||
    var PORT_TYPE_INPUT = 1;
 | 
			
		||||
    var PORT_TYPE_OUTPUT = 0;
 | 
			
		||||
 | 
			
		||||
    var node_defs = {};
 | 
			
		||||
    var linkTabMap = {};
 | 
			
		||||
 | 
			
		||||
@@ -2458,6 +2461,144 @@ RED.nodes = (function() {
 | 
			
		||||
        return helpContent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getNodeIslands(nodes) {
 | 
			
		||||
        var selectedNodes = new Set(nodes);
 | 
			
		||||
        // Maps node => island index
 | 
			
		||||
        var nodeToIslandIndex = new Map();
 | 
			
		||||
        // Maps island index => [nodes in island]
 | 
			
		||||
        var islandIndexToNodes = new Map();
 | 
			
		||||
        var internalLinks = new Set();
 | 
			
		||||
        nodes.forEach((node, index) => {
 | 
			
		||||
            nodeToIslandIndex.set(node,index);
 | 
			
		||||
            islandIndexToNodes.set(index, [node]);
 | 
			
		||||
            var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
 | 
			
		||||
            var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
 | 
			
		||||
            inboundLinks.forEach(l => {
 | 
			
		||||
                if (selectedNodes.has(l.source)) {
 | 
			
		||||
                    internalLinks.add(l)
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            outboundLinks.forEach(l => {
 | 
			
		||||
                if (selectedNodes.has(l.target)) {
 | 
			
		||||
                    internalLinks.add(l)
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        internalLinks.forEach(l => {
 | 
			
		||||
            let source = l.source;
 | 
			
		||||
            let target = l.target;
 | 
			
		||||
            if (nodeToIslandIndex.get(source) !== nodeToIslandIndex.get(target)) {
 | 
			
		||||
                let sourceIsland = nodeToIslandIndex.get(source);
 | 
			
		||||
                let islandToMove = nodeToIslandIndex.get(target);
 | 
			
		||||
                let nodesToMove = islandIndexToNodes.get(islandToMove);
 | 
			
		||||
                nodesToMove.forEach(n => {
 | 
			
		||||
                    nodeToIslandIndex.set(n,sourceIsland);
 | 
			
		||||
                    islandIndexToNodes.get(sourceIsland).push(n);
 | 
			
		||||
                })
 | 
			
		||||
                islandIndexToNodes.delete(islandToMove);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        const result = [];
 | 
			
		||||
        islandIndexToNodes.forEach((nodes,index) => {
 | 
			
		||||
            result.push(nodes);
 | 
			
		||||
        })
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function detachNodes(nodes) {
 | 
			
		||||
        let allSelectedNodes = [];
 | 
			
		||||
        nodes.forEach(node => {
 | 
			
		||||
            if (node.type === 'group') {
 | 
			
		||||
                let groupNodes = RED.group.getNodes(node,true,true);
 | 
			
		||||
                allSelectedNodes = allSelectedNodes.concat(groupNodes);
 | 
			
		||||
            } else {
 | 
			
		||||
                allSelectedNodes.push(node);
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        if (allSelectedNodes.length > 0 ) {
 | 
			
		||||
            const nodeIslands = RED.nodes.getNodeIslands(allSelectedNodes);
 | 
			
		||||
            let removedLinks = [];
 | 
			
		||||
            let newLinks = [];
 | 
			
		||||
            let createdLinkIds = new Set();
 | 
			
		||||
 | 
			
		||||
            nodeIslands.forEach(nodes => {
 | 
			
		||||
                let selectedNodes = new Set(nodes);
 | 
			
		||||
                let allInboundLinks = [];
 | 
			
		||||
                let allOutboundLinks = [];
 | 
			
		||||
                // Identify links that enter or exit this island of nodes
 | 
			
		||||
                nodes.forEach(node => {
 | 
			
		||||
                    var inboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_INPUT);
 | 
			
		||||
                    var outboundLinks = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
 | 
			
		||||
                    inboundLinks.forEach(l => {
 | 
			
		||||
                        if (!selectedNodes.has(l.source)) {
 | 
			
		||||
                            allInboundLinks.push(l)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                    outboundLinks.forEach(l => {
 | 
			
		||||
                        if (!selectedNodes.has(l.target)) {
 | 
			
		||||
                            allOutboundLinks.push(l)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                // Identify the links to restore
 | 
			
		||||
                allInboundLinks.forEach(inLink => {
 | 
			
		||||
                    // For Each inbound link,
 | 
			
		||||
                    //  - get source node.
 | 
			
		||||
                    //  - trace through to all outbound links
 | 
			
		||||
                    let sourceNode = inLink.source;
 | 
			
		||||
                    let targetNodes = new Set();
 | 
			
		||||
                    let visited = new Set();
 | 
			
		||||
                    let stack = [inLink.target];
 | 
			
		||||
                    while (stack.length > 0) {
 | 
			
		||||
                        let node = stack.pop(stack);
 | 
			
		||||
                        visited.add(node)
 | 
			
		||||
                        let links = RED.nodes.getNodeLinks(node, PORT_TYPE_OUTPUT);
 | 
			
		||||
                        links.forEach(l => {
 | 
			
		||||
                            if (visited.has(l.target)) {
 | 
			
		||||
                                return
 | 
			
		||||
                            }
 | 
			
		||||
                            visited.add(l.target);
 | 
			
		||||
                            if (selectedNodes.has(l.target)) {
 | 
			
		||||
                                // internal link
 | 
			
		||||
                                stack.push(l.target)
 | 
			
		||||
                            } else {
 | 
			
		||||
                                targetNodes.add(l.target)
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                    targetNodes.forEach(target => {
 | 
			
		||||
                        let linkId = `${sourceNode.id}[${inLink.sourcePort}] -> ${target.id}`
 | 
			
		||||
                        if (!createdLinkIds.has(linkId)) {
 | 
			
		||||
                            createdLinkIds.add(linkId);
 | 
			
		||||
                            let link = {
 | 
			
		||||
                                source: sourceNode,
 | 
			
		||||
                                sourcePort: inLink.sourcePort,
 | 
			
		||||
                                target: target
 | 
			
		||||
                            }
 | 
			
		||||
                            let existingLinks = RED.nodes.filterLinks(link)
 | 
			
		||||
                            if (existingLinks.length === 0) {
 | 
			
		||||
                                newLinks.push(link);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
                // 2. delete all those links
 | 
			
		||||
                allInboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
 | 
			
		||||
                allOutboundLinks.forEach(l => { RED.nodes.removeLink(l); removedLinks.push(l)})
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
            newLinks.forEach(l => RED.nodes.addLink(l));
 | 
			
		||||
            return {
 | 
			
		||||
                newLinks,
 | 
			
		||||
                removedLinks
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        init: function() {
 | 
			
		||||
            RED.events.on("registry:node-type-added",function(type) {
 | 
			
		||||
@@ -2539,7 +2680,7 @@ RED.nodes = (function() {
 | 
			
		||||
        add: addNode,
 | 
			
		||||
        remove: removeNode,
 | 
			
		||||
        clear: clear,
 | 
			
		||||
 | 
			
		||||
        detachNodes: detachNodes,
 | 
			
		||||
        moveNodesForwards: moveNodesForwards,
 | 
			
		||||
        moveNodesBackwards: moveNodesBackwards,
 | 
			
		||||
        moveNodesToFront: moveNodesToFront,
 | 
			
		||||
@@ -2552,6 +2693,9 @@ RED.nodes = (function() {
 | 
			
		||||
        addLink: addLink,
 | 
			
		||||
        removeLink: removeLink,
 | 
			
		||||
        getNodeLinks: function(id, portType) {
 | 
			
		||||
            if (typeof id !== 'string') {
 | 
			
		||||
                id = id.id;
 | 
			
		||||
            }
 | 
			
		||||
            if (nodeLinks[id]) {
 | 
			
		||||
                if (portType === 1) {
 | 
			
		||||
                    // Return cloned arrays so they can be safely modified by caller
 | 
			
		||||
@@ -2635,6 +2779,7 @@ RED.nodes = (function() {
 | 
			
		||||
        getAllFlowNodes: getAllFlowNodes,
 | 
			
		||||
        getAllUpstreamNodes: getAllUpstreamNodes,
 | 
			
		||||
        getAllDownstreamNodes: getAllDownstreamNodes,
 | 
			
		||||
        getNodeIslands: getNodeIslands,
 | 
			
		||||
        createExportableNodeSet: createExportableNodeSet,
 | 
			
		||||
        createCompleteNodeSet: createCompleteNodeSet,
 | 
			
		||||
        updateConfigNodeUsers: updateConfigNodeUsers,
 | 
			
		||||
 
 | 
			
		||||
@@ -590,12 +590,14 @@ RED.group = (function() {
 | 
			
		||||
        markDirty(group);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getNodes(group,recursive) {
 | 
			
		||||
    function getNodes(group,recursive,excludeGroup) {
 | 
			
		||||
        var nodes = [];
 | 
			
		||||
        group.nodes.forEach(function(n) {
 | 
			
		||||
            nodes.push(n);
 | 
			
		||||
            if (n.type !== 'group' || !excludeGroup) {
 | 
			
		||||
                nodes.push(n);
 | 
			
		||||
            }
 | 
			
		||||
            if (recursive && n.type === 'group') {
 | 
			
		||||
                nodes = nodes.concat(getNodes(n,recursive))
 | 
			
		||||
                nodes = nodes.concat(getNodes(n,recursive,excludeGroup))
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        return nodes;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,5 +27,6 @@ RED.state = {
 | 
			
		||||
    PANNING: 10,
 | 
			
		||||
    SELECTING_NODE: 11,
 | 
			
		||||
    GROUP_DRAGGING: 12,
 | 
			
		||||
    GROUP_RESIZE: 13
 | 
			
		||||
    GROUP_RESIZE: 13,
 | 
			
		||||
    DETACHED_DRAGGING: 14
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -538,6 +538,8 @@ RED.view = (function() {
 | 
			
		||||
        RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection();deleteSelection();});
 | 
			
		||||
        RED.actions.add("core:paste-from-internal-clipboard",function(){importNodes(clipboard,{generateIds: true});});
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
 | 
			
		||||
 | 
			
		||||
        RED.events.on("view:selection-changed", function(selection) {
 | 
			
		||||
            var hasSelection = (selection.nodes && selection.nodes.length > 0);
 | 
			
		||||
            var hasMultipleSelection = hasSelection && selection.nodes.length > 1;
 | 
			
		||||
@@ -560,6 +562,7 @@ RED.view = (function() {
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:delete-selection",deleteSelection);
 | 
			
		||||
        RED.actions.add("core:delete-selection-and-reconnect",function() { deleteSelection(true) });
 | 
			
		||||
        RED.actions.add("core:edit-selected-node",editSelection);
 | 
			
		||||
        RED.actions.add("core:go-to-selection",function() {
 | 
			
		||||
            if (movingSet.length() > 0) {
 | 
			
		||||
@@ -1384,7 +1387,7 @@ RED.view = (function() {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length() === 0) {
 | 
			
		||||
        if (mouse_mode != RED.state.QUICK_JOINING && mouse_mode != RED.state.IMPORT_DRAGGING && mouse_mode != RED.state.DETACHED_DRAGGING && !mousedown_node && !mousedown_group && selectedLinks.length() === 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1489,7 +1492,7 @@ RED.view = (function() {
 | 
			
		||||
                                   RED.nodes.filterLinks({ target: node.n }).length === 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING) {
 | 
			
		||||
        } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
            mousePos = mouse_position;
 | 
			
		||||
            var minX = 0;
 | 
			
		||||
            var minY = 0;
 | 
			
		||||
@@ -1845,10 +1848,30 @@ RED.view = (function() {
 | 
			
		||||
        //         movingSet.add(mousedown_node);
 | 
			
		||||
        //     }
 | 
			
		||||
        // }
 | 
			
		||||
        if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE) {
 | 
			
		||||
        if (mouse_mode == RED.state.MOVING || mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
            // if (mousedown_node) {
 | 
			
		||||
            //     delete mousedown_node.gSelected;
 | 
			
		||||
            // }
 | 
			
		||||
            if (mouse_mode === RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
                var ns = [];
 | 
			
		||||
                for (var j=0;j<movingSet.length();j++) {
 | 
			
		||||
                    var n = movingSet.get(j);
 | 
			
		||||
                    if (n.ox !== n.n.x || n.oy !== n.n.y) {
 | 
			
		||||
                        ns.push({n:n.n,ox:n.ox,oy:n.oy,moved:n.n.moved});
 | 
			
		||||
                        n.n.dirty = true;
 | 
			
		||||
                        n.n.moved = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                var detachEvent = RED.history.peek();
 | 
			
		||||
                // The last event in the stack *should* be a multi-event from
 | 
			
		||||
                // where the links were added/removed
 | 
			
		||||
                var historyEvent = {t:"move",nodes:ns,dirty:RED.nodes.dirty()}
 | 
			
		||||
                if (detachEvent.t === "multi") {
 | 
			
		||||
                    detachEvent.events.push(historyEvent)
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.history.push(historyEvent)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (i=0;i<movingSet.length();i++) {
 | 
			
		||||
                var node = movingSet.get(i);
 | 
			
		||||
                delete node.ox;
 | 
			
		||||
@@ -1893,7 +1916,16 @@ RED.view = (function() {
 | 
			
		||||
        if (mouse_mode === RED.state.MOVING || mouse_mode === RED.state.MOVING_ACTIVE) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (mouse_mode === RED.state.IMPORT_DRAGGING) {
 | 
			
		||||
        if (mouse_mode === RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
            for (var j=0;j<movingSet.length();j++) {
 | 
			
		||||
                var n = movingSet.get(j);
 | 
			
		||||
                n.n.x = n.ox;
 | 
			
		||||
                n.n.y = n.oy;
 | 
			
		||||
            }
 | 
			
		||||
            clearSelection();
 | 
			
		||||
            RED.history.pop();
 | 
			
		||||
            mouse_mode = 0;
 | 
			
		||||
        } else if (mouse_mode === RED.state.IMPORT_DRAGGING) {
 | 
			
		||||
            clearSelection();
 | 
			
		||||
            RED.history.pop();
 | 
			
		||||
            mouse_mode = 0;
 | 
			
		||||
@@ -2133,7 +2165,7 @@ RED.view = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function deleteSelection() {
 | 
			
		||||
    function deleteSelection(reconnectWires) {
 | 
			
		||||
        if (mouse_mode === RED.state.SELECTING_NODE) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -2191,6 +2223,16 @@ RED.view = (function() {
 | 
			
		||||
            var removedSubflowInputs = [];
 | 
			
		||||
            var removedSubflowStatus;
 | 
			
		||||
            var subflowInstances = [];
 | 
			
		||||
            var historyEvents = [];
 | 
			
		||||
 | 
			
		||||
            if (reconnectWires) {
 | 
			
		||||
                var reconnectResult = RED.nodes.detachNodes(movingSet.nodes())
 | 
			
		||||
                var addedLinks = reconnectResult.newLinks;
 | 
			
		||||
                if (addedLinks.length > 0) {
 | 
			
		||||
                    historyEvents.push({ t:'add', links: addedLinks })
 | 
			
		||||
                }
 | 
			
		||||
                removedLinks = removedLinks.concat(reconnectResult.removedLinks)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var startDirty = RED.nodes.dirty();
 | 
			
		||||
            var startChanged = false;
 | 
			
		||||
@@ -2281,7 +2323,6 @@ RED.view = (function() {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var linkRemoveHistoryEvents = [];
 | 
			
		||||
            if (selectedLinks.length() > 0) {
 | 
			
		||||
                selectedLinks.forEach(function(link) {
 | 
			
		||||
                    if (link.link) {
 | 
			
		||||
@@ -2289,31 +2330,22 @@ RED.view = (function() {
 | 
			
		||||
                        var targetId = link.target.id;
 | 
			
		||||
                        var sourceIdIndex = link.target.links.indexOf(sourceId);
 | 
			
		||||
                        var targetIdIndex = link.source.links.indexOf(targetId);
 | 
			
		||||
 | 
			
		||||
                        linkRemoveHistoryEvents.push({
 | 
			
		||||
                            t:"multi",
 | 
			
		||||
                            events: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    t: "edit",
 | 
			
		||||
                                    node: link.source,
 | 
			
		||||
                                    changed: link.source.changed,
 | 
			
		||||
                                    changes: {
 | 
			
		||||
                                        links: $.extend(true,{},{v:link.source.links}).v
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    t: "edit",
 | 
			
		||||
                                    node: link.target,
 | 
			
		||||
                                    changed: link.target.changed,
 | 
			
		||||
                                    changes: {
 | 
			
		||||
                                        links: $.extend(true,{},{v:link.target.links}).v
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            ],
 | 
			
		||||
                            dirty:startDirty
 | 
			
		||||
                        historyEvents.push({
 | 
			
		||||
                            t: "edit",
 | 
			
		||||
                            node: link.source,
 | 
			
		||||
                            changed: link.source.changed,
 | 
			
		||||
                            changes: {
 | 
			
		||||
                                links: $.extend(true,{},{v:link.source.links}).v
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        historyEvents.push({
 | 
			
		||||
                            t: "edit",
 | 
			
		||||
                            node: link.target,
 | 
			
		||||
                            changed: link.target.changed,
 | 
			
		||||
                            changes: {
 | 
			
		||||
                                links: $.extend(true,{},{v:link.target.links}).v
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
 | 
			
		||||
                        link.source.changed = true;
 | 
			
		||||
                        link.target.changed = true;
 | 
			
		||||
                        link.target.links.splice(sourceIdIndex,1);
 | 
			
		||||
@@ -2344,11 +2376,11 @@ RED.view = (function() {
 | 
			
		||||
            if (removedSubflowStatus) {
 | 
			
		||||
                historyEvent.subflow.status = removedSubflowStatus;
 | 
			
		||||
            }
 | 
			
		||||
            if (linkRemoveHistoryEvents.length > 0) {
 | 
			
		||||
                linkRemoveHistoryEvents.unshift(historyEvent);
 | 
			
		||||
            if (historyEvents.length > 0) {
 | 
			
		||||
                historyEvents.unshift(historyEvent);
 | 
			
		||||
                RED.history.push({
 | 
			
		||||
                    t:"multi",
 | 
			
		||||
                    events: linkRemoveHistoryEvents
 | 
			
		||||
                    events: historyEvents
 | 
			
		||||
                })
 | 
			
		||||
            } else {
 | 
			
		||||
                RED.history.push(historyEvent);
 | 
			
		||||
@@ -2431,6 +2463,28 @@ RED.view = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function detachSelectedNodes() {
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes);
 | 
			
		||||
            if (removedLinks.length || newLinks.length) {
 | 
			
		||||
                RED.history.push({
 | 
			
		||||
                    t: "multi",
 | 
			
		||||
                    events: [
 | 
			
		||||
                        { t:'delete', links: removedLinks },
 | 
			
		||||
                        { t:'add', links: newLinks }
 | 
			
		||||
                    ],
 | 
			
		||||
                    dirty: RED.nodes.dirty()
 | 
			
		||||
                })
 | 
			
		||||
                RED.nodes.dirty(true)
 | 
			
		||||
            }
 | 
			
		||||
            prepareDrag([selection.nodes[0].x,selection.nodes[0].y]);
 | 
			
		||||
            mouse_mode = RED.state.DETACHED_DRAGGING;
 | 
			
		||||
            RED.view.redraw(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function calculateTextWidth(str, className) {
 | 
			
		||||
        var result = convertLineBreakCharacter(str);
 | 
			
		||||
        var width = 0;
 | 
			
		||||
@@ -2956,10 +3010,13 @@ RED.view = (function() {
 | 
			
		||||
            msn.dx = msn.n.x-mouse[0];
 | 
			
		||||
            msn.dy = msn.n.y-mouse[1];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        mouse_offset = d3.mouse(document.body);
 | 
			
		||||
        if (isNaN(mouse_offset[0])) {
 | 
			
		||||
            mouse_offset = d3.touches(document.body)[0];
 | 
			
		||||
        try {
 | 
			
		||||
            mouse_offset = d3.mouse(document.body);
 | 
			
		||||
            if (isNaN(mouse_offset[0])) {
 | 
			
		||||
                mouse_offset = d3.touches(document.body)[0];
 | 
			
		||||
            }
 | 
			
		||||
        } catch(err) {
 | 
			
		||||
            mouse_offset = [0,0]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -3040,7 +3097,7 @@ RED.view = (function() {
 | 
			
		||||
        //var touch0 = d3.event;
 | 
			
		||||
        //var pos = [touch0.pageX,touch0.pageY];
 | 
			
		||||
        //RED.touch.radialMenu.show(d3.select(this),pos);
 | 
			
		||||
        if (mouse_mode == RED.state.IMPORT_DRAGGING) {
 | 
			
		||||
        if (mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
            var historyEvent = RED.history.peek();
 | 
			
		||||
            if (activeSpliceLink) {
 | 
			
		||||
                // TODO: DRY - droppable/nodeMouseDown/canvasMouseUp
 | 
			
		||||
@@ -3078,6 +3135,18 @@ RED.view = (function() {
 | 
			
		||||
                activeHoverGroup = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (mouse_mode == RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
                var ns = [];
 | 
			
		||||
                for (var j=0;j<movingSet.length();j++) {
 | 
			
		||||
                    var n = movingSet.get(j);
 | 
			
		||||
                    if (n.ox !== n.n.x || n.oy !== n.n.y) {
 | 
			
		||||
                        ns.push({n:n.n,ox:n.ox,oy:n.oy,moved:n.n.moved});
 | 
			
		||||
                        n.n.dirty = true;
 | 
			
		||||
                        n.n.moved = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                RED.history.replace({t:"multi",events:[historyEvent,{t:"move",nodes:ns}],dirty: historyEvent.dirty})
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            updateSelection();
 | 
			
		||||
            RED.nodes.dirty(true);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user