mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	@@ -15,5 +15,5 @@
 | 
			
		||||
    "shadow": true,     // allow variable shadowing (re-use of names...)
 | 
			
		||||
    "sub": true,        // don't warn that foo['bar'] should be written as foo.bar
 | 
			
		||||
    "proto": true,      // allow setting of __proto__ in node < v0.12,
 | 
			
		||||
    "esversion": 6      // allow es6
 | 
			
		||||
    "esversion": 11      // allow es11(ES2020)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,8 @@
 | 
			
		||||
        "selectNodes": "Click nodes to select",
 | 
			
		||||
        "enableFlow": "Enable flow",
 | 
			
		||||
        "disableFlow": "Disable flow",
 | 
			
		||||
        "lockFlow": "Lock flow",
 | 
			
		||||
        "unlockFlow": "Unlock flow",
 | 
			
		||||
        "moveToStart": "Move flow to start",
 | 
			
		||||
        "moveToEnd": "Move flow to end"
 | 
			
		||||
    },
 | 
			
		||||
@@ -105,6 +107,7 @@
 | 
			
		||||
            "displayStatus": "Show node status",
 | 
			
		||||
            "displayConfig": "Configuration nodes",
 | 
			
		||||
            "import": "Import",
 | 
			
		||||
            "importExample": "Import Example Flow",
 | 
			
		||||
            "export": "Export",
 | 
			
		||||
            "search": "Search flows",
 | 
			
		||||
            "searchInput": "search your flows",
 | 
			
		||||
@@ -501,6 +504,7 @@
 | 
			
		||||
        "addRemoveNode": "Add/remove node from selection",
 | 
			
		||||
        "editSelected": "Edit selected node",
 | 
			
		||||
        "deleteSelected": "Delete selected nodes or link",
 | 
			
		||||
        "deleteReconnect": "Delete and Reconnect",
 | 
			
		||||
        "importNode": "Import nodes",
 | 
			
		||||
        "exportNode": "Export nodes",
 | 
			
		||||
        "nudgeNode": "Move selected nodes (1px)",
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
/** 
 | 
			
		||||
/**
 | 
			
		||||
 * An API for undo / redo history buffer
 | 
			
		||||
 * @namespace RED.history
 | 
			
		||||
*/
 | 
			
		||||
@@ -434,7 +434,9 @@ RED.history = (function() {
 | 
			
		||||
 | 
			
		||||
                if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('disabled')) {
 | 
			
		||||
                    $("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!ev.node.disabled);
 | 
			
		||||
                    $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!ev.node.disabled);
 | 
			
		||||
                }
 | 
			
		||||
                if (ev.node.type === 'tab' && ev.changes.hasOwnProperty('locked')) {
 | 
			
		||||
                    $("#red-ui-tab-"+(ev.node.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!ev.node.locked);
 | 
			
		||||
                }
 | 
			
		||||
                if (ev.subflow) {
 | 
			
		||||
                    inverseEv.subflow = {};
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
 * @namespace RED.nodes
 | 
			
		||||
*/
 | 
			
		||||
RED.nodes = (function() {
 | 
			
		||||
 | 
			
		||||
    var PORT_TYPE_INPUT = 1;
 | 
			
		||||
    var PORT_TYPE_OUTPUT = 0;
 | 
			
		||||
 | 
			
		||||
@@ -63,6 +62,7 @@ RED.nodes = (function() {
 | 
			
		||||
            defaults: {
 | 
			
		||||
                label: {value:""},
 | 
			
		||||
                disabled: {value: false},
 | 
			
		||||
                locked: {value: false},
 | 
			
		||||
                info: {value: ""},
 | 
			
		||||
                env: {value: []}
 | 
			
		||||
            }
 | 
			
		||||
@@ -575,15 +575,48 @@ RED.nodes = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const nodeProxyHandler = {
 | 
			
		||||
        get(node, prop) {
 | 
			
		||||
            if (prop === '__isProxy__') {
 | 
			
		||||
                return true
 | 
			
		||||
            } else if (prop == '__node__') {
 | 
			
		||||
                return node
 | 
			
		||||
            }
 | 
			
		||||
            return node[prop]
 | 
			
		||||
        },
 | 
			
		||||
        set(node, prop, value) {
 | 
			
		||||
            if (node.z && (RED.nodes.workspace(node.z)?.locked || RED.nodes.subflow(node.z)?.locked)) {
 | 
			
		||||
                if (
 | 
			
		||||
                    node._def.defaults[prop] ||
 | 
			
		||||
                    prop === 'z' ||
 | 
			
		||||
                    prop === 'l' ||
 | 
			
		||||
                    prop === 'd' ||
 | 
			
		||||
                    (prop === 'changed' && (!!node.changed) !== (!!value)) || // jshint ignore:line
 | 
			
		||||
                    ((prop === 'x' || prop === 'y') && !node.resize && node.type !== 'group')
 | 
			
		||||
                ) {
 | 
			
		||||
                    throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            node[prop] = value;
 | 
			
		||||
            return true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addNode(n) {
 | 
			
		||||
        let newNode
 | 
			
		||||
        if (!n.__isProxy__) {
 | 
			
		||||
            newNode = new Proxy(n, nodeProxyHandler)
 | 
			
		||||
        } else {
 | 
			
		||||
            newNode = n
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (n.type.indexOf("subflow") !== 0) {
 | 
			
		||||
            n["_"] = n._def._;
 | 
			
		||||
        } else {
 | 
			
		||||
            var subflowId = n.type.substring(8);
 | 
			
		||||
            var sf = RED.nodes.subflow(subflowId);
 | 
			
		||||
            if (sf) {
 | 
			
		||||
                sf.instances.push(sf);
 | 
			
		||||
                sf.instances.push(newNode);
 | 
			
		||||
            }
 | 
			
		||||
            n["_"] = RED._;
 | 
			
		||||
        }
 | 
			
		||||
@@ -600,12 +633,13 @@ RED.nodes = (function() {
 | 
			
		||||
                });
 | 
			
		||||
                n.i = nextId+1;
 | 
			
		||||
            }
 | 
			
		||||
            allNodes.addNode(n);
 | 
			
		||||
            allNodes.addNode(newNode);
 | 
			
		||||
            if (!nodeLinks[n.id]) {
 | 
			
		||||
                nodeLinks[n.id] = {in:[],out:[]};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        RED.events.emit('nodes:add',n);
 | 
			
		||||
        RED.events.emit('nodes:add',newNode);
 | 
			
		||||
        return newNode
 | 
			
		||||
    }
 | 
			
		||||
    function addLink(l) {
 | 
			
		||||
        if (nodeLinks[l.source.id]) {
 | 
			
		||||
@@ -1052,6 +1086,9 @@ RED.nodes = (function() {
 | 
			
		||||
        node.type = n.type;
 | 
			
		||||
        for (var d in n._def.defaults) {
 | 
			
		||||
            if (n._def.defaults.hasOwnProperty(d)) {
 | 
			
		||||
                if (d === 'locked' && !n.locked) {
 | 
			
		||||
                    continue
 | 
			
		||||
                }
 | 
			
		||||
                node[d] = n[d];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -1331,7 +1368,6 @@ RED.nodes = (function() {
 | 
			
		||||
        } else {
 | 
			
		||||
            nodeSet = [sf];
 | 
			
		||||
        }
 | 
			
		||||
        console.log(nodeSet);
 | 
			
		||||
        return createExportableNodeSet(nodeSet);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
@@ -2318,19 +2354,6 @@ RED.nodes = (function() {
 | 
			
		||||
            if (n.g && !new_group_set.has(n.g)) {
 | 
			
		||||
                delete n.g;
 | 
			
		||||
            }
 | 
			
		||||
            n.nodes = n.nodes.map(function(id) {
 | 
			
		||||
                return node_map[id];
 | 
			
		||||
            })
 | 
			
		||||
            // Just in case the group references a node that doesn't exist for some reason
 | 
			
		||||
            n.nodes = n.nodes.filter(function(v) {
 | 
			
		||||
                if (v) {
 | 
			
		||||
                    // Repair any nodes that have forgotten they are in this group
 | 
			
		||||
                    if (v.g !== n.id) {
 | 
			
		||||
                        v.g = n.id;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return !!v
 | 
			
		||||
            });
 | 
			
		||||
            if (!n.g) {
 | 
			
		||||
                groupDepthMap[n.id] = 0;
 | 
			
		||||
            }
 | 
			
		||||
@@ -2353,21 +2376,22 @@ RED.nodes = (function() {
 | 
			
		||||
            return groupDepthMap[A.id] - groupDepthMap[B.id];
 | 
			
		||||
        });
 | 
			
		||||
        for (i=0;i<new_groups.length;i++) {
 | 
			
		||||
            n = new_groups[i];
 | 
			
		||||
            addGroup(n);
 | 
			
		||||
            new_groups[i] = addGroup(new_groups[i]);
 | 
			
		||||
            node_map[new_groups[i].id] = new_groups[i]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (i=0;i<new_junctions.length;i++) {
 | 
			
		||||
            var junction = new_junctions[i];
 | 
			
		||||
            addJunction(junction);
 | 
			
		||||
            new_junctions[i] = addJunction(new_junctions[i]);
 | 
			
		||||
            node_map[new_junctions[i].id] = new_junctions[i]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Now the nodes have been fully updated, add them.
 | 
			
		||||
        for (i=0;i<new_nodes.length;i++) {
 | 
			
		||||
            var node = new_nodes[i];
 | 
			
		||||
            addNode(node);
 | 
			
		||||
            new_nodes[i] = addNode(new_nodes[i])
 | 
			
		||||
            node_map[new_nodes[i].id] = new_nodes[i]
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Finally validate them all.
 | 
			
		||||
        // This has to be done after everything is added so that any checks for
 | 
			
		||||
        // dependent config nodes will pass
 | 
			
		||||
@@ -2375,6 +2399,39 @@ RED.nodes = (function() {
 | 
			
		||||
            var node = new_nodes[i];
 | 
			
		||||
            RED.editor.validateNode(node);
 | 
			
		||||
        }
 | 
			
		||||
        const lookupNode = (id) => {
 | 
			
		||||
            const mappedNode = node_map[id]
 | 
			
		||||
            if (!mappedNode) {
 | 
			
		||||
                return null
 | 
			
		||||
            }
 | 
			
		||||
            if (mappedNode.__isProxy__) {
 | 
			
		||||
                return mappedNode
 | 
			
		||||
            } else {
 | 
			
		||||
                return node_map[mappedNode.id]
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Update groups to reference proxy node objects
 | 
			
		||||
        for (i=0;i<new_groups.length;i++) {
 | 
			
		||||
            n = new_groups[i];
 | 
			
		||||
            // bypass the proxy in case the flow is locked
 | 
			
		||||
            n.__node__.nodes = n.nodes.map(lookupNode)
 | 
			
		||||
            // Just in case the group references a node that doesn't exist for some reason
 | 
			
		||||
            n.__node__.nodes = n.nodes.filter(function(v) {
 | 
			
		||||
                if (v) {
 | 
			
		||||
                    // Repair any nodes that have forgotten they are in this group
 | 
			
		||||
                    if (v.g !== n.id) {
 | 
			
		||||
                        v.g = n.id;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return !!v
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update links to use proxy node objects
 | 
			
		||||
        for (i=0;i<new_links.length;i++) {
 | 
			
		||||
            new_links[i].source = lookupNode(new_links[i].source.id) || new_links[i].source
 | 
			
		||||
            new_links[i].target = lookupNode(new_links[i].target.id) || new_links[i].target
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        RED.workspaces.refresh();
 | 
			
		||||
 | 
			
		||||
@@ -2503,11 +2560,17 @@ RED.nodes = (function() {
 | 
			
		||||
        junctions = {};
 | 
			
		||||
        junctionsByZ = {};
 | 
			
		||||
 | 
			
		||||
        var workspaceIds = Object.keys(workspaces);
 | 
			
		||||
        // Ensure all workspaces are unlocked so we don't get any edit-protection
 | 
			
		||||
        // preventing removal
 | 
			
		||||
        workspaceIds.forEach(function(id) {
 | 
			
		||||
            workspaces[id].locked = false
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        var subflowIds = Object.keys(subflows);
 | 
			
		||||
        subflowIds.forEach(function(id) {
 | 
			
		||||
            RED.subflow.removeSubflow(id)
 | 
			
		||||
        });
 | 
			
		||||
        var workspaceIds = Object.keys(workspaces);
 | 
			
		||||
        workspaceIds.forEach(function(id) {
 | 
			
		||||
            RED.workspaces.remove(workspaces[id]);
 | 
			
		||||
        });
 | 
			
		||||
@@ -2528,10 +2591,14 @@ RED.nodes = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addGroup(group) {
 | 
			
		||||
        if (!group.__isProxy__) {
 | 
			
		||||
            group = new Proxy(group, nodeProxyHandler)
 | 
			
		||||
        }
 | 
			
		||||
        groupsByZ[group.z] = groupsByZ[group.z] || [];
 | 
			
		||||
        groupsByZ[group.z].push(group);
 | 
			
		||||
        groups[group.id] = group;
 | 
			
		||||
        RED.events.emit("groups:add",group);
 | 
			
		||||
        return group
 | 
			
		||||
    }
 | 
			
		||||
    function removeGroup(group) {
 | 
			
		||||
        var i = groupsByZ[group.z].indexOf(group);
 | 
			
		||||
@@ -2552,6 +2619,9 @@ RED.nodes = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addJunction(junction) {
 | 
			
		||||
        if (!junction.__isProxy__) {
 | 
			
		||||
            junction = new Proxy(junction, nodeProxyHandler)
 | 
			
		||||
        }
 | 
			
		||||
        junctionsByZ[junction.z] = junctionsByZ[junction.z] || []
 | 
			
		||||
        junctionsByZ[junction.z].push(junction)
 | 
			
		||||
        junctions[junction.id] = junction;
 | 
			
		||||
@@ -2559,6 +2629,7 @@ RED.nodes = (function() {
 | 
			
		||||
            nodeLinks[junction.id] = {in:[],out:[]};
 | 
			
		||||
        }
 | 
			
		||||
        RED.events.emit("junctions:add", junction)
 | 
			
		||||
        return junction
 | 
			
		||||
    }
 | 
			
		||||
    function removeJunction(junction) {
 | 
			
		||||
        var i = junctionsByZ[junction.z].indexOf(junction)
 | 
			
		||||
 
 | 
			
		||||
@@ -668,11 +668,6 @@ var RED = (function() {
 | 
			
		||||
        ]});
 | 
			
		||||
 | 
			
		||||
        menuOptions.push({id:"menu-item-arrange-menu", label:RED._("menu.label.arrange"), options: [
 | 
			
		||||
            {id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"},
 | 
			
		||||
            null,
 | 
			
		||||
            {id: "menu-item-view-tools-align-left", label:RED._("menu.label.alignLeft"), disabled: true, onselect: "core:align-selection-to-left"},
 | 
			
		||||
            {id: "menu-item-view-tools-align-center", label:RED._("menu.label.alignCenter"), disabled: true, onselect: "core:align-selection-to-center"},
 | 
			
		||||
            {id: "menu-item-view-tools-align-right", label:RED._("menu.label.alignRight"), disabled: true, onselect: "core:align-selection-to-right"},
 | 
			
		||||
@@ -682,7 +677,12 @@ var RED = (function() {
 | 
			
		||||
            {id: "menu-item-view-tools-align-bottom", label:RED._("menu.label.alignBottom"), disabled: true, onselect: "core:align-selection-to-bottom"},
 | 
			
		||||
            null,
 | 
			
		||||
            {id: "menu-item-view-tools-distribute-horizontally", label:RED._("menu.label.distributeHorizontally"), disabled: true, onselect: "core:distribute-selection-horizontally"},
 | 
			
		||||
            {id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"}
 | 
			
		||||
            {id: "menu-item-view-tools-distribute-veritcally", label:RED._("menu.label.distributeVertically"), disabled: true, onselect: "core:distribute-selection-vertically"},
 | 
			
		||||
            null,
 | 
			
		||||
            {id: "menu-item-view-tools-move-to-back", label:RED._("menu.label.moveToBack"), disabled: true, onselect: "core:move-selection-to-back"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-to-front", label:RED._("menu.label.moveToFront"), disabled: true, onselect: "core:move-selection-to-front"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-backwards", label:RED._("menu.label.moveBackwards"), disabled: true, onselect: "core:move-selection-backwards"},
 | 
			
		||||
            {id: "menu-item-view-tools-move-forwards", label:RED._("menu.label.moveForwards"), disabled: true, onselect: "core:move-selection-forwards"}
 | 
			
		||||
        ]});
 | 
			
		||||
 | 
			
		||||
        menuOptions.push(null);
 | 
			
		||||
 
 | 
			
		||||
@@ -503,7 +503,7 @@ RED.clipboard = (function() {
 | 
			
		||||
        $("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport);
 | 
			
		||||
        $("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)});
 | 
			
		||||
 | 
			
		||||
        if (RED.workspaces.active() === 0) {
 | 
			
		||||
        if (RED.workspaces.active() === 0 || RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            $("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected");
 | 
			
		||||
            $("#red-ui-clipboard-dialog-import-opt-new").addClass("selected");
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -1271,15 +1271,17 @@ RED.clipboard = (function() {
 | 
			
		||||
            RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget);
 | 
			
		||||
 | 
			
		||||
            $('#red-ui-workspace-chart').on("dragenter",function(event) {
 | 
			
		||||
                if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
 | 
			
		||||
                     $.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                if (!RED.workspaces.isActiveLocked() && (
 | 
			
		||||
                    $.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
 | 
			
		||||
                     $.inArray("Files",event.originalEvent.dataTransfer.types) != -1)) {
 | 
			
		||||
                    $("#red-ui-drop-target").css({display:'table'}).focus();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            $('#red-ui-drop-target').on("dragover",function(event) {
 | 
			
		||||
                if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 ||
 | 
			
		||||
                     $.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                     $.inArray("Files",event.originalEvent.dataTransfer.types) != -1 ||
 | 
			
		||||
                        RED.workspaces.isActiveLocked()) {
 | 
			
		||||
                    event.preventDefault();
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
@@ -1287,27 +1289,29 @@ RED.clipboard = (function() {
 | 
			
		||||
                hideDropTarget();
 | 
			
		||||
            })
 | 
			
		||||
            .on("drop",function(event) {
 | 
			
		||||
                try {
 | 
			
		||||
                    if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                        var data = event.originalEvent.dataTransfer.getData("text/plain");
 | 
			
		||||
                        data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
 | 
			
		||||
                        importNodes(data);
 | 
			
		||||
                    } else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                        var files = event.originalEvent.dataTransfer.files;
 | 
			
		||||
                        if (files.length === 1) {
 | 
			
		||||
                            var file = files[0];
 | 
			
		||||
                            var reader = new FileReader();
 | 
			
		||||
                            reader.onload = (function(theFile) {
 | 
			
		||||
                                return function(e) {
 | 
			
		||||
                                    importNodes(e.target.result);
 | 
			
		||||
                                };
 | 
			
		||||
                            })(file);
 | 
			
		||||
                            reader.readAsText(file);
 | 
			
		||||
                if (!RED.workspaces.isActiveLocked()) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                            var data = event.originalEvent.dataTransfer.getData("text/plain");
 | 
			
		||||
                            data = data.substring(data.indexOf('['),data.lastIndexOf(']')+1);
 | 
			
		||||
                            importNodes(data);
 | 
			
		||||
                        } else if ($.inArray("Files",event.originalEvent.dataTransfer.types) != -1) {
 | 
			
		||||
                            var files = event.originalEvent.dataTransfer.files;
 | 
			
		||||
                            if (files.length === 1) {
 | 
			
		||||
                                var file = files[0];
 | 
			
		||||
                                var reader = new FileReader();
 | 
			
		||||
                                reader.onload = (function(theFile) {
 | 
			
		||||
                                    return function(e) {
 | 
			
		||||
                                        importNodes(e.target.result);
 | 
			
		||||
                                    };
 | 
			
		||||
                                })(file);
 | 
			
		||||
                                reader.readAsText(file);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } catch(err) {
 | 
			
		||||
                        // Ensure any errors throw above doesn't stop the drop target from
 | 
			
		||||
                        // being hidden.
 | 
			
		||||
                    }
 | 
			
		||||
                } catch(err) {
 | 
			
		||||
                    // Ensure any errors throw above doesn't stop the drop target from
 | 
			
		||||
                    // being hidden.
 | 
			
		||||
                }
 | 
			
		||||
                hideDropTarget();
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,10 @@ RED.contextMenu = (function () {
 | 
			
		||||
            const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1
 | 
			
		||||
            const canDelete = hasSelection || hasLinks
 | 
			
		||||
            const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group'
 | 
			
		||||
 | 
			
		||||
            const canEdit = !RED.workspaces.isActiveLocked()
 | 
			
		||||
            const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g
 | 
			
		||||
            const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0
 | 
			
		||||
            const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0
 | 
			
		||||
            const offset = $("#red-ui-workspace-chart").offset()
 | 
			
		||||
 | 
			
		||||
            let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft()
 | 
			
		||||
@@ -42,89 +44,133 @@ RED.contextMenu = (function () {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            menuItems.push(
 | 
			
		||||
                { onselect: 'core:show-action-list', onpostselect: function () { } },
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("contextMenu.insert"),
 | 
			
		||||
                    options: [
 | 
			
		||||
                        {
 | 
			
		||||
                            label: RED._("contextMenu.node"),
 | 
			
		||||
                            onselect: function () {
 | 
			
		||||
                                RED.view.showQuickAddDialog({
 | 
			
		||||
                                    position: [addX, addY],
 | 
			
		||||
                                    touchTrigger: true,
 | 
			
		||||
                                    splice: isSingleLink ? selection.links[0] : undefined,
 | 
			
		||||
                                    // spliceMultiple: isMultipleLinks
 | 
			
		||||
                                })
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        (hasLinks) ? { // has least 1 wire selected
 | 
			
		||||
                            label: RED._("contextMenu.junction"),
 | 
			
		||||
                            onselect: 'core:split-wires-with-junctions',
 | 
			
		||||
                            disabled: !hasLinks
 | 
			
		||||
                        } : {
 | 
			
		||||
                            label: RED._("contextMenu.junction"),
 | 
			
		||||
                            onselect: function () {
 | 
			
		||||
                                const nn = {
 | 
			
		||||
                                    _def: { defaults: {} },
 | 
			
		||||
                                    type: 'junction',
 | 
			
		||||
                                    z: RED.workspaces.active(),
 | 
			
		||||
                                    id: RED.nodes.id(),
 | 
			
		||||
                                    x: addX,
 | 
			
		||||
                                    y: addY,
 | 
			
		||||
                                    w: 0, h: 0,
 | 
			
		||||
                                    outputs: 1,
 | 
			
		||||
                                    inputs: 1,
 | 
			
		||||
                                    dirty: true
 | 
			
		||||
                                }
 | 
			
		||||
                                const historyEvent = {
 | 
			
		||||
                                    dirty: RED.nodes.dirty(),
 | 
			
		||||
                                    t: 'add',
 | 
			
		||||
                                    junctions: [nn]
 | 
			
		||||
                                }
 | 
			
		||||
                                RED.nodes.addJunction(nn);
 | 
			
		||||
                                RED.history.push(historyEvent);
 | 
			
		||||
                                RED.nodes.dirty(true);
 | 
			
		||||
                                RED.view.select({nodes: [nn] });
 | 
			
		||||
                                RED.view.redraw(true)
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            label: RED._("contextMenu.linkNodes"),
 | 
			
		||||
                            onselect: 'core:split-wire-with-link-nodes',
 | 
			
		||||
                            disabled: !hasLinks
 | 
			
		||||
                        }
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                { onselect: 'core:show-action-list', onpostselect: function () { } }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            const insertOptions = []
 | 
			
		||||
            menuItems.push({ label: RED._("contextMenu.insert"), options: insertOptions })
 | 
			
		||||
            insertOptions.push(
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("contextMenu.node"),
 | 
			
		||||
                    onselect: function () {
 | 
			
		||||
                        RED.view.showQuickAddDialog({
 | 
			
		||||
                            position: [addX, addY],
 | 
			
		||||
                            touchTrigger: true,
 | 
			
		||||
                            splice: isSingleLink ? selection.links[0] : undefined,
 | 
			
		||||
                            // spliceMultiple: isMultipleLinks
 | 
			
		||||
                        })
 | 
			
		||||
                    },
 | 
			
		||||
                    disabled: !canEdit
 | 
			
		||||
                },
 | 
			
		||||
                (hasLinks) ? { // has least 1 wire selected
 | 
			
		||||
                    label: RED._("contextMenu.junction"),
 | 
			
		||||
                    onselect: 'core:split-wires-with-junctions',
 | 
			
		||||
                    disabled: !canEdit || !hasLinks
 | 
			
		||||
                } : {
 | 
			
		||||
                    label: RED._("contextMenu.junction"),
 | 
			
		||||
                    onselect: function () {
 | 
			
		||||
                        const nn = {
 | 
			
		||||
                            _def: { defaults: {} },
 | 
			
		||||
                            type: 'junction',
 | 
			
		||||
                            z: RED.workspaces.active(),
 | 
			
		||||
                            id: RED.nodes.id(),
 | 
			
		||||
                            x: addX,
 | 
			
		||||
                            y: addY,
 | 
			
		||||
                            w: 0, h: 0,
 | 
			
		||||
                            outputs: 1,
 | 
			
		||||
                            inputs: 1,
 | 
			
		||||
                            dirty: true
 | 
			
		||||
                        }
 | 
			
		||||
                        const historyEvent = {
 | 
			
		||||
                            dirty: RED.nodes.dirty(),
 | 
			
		||||
                            t: 'add',
 | 
			
		||||
                            junctions: [nn]
 | 
			
		||||
                        }
 | 
			
		||||
                        RED.nodes.addJunction(nn);
 | 
			
		||||
                        RED.history.push(historyEvent);
 | 
			
		||||
                        RED.nodes.dirty(true);
 | 
			
		||||
                        RED.view.select({nodes: [nn] });
 | 
			
		||||
                        RED.view.redraw(true)
 | 
			
		||||
                    },
 | 
			
		||||
                    disabled: !canEdit
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("contextMenu.linkNodes"),
 | 
			
		||||
                    onselect: 'core:split-wire-with-link-nodes',
 | 
			
		||||
                    disabled: !canEdit || !hasLinks
 | 
			
		||||
                },
 | 
			
		||||
                null,
 | 
			
		||||
                { onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
 | 
			
		||||
                { onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
 | 
			
		||||
            )
 | 
			
		||||
            if (hasSelection && canEdit) {
 | 
			
		||||
                const nodeOptions = []
 | 
			
		||||
                if (!hasMultipleSelection && !isGroup) {
 | 
			
		||||
                    nodeOptions.push(
 | 
			
		||||
                        { onselect: 'core:show-node-help' },
 | 
			
		||||
                        null
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                nodeOptions.push(
 | 
			
		||||
                    { onselect: 'core:enable-selected-nodes' },
 | 
			
		||||
                    { onselect: 'core:disable-selected-nodes' },
 | 
			
		||||
                    null,
 | 
			
		||||
                    { onselect: 'core:show-selected-node-labels' },
 | 
			
		||||
                    { onselect: 'core:hide-selected-node-labels' }
 | 
			
		||||
                )
 | 
			
		||||
                menuItems.push({
 | 
			
		||||
                    label: RED._('sidebar.info.node'),
 | 
			
		||||
                    options: nodeOptions
 | 
			
		||||
                })
 | 
			
		||||
                menuItems.push({
 | 
			
		||||
                    label: RED._('sidebar.info.group'),
 | 
			
		||||
                    options: [
 | 
			
		||||
                        { onselect: 'core:group-selection' },
 | 
			
		||||
                        { onselect: 'core:ungroup-selection', disabled: !hasGroup },
 | 
			
		||||
                        null,
 | 
			
		||||
                        { onselect: 'core:copy-group-style', disabled: !hasGroup },
 | 
			
		||||
                        { onselect: 'core:paste-group-style', disabled: !hasGroup}
 | 
			
		||||
                    ]
 | 
			
		||||
                })
 | 
			
		||||
                if (canRemoveFromGroup) {
 | 
			
		||||
                    menuItems[menuItems.length - 1].options.push(
 | 
			
		||||
                        null,
 | 
			
		||||
                        { onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") }
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (canEdit && hasMultipleSelection) {
 | 
			
		||||
                menuItems.push({
 | 
			
		||||
                    label: RED._('menu.label.arrange'),
 | 
			
		||||
                    options: [
 | 
			
		||||
                        { label:RED._("menu.label.alignLeft"), onselect: "core:align-selection-to-left"},
 | 
			
		||||
                        { label:RED._("menu.label.alignCenter"), onselect: "core:align-selection-to-center"},
 | 
			
		||||
                        { label:RED._("menu.label.alignRight"), onselect: "core:align-selection-to-right"},
 | 
			
		||||
                        null,
 | 
			
		||||
                        { label:RED._("menu.label.alignTop"), onselect: "core:align-selection-to-top"},
 | 
			
		||||
                        { label:RED._("menu.label.alignMiddle"), onselect: "core:align-selection-to-middle"},
 | 
			
		||||
                        { label:RED._("menu.label.alignBottom"), onselect: "core:align-selection-to-bottom"},
 | 
			
		||||
                        null,
 | 
			
		||||
                        { label:RED._("menu.label.distributeHorizontally"), onselect: "core:distribute-selection-horizontally"},
 | 
			
		||||
                        { label:RED._("menu.label.distributeVertically"), onselect: "core:distribute-selection-vertically"}
 | 
			
		||||
                    ]
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            menuItems.push(
 | 
			
		||||
                null,
 | 
			
		||||
                { onselect: 'core:undo', disabled: RED.history.list().length === 0 },
 | 
			
		||||
                { onselect: 'core:redo', disabled: RED.history.listRedo().length === 0 },
 | 
			
		||||
                null,
 | 
			
		||||
                { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !hasSelection },
 | 
			
		||||
                { onselect: 'core:cut-selection-to-internal-clipboard', label: RED._("keyboard.cutNode"), disabled: !canEdit || !hasSelection },
 | 
			
		||||
                { onselect: 'core:copy-selection-to-internal-clipboard', label: RED._("keyboard.copyNode"), disabled: !hasSelection },
 | 
			
		||||
                { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !RED.view.clipboard() },
 | 
			
		||||
                { onselect: 'core:delete-selection', disabled: !canDelete },
 | 
			
		||||
                { onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
 | 
			
		||||
                { onselect: 'core:delete-selection', disabled: !canEdit || !canDelete },
 | 
			
		||||
                { onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
 | 
			
		||||
                { onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
 | 
			
		||||
                { onselect: 'core:select-all-nodes' }
 | 
			
		||||
                { onselect: 'core:select-all-nodes' },
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            if (hasSelection) {
 | 
			
		||||
                menuItems.push(
 | 
			
		||||
                    null,
 | 
			
		||||
                    isGroup ?
 | 
			
		||||
                        { onselect: 'core:ungroup-selection', disabled: !isGroup }
 | 
			
		||||
                        : { onselect: 'core:group-selection', disabled: !hasSelection }
 | 
			
		||||
                )
 | 
			
		||||
                if (canRemoveFromGroup) {
 | 
			
		||||
                    menuItems.push({ onselect: 'core:remove-selection-from-group', label: RED._("menu.label.groupRemoveSelection") })
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var direction = "right";
 | 
			
		||||
 
 | 
			
		||||
@@ -558,6 +558,11 @@ RED.deploy = (function() {
 | 
			
		||||
                RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
 | 
			
		||||
            }
 | 
			
		||||
            RED.nodes.eachNode(function (node) {
 | 
			
		||||
                const flow = node.z && (RED.nodes.workspace(node.z) || RED.nodes.subflow(node.z) || null);
 | 
			
		||||
                const isLocked = flow ? flow.locked : false;
 | 
			
		||||
                if (flow && isLocked) {
 | 
			
		||||
                    flow.locked = false;
 | 
			
		||||
                }
 | 
			
		||||
                if (node.changed) {
 | 
			
		||||
                    node.dirty = true;
 | 
			
		||||
                    node.changed = false;
 | 
			
		||||
@@ -569,6 +574,9 @@ RED.deploy = (function() {
 | 
			
		||||
                if (node.credentials) {
 | 
			
		||||
                    delete node.credentials;
 | 
			
		||||
                }
 | 
			
		||||
                if (flow && isLocked) {
 | 
			
		||||
                    flow.locked = isLocked;
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            RED.nodes.eachConfig(function (confNode) {
 | 
			
		||||
                confNode.changed = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -1847,11 +1847,15 @@ RED.editor = (function() {
 | 
			
		||||
                            workspace.disabled = disabled;
 | 
			
		||||
 | 
			
		||||
                            $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!workspace.disabled);
 | 
			
		||||
                            if (workspace.id === RED.workspaces.active()) {
 | 
			
		||||
                                $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        var locked = $("#node-input-locked").prop("checked");
 | 
			
		||||
                        if (workspace.locked !== locked) {
 | 
			
		||||
                            editState.changes.locked = workspace.locked;
 | 
			
		||||
                            editState.changed = true;
 | 
			
		||||
                            workspace.locked = locked;
 | 
			
		||||
                            $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (editState.changed) {
 | 
			
		||||
                            var historyEvent = {
 | 
			
		||||
                                t: "edit",
 | 
			
		||||
@@ -1892,6 +1896,7 @@ RED.editor = (function() {
 | 
			
		||||
                var trayBody = tray.find('.red-ui-tray-body');
 | 
			
		||||
                trayBody.parent().css('overflow','hidden');
 | 
			
		||||
                var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
 | 
			
		||||
                var trayFooterRight = $('<div class="red-ui-tray-footer-right"></div>').appendTo(trayFooter)
 | 
			
		||||
 | 
			
		||||
                var nodeEditPanes = [
 | 
			
		||||
                    'editor-tab-flow-properties',
 | 
			
		||||
@@ -1906,6 +1911,18 @@ RED.editor = (function() {
 | 
			
		||||
                    disabledIcon: "fa-ban",
 | 
			
		||||
                    invertState: true
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
                if (!workspace.hasOwnProperty("locked")) {
 | 
			
		||||
                    workspace.locked = false;
 | 
			
		||||
                }
 | 
			
		||||
                $('<input id="node-input-locked" type="checkbox">').prop("checked",workspace.locked).appendTo(trayFooterRight).toggleButton({
 | 
			
		||||
                    enabledLabel: 'Unlocked',
 | 
			
		||||
                    enabledIcon: "fa-unlock-alt",
 | 
			
		||||
                    disabledLabel: 'Locked',
 | 
			
		||||
                    disabledIcon: "fa-lock",
 | 
			
		||||
                    invertState: true
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
                prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", defaultTab, function(_activeEditPanes) {
 | 
			
		||||
                    activeEditPanes = _activeEditPanes;
 | 
			
		||||
                    trayBody.i18n();
 | 
			
		||||
 
 | 
			
		||||
@@ -52,8 +52,6 @@
 | 
			
		||||
                    node.info = info;
 | 
			
		||||
                }
 | 
			
		||||
                $("#red-ui-tab-"+(node.id.replace(".","-"))).toggleClass('red-ui-workspace-disabled',!!node.disabled);
 | 
			
		||||
                $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!node.disabled);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
@@ -185,6 +185,8 @@ RED.group = (function() {
 | 
			
		||||
            var activateMerge = false;
 | 
			
		||||
            var activateRemove = false;
 | 
			
		||||
            var singleGroupSelected = false;
 | 
			
		||||
            var locked = RED.workspaces.isActiveLocked()
 | 
			
		||||
 | 
			
		||||
            if (activateGroup) {
 | 
			
		||||
                singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group';
 | 
			
		||||
                selection.nodes.forEach(function (n) {
 | 
			
		||||
@@ -199,12 +201,12 @@ RED.group = (function() {
 | 
			
		||||
                    activateMerge = (selection.nodes.length > 1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-group", !activateGroup);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-ungroup", !activateUngroup);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-merge", !activateMerge);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-remove", !activateRemove);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-group", locked || !activateGroup);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-ungroup", locked || !activateUngroup);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-merge", locked || !activateMerge);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-group-remove", locked || !activateRemove);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-copy-group-style", !singleGroupSelected);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-paste-group-style", !activateUngroup);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-paste-group-style", locked || !activateUngroup);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:group-selection", function() { groupSelection() })
 | 
			
		||||
@@ -261,6 +263,7 @@ RED.group = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function pasteGroupStyle() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (RED.view.state() !== RED.state.DEFAULT) { return }
 | 
			
		||||
        if (groupStyleClipboard) {
 | 
			
		||||
            var selection = RED.view.selection();
 | 
			
		||||
@@ -295,6 +298,7 @@ RED.group = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function groupSelection() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (RED.view.state() !== RED.state.DEFAULT) { return }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
@@ -313,6 +317,7 @@ RED.group = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function ungroupSelection() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (RED.view.state() !== RED.state.DEFAULT) { return }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
@@ -336,6 +341,7 @@ RED.group = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function ungroup(g) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        var nodes = [];
 | 
			
		||||
        var parentGroup = RED.nodes.group(g.g);
 | 
			
		||||
        g.nodes.forEach(function(n) {
 | 
			
		||||
@@ -362,6 +368,7 @@ RED.group = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function mergeSelection() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (RED.view.state() !== RED.state.DEFAULT) { return }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
@@ -431,6 +438,7 @@ RED.group = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeSelection() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (RED.view.state() !== RED.state.DEFAULT) { return }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
@@ -458,6 +466,7 @@ RED.group = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function createGroup(nodes) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (nodes.length === 0) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -480,7 +489,7 @@ RED.group = (function() {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        group.z = nodes[0].z;
 | 
			
		||||
        RED.nodes.addGroup(group);
 | 
			
		||||
        group = RED.nodes.addGroup(group);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            addToGroup(group,nodes);
 | 
			
		||||
@@ -563,6 +572,7 @@ RED.group = (function() {
 | 
			
		||||
        markDirty(group);
 | 
			
		||||
    }
 | 
			
		||||
    function removeFromGroup(group, nodes, reparent) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (!Array.isArray(nodes)) {
 | 
			
		||||
            nodes = [nodes];
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -282,6 +282,7 @@ RED.palette = (function() {
 | 
			
		||||
            var hoverGroup;
 | 
			
		||||
            var paletteWidth;
 | 
			
		||||
            var paletteTop;
 | 
			
		||||
            var dropEnabled;
 | 
			
		||||
            $(d).draggable({
 | 
			
		||||
                helper: 'clone',
 | 
			
		||||
                appendTo: '#red-ui-editor',
 | 
			
		||||
@@ -289,6 +290,7 @@ RED.palette = (function() {
 | 
			
		||||
                revertDuration: 200,
 | 
			
		||||
                containment:'#red-ui-main-container',
 | 
			
		||||
                start: function() {
 | 
			
		||||
                    dropEnabled = !(RED.nodes.workspace(RED.workspaces.active())?.locked);
 | 
			
		||||
                    paletteWidth = $("#red-ui-palette").width();
 | 
			
		||||
                    paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top;
 | 
			
		||||
                    hoverGroup = null;
 | 
			
		||||
@@ -299,96 +301,100 @@ RED.palette = (function() {
 | 
			
		||||
                    RED.view.focus();
 | 
			
		||||
                },
 | 
			
		||||
                stop: function() {
 | 
			
		||||
                    d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                    if (hoverGroup) {
 | 
			
		||||
                        document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
 | 
			
		||||
                    if (dropEnabled) {
 | 
			
		||||
                        d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                        if (hoverGroup) {
 | 
			
		||||
                            document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
 | 
			
		||||
                        }
 | 
			
		||||
                        if (activeGroup) {
 | 
			
		||||
                            document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
 | 
			
		||||
                        }
 | 
			
		||||
                        if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
 | 
			
		||||
                        if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (activeGroup) {
 | 
			
		||||
                        document.getElementById("group_select_"+activeGroup.id).classList.remove("red-ui-flow-group-active-hovered");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (spliceTimer) { clearTimeout(spliceTimer); spliceTimer = null; }
 | 
			
		||||
                    if (groupTimer) { clearTimeout(groupTimer); groupTimer = null; }
 | 
			
		||||
                },
 | 
			
		||||
                drag: function(e,ui) {
 | 
			
		||||
                    var paletteNode = getPaletteNode(nt);
 | 
			
		||||
                    ui.originalPosition.left = paletteNode.offset().left;
 | 
			
		||||
                    mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
 | 
			
		||||
                    mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
 | 
			
		||||
                    if (!groupTimer) {
 | 
			
		||||
                        groupTimer = setTimeout(function() {
 | 
			
		||||
                            var mx = mouseX / RED.view.scale();
 | 
			
		||||
                            var my = mouseY / RED.view.scale();
 | 
			
		||||
                            var group = RED.view.getGroupAtPoint(mx,my);
 | 
			
		||||
                            if (group !== hoverGroup) {
 | 
			
		||||
                                if (hoverGroup) {
 | 
			
		||||
                                    document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
 | 
			
		||||
                                }
 | 
			
		||||
                                if (group) {
 | 
			
		||||
                                    document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
 | 
			
		||||
                                }
 | 
			
		||||
                                hoverGroup = group;
 | 
			
		||||
                                if (hoverGroup) {
 | 
			
		||||
                                    $(ui.helper).data('group',hoverGroup);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    $(ui.helper).removeData('group');
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            groupTimer = null;
 | 
			
		||||
 | 
			
		||||
                        },200)
 | 
			
		||||
                    }
 | 
			
		||||
                    if (def.inputs > 0 && def.outputs > 0) {
 | 
			
		||||
                        if (!spliceTimer) {
 | 
			
		||||
                            spliceTimer = setTimeout(function() {
 | 
			
		||||
                                var nodes = [];
 | 
			
		||||
                                var bestDistance = Infinity;
 | 
			
		||||
                                var bestLink = null;
 | 
			
		||||
                                if (chartSVG.getIntersectionList) {
 | 
			
		||||
                                    var svgRect = chartSVG.createSVGRect();
 | 
			
		||||
                                    svgRect.x = mouseX;
 | 
			
		||||
                                    svgRect.y = mouseY;
 | 
			
		||||
                                    svgRect.width = 1;
 | 
			
		||||
                                    svgRect.height = 1;
 | 
			
		||||
                                    nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    // Firefox doesn't do getIntersectionList and that
 | 
			
		||||
                                    // makes us sad
 | 
			
		||||
                                    nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
 | 
			
		||||
                                }
 | 
			
		||||
                    if (dropEnabled) {
 | 
			
		||||
                        mouseX = ui.position.left - paletteWidth + (ui.helper.width()/2) + chart.scrollLeft();
 | 
			
		||||
                        mouseY = ui.position.top - paletteTop + (ui.helper.height()/2) + chart.scrollTop() + 10;
 | 
			
		||||
                        if (!groupTimer) {
 | 
			
		||||
                            groupTimer = setTimeout(function() {
 | 
			
		||||
                                var mx = mouseX / RED.view.scale();
 | 
			
		||||
                                var my = mouseY / RED.view.scale();
 | 
			
		||||
                                for (var i=0;i<nodes.length;i++) {
 | 
			
		||||
                                    var node = d3.select(nodes[i]);
 | 
			
		||||
                                    if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
 | 
			
		||||
                                        var length = nodes[i].getTotalLength();
 | 
			
		||||
                                        for (var j=0;j<length;j+=10) {
 | 
			
		||||
                                            var p = nodes[i].getPointAtLength(j);
 | 
			
		||||
                                            var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
 | 
			
		||||
                                            if (d2 < 200 && d2 < bestDistance) {
 | 
			
		||||
                                                bestDistance = d2;
 | 
			
		||||
                                                bestLink = nodes[i];
 | 
			
		||||
                                var group = RED.view.getGroupAtPoint(mx,my);
 | 
			
		||||
                                if (group !== hoverGroup) {
 | 
			
		||||
                                    if (hoverGroup) {
 | 
			
		||||
                                        document.getElementById("group_select_"+hoverGroup.id).classList.remove("red-ui-flow-group-hovered");
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if (group) {
 | 
			
		||||
                                        document.getElementById("group_select_"+group.id).classList.add("red-ui-flow-group-hovered");
 | 
			
		||||
                                    }
 | 
			
		||||
                                    hoverGroup = group;
 | 
			
		||||
                                    if (hoverGroup) {
 | 
			
		||||
                                        $(ui.helper).data('group',hoverGroup);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        $(ui.helper).removeData('group');
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                groupTimer = null;
 | 
			
		||||
 | 
			
		||||
                            },200)
 | 
			
		||||
                        }
 | 
			
		||||
                        if (def.inputs > 0 && def.outputs > 0) {
 | 
			
		||||
                            if (!spliceTimer) {
 | 
			
		||||
                                spliceTimer = setTimeout(function() {
 | 
			
		||||
                                    var nodes = [];
 | 
			
		||||
                                    var bestDistance = Infinity;
 | 
			
		||||
                                    var bestLink = null;
 | 
			
		||||
                                    if (chartSVG.getIntersectionList) {
 | 
			
		||||
                                        var svgRect = chartSVG.createSVGRect();
 | 
			
		||||
                                        svgRect.x = mouseX;
 | 
			
		||||
                                        svgRect.y = mouseY;
 | 
			
		||||
                                        svgRect.width = 1;
 | 
			
		||||
                                        svgRect.height = 1;
 | 
			
		||||
                                        nodes = chartSVG.getIntersectionList(svgRect,chartSVG);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        // Firefox doesn't do getIntersectionList and that
 | 
			
		||||
                                        // makes us sad
 | 
			
		||||
                                        nodes = RED.view.getLinksAtPoint(mouseX,mouseY);
 | 
			
		||||
                                    }
 | 
			
		||||
                                    var mx = mouseX / RED.view.scale();
 | 
			
		||||
                                    var my = mouseY / RED.view.scale();
 | 
			
		||||
                                    for (var i=0;i<nodes.length;i++) {
 | 
			
		||||
                                        var node = d3.select(nodes[i]);
 | 
			
		||||
                                        if (node.classed('red-ui-flow-link-background') && !node.classed('red-ui-flow-link-link')) {
 | 
			
		||||
                                            var length = nodes[i].getTotalLength();
 | 
			
		||||
                                            for (var j=0;j<length;j+=10) {
 | 
			
		||||
                                                var p = nodes[i].getPointAtLength(j);
 | 
			
		||||
                                                var d2 = ((p.x-mx)*(p.x-mx))+((p.y-my)*(p.y-my));
 | 
			
		||||
                                                if (d2 < 200 && d2 < bestDistance) {
 | 
			
		||||
                                                    bestDistance = d2;
 | 
			
		||||
                                                    bestLink = nodes[i];
 | 
			
		||||
                                                }
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                if (activeSpliceLink && activeSpliceLink !== bestLink) {
 | 
			
		||||
                                    d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                                }
 | 
			
		||||
                                if (bestLink) {
 | 
			
		||||
                                    d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                                }
 | 
			
		||||
                                if (activeSpliceLink !== bestLink) {
 | 
			
		||||
                                    if (bestLink) {
 | 
			
		||||
                                        $(ui.helper).data('splice',d3.select(bestLink).data()[0]);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        $(ui.helper).removeData('splice');
 | 
			
		||||
                                    if (activeSpliceLink && activeSpliceLink !== bestLink) {
 | 
			
		||||
                                        d3.select(activeSpliceLink.parentNode).classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                activeSpliceLink = bestLink;
 | 
			
		||||
                                spliceTimer = null;
 | 
			
		||||
                            },200);
 | 
			
		||||
                                    if (bestLink) {
 | 
			
		||||
                                        d3.select(bestLink.parentNode).classed('red-ui-flow-link-splice',true)
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        d3.select('.red-ui-flow-link-splice').classed('red-ui-flow-link-splice',false);
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if (activeSpliceLink !== bestLink) {
 | 
			
		||||
                                        if (bestLink) {
 | 
			
		||||
                                            $(ui.helper).data('splice',d3.select(bestLink).data()[0]);
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            $(ui.helper).removeData('splice');
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    activeSpliceLink = bestLink;
 | 
			
		||||
                                    spliceTimer = null;
 | 
			
		||||
                                },200);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -273,6 +273,11 @@ RED.subflow = (function() {
 | 
			
		||||
        var subflowInstances = [];
 | 
			
		||||
        if (activeSubflow) {
 | 
			
		||||
            RED.nodes.filterNodes({type:"subflow:"+activeSubflow.id}).forEach(function(n) {
 | 
			
		||||
                const parentFlow = RED.nodes.workspace(n.z)
 | 
			
		||||
                const wasLocked = parentFlow && parentFlow.locked
 | 
			
		||||
                if (wasLocked) {
 | 
			
		||||
                    parentFlow.locked = false
 | 
			
		||||
                }
 | 
			
		||||
                subflowInstances.push({
 | 
			
		||||
                    id: n.id,
 | 
			
		||||
                    changed: n.changed
 | 
			
		||||
@@ -285,6 +290,9 @@ RED.subflow = (function() {
 | 
			
		||||
                n.resize = true;
 | 
			
		||||
                n.dirty = true;
 | 
			
		||||
                RED.editor.updateNodeProperties(n);
 | 
			
		||||
                if (wasLocked) {
 | 
			
		||||
                    parentFlow.locked = true
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            RED.editor.validateNode(activeSubflow);
 | 
			
		||||
            return {
 | 
			
		||||
@@ -450,6 +458,9 @@ RED.subflow = (function() {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (subflow.instances.length > 0) {
 | 
			
		||||
            if (subflow.instances.some(sf => { const ws = RED.nodes.workspace(sf.z); return ws?ws.locked:false })) {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            const msg = $('<div>')
 | 
			
		||||
            $('<p>').text(RED._("subflow.subflowInstances",{count: subflow.instances.length})).appendTo(msg);
 | 
			
		||||
            $('<p>').text(RED._("subflow.confirmDelete")).appendTo(msg);
 | 
			
		||||
@@ -554,7 +565,7 @@ RED.subflow = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        RED.events.on("view:selection-changed",function(selection) {
 | 
			
		||||
            if (!selection.nodes) {
 | 
			
		||||
            if (!selection.nodes || RED.workspaces.isActiveLocked()) {
 | 
			
		||||
                RED.menu.setDisabled("menu-item-subflow-convert",true);
 | 
			
		||||
            } else {
 | 
			
		||||
                RED.menu.setDisabled("menu-item-subflow-convert",false);
 | 
			
		||||
@@ -617,6 +628,9 @@ RED.subflow = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function convertToSubflow() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (!selection.nodes) {
 | 
			
		||||
            RED.notify(RED._("subflow.errors.noNodesSelected"),"error");
 | 
			
		||||
@@ -772,7 +786,7 @@ RED.subflow = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        subflowInstance._def = RED.nodes.getType(subflowInstance.type);
 | 
			
		||||
        RED.editor.validateNode(subflowInstance);
 | 
			
		||||
        RED.nodes.add(subflowInstance);
 | 
			
		||||
        subflowInstance = RED.nodes.add(subflowInstance);
 | 
			
		||||
 | 
			
		||||
        if (containingGroup) {
 | 
			
		||||
            RED.group.addToGroup(containingGroup, subflowInstance);
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,15 @@ RED.sidebar.config = (function() {
 | 
			
		||||
 | 
			
		||||
    var categories = {};
 | 
			
		||||
 | 
			
		||||
    function getOrCreateCategory(name,parent,label) {
 | 
			
		||||
    function getOrCreateCategory(name,parent,label,isLocked) {
 | 
			
		||||
        name = name.replace(/\./i,"-");
 | 
			
		||||
        if (!categories[name]) {
 | 
			
		||||
            var container = $('<div class="red-ui-palette-category red-ui-sidebar-config-category" id="red-ui-sidebar-config-category-'+name+'"></div>').appendTo(parent);
 | 
			
		||||
            var header = $('<div class="red-ui-sidebar-config-tray-header red-ui-palette-header"><i class="fa fa-angle-down expanded"></i></div>').appendTo(container);
 | 
			
		||||
            let lockIcon
 | 
			
		||||
            if (label) {
 | 
			
		||||
                lockIcon = $('<span style="margin-right: 5px"><i class="fa fa-lock"/></span>').appendTo(header)
 | 
			
		||||
                lockIcon.toggle(!!isLocked)
 | 
			
		||||
                $('<span class="red-ui-palette-node-config-label"/>').text(label).appendTo(header);
 | 
			
		||||
            } else {
 | 
			
		||||
                $('<span class="red-ui-palette-node-config-label" data-i18n="sidebar.config.'+name+'">').appendTo(header);
 | 
			
		||||
@@ -62,6 +65,7 @@ RED.sidebar.config = (function() {
 | 
			
		||||
            var icon = header.find("i");
 | 
			
		||||
            var result = {
 | 
			
		||||
                label: label,
 | 
			
		||||
                lockIcon,
 | 
			
		||||
                list: category,
 | 
			
		||||
                size: function() {
 | 
			
		||||
                    return result.list.find("li:not(.red-ui-palette-node-config-none)").length
 | 
			
		||||
@@ -100,6 +104,9 @@ RED.sidebar.config = (function() {
 | 
			
		||||
            });
 | 
			
		||||
            categories[name] = result;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (isLocked !== undefined && categories[name].lockIcon) {
 | 
			
		||||
                categories[name].lockIcon.toggle(!!isLocked)
 | 
			
		||||
            }
 | 
			
		||||
            if (categories[name].label !== label) {
 | 
			
		||||
                categories[name].list.parent().find('.red-ui-palette-node-config-label').text(label);
 | 
			
		||||
                categories[name].label = label;
 | 
			
		||||
@@ -216,7 +223,7 @@ RED.sidebar.config = (function() {
 | 
			
		||||
 | 
			
		||||
        RED.nodes.eachWorkspace(function(ws) {
 | 
			
		||||
            validList[ws.id.replace(/\./g,"-")] = true;
 | 
			
		||||
            getOrCreateCategory(ws.id,flowCategories,ws.label);
 | 
			
		||||
            getOrCreateCategory(ws.id,flowCategories,ws.label, ws.locked);
 | 
			
		||||
        })
 | 
			
		||||
        RED.nodes.eachSubflow(function(sf) {
 | 
			
		||||
            validList[sf.id.replace(/\./g,"-")] = true;
 | 
			
		||||
@@ -274,6 +281,15 @@ RED.sidebar.config = (function() {
 | 
			
		||||
                    changes: {},
 | 
			
		||||
                    dirty: RED.nodes.dirty()
 | 
			
		||||
                }
 | 
			
		||||
                for (let i = 0; i < selectedNodes.length; i++) {
 | 
			
		||||
                    let node = RED.nodes.node(selectedNodes[i])
 | 
			
		||||
                    if (node.z) {
 | 
			
		||||
                        let ws = RED.nodes.workspace(node.z)
 | 
			
		||||
                        if (ws && ws.locked) {
 | 
			
		||||
                            return
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                selectedNodes.forEach(function(id) {
 | 
			
		||||
                    var node = RED.nodes.node(id);
 | 
			
		||||
                    try {
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,8 @@ RED.sidebar.help = (function() {
 | 
			
		||||
        RED.events.on('registry:node-type-removed', queueRefresh);
 | 
			
		||||
        RED.events.on('subflows:change', refreshSubflow);
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:show-help-tab",show);
 | 
			
		||||
        RED.actions.add("core:show-help-tab", show);
 | 
			
		||||
        RED.actions.add("core:show-node-help", showNodeHelp)
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -338,6 +339,19 @@ RED.sidebar.help = (function() {
 | 
			
		||||
        resizeStack();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showNodeHelp(node) {
 | 
			
		||||
        if (!node) {
 | 
			
		||||
            const selection = RED.view.selection()
 | 
			
		||||
            if (selection.nodes && selection.nodes.length > 0) {
 | 
			
		||||
                node = selection.nodes.find(n => n.type !== 'group' && n.type !== 'junction')
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (node) {
 | 
			
		||||
            show(node.type, true)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // TODO: DRY - projects.js
 | 
			
		||||
    function addTargetToExternalLinks(el) {
 | 
			
		||||
        $(el).find("a").each(function(el) {
 | 
			
		||||
 
 | 
			
		||||
@@ -221,6 +221,22 @@ RED.sidebar.info.outliner = (function() {
 | 
			
		||||
        } else {
 | 
			
		||||
            $('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
 | 
			
		||||
        }
 | 
			
		||||
        if (n.type === 'tab') {
 | 
			
		||||
            var lockToggleButton = $('<button type="button" class="red-ui-info-outline-item-control-lock red-ui-button red-ui-button-small"><i class="fa fa-unlock-alt"></i><i class="fa fa-lock"></i></button>').appendTo(controls).on("click",function(evt) {
 | 
			
		||||
                evt.preventDefault();
 | 
			
		||||
                evt.stopPropagation();
 | 
			
		||||
                if (n.locked) {
 | 
			
		||||
                    RED.workspaces.unlock(n.id)
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.workspaces.lock(n.id)
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            RED.popover.tooltip(lockToggleButton,function() {
 | 
			
		||||
                return RED._("common.label."+(n.locked?"unlock":"lock"));
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            $('<div class="red-ui-info-outline-item-control-spacer">').appendTo(controls)
 | 
			
		||||
        }
 | 
			
		||||
        controls.find("button").on("dblclick", function(evt) {
 | 
			
		||||
            evt.preventDefault();
 | 
			
		||||
            evt.stopPropagation();
 | 
			
		||||
@@ -364,6 +380,8 @@ RED.sidebar.info.outliner = (function() {
 | 
			
		||||
        flowList.treeList.addChild(objects[ws.id])
 | 
			
		||||
        objects[ws.id].element.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
 | 
			
		||||
        objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!ws.disabled)
 | 
			
		||||
        objects[ws.id].element.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
 | 
			
		||||
        objects[ws.id].treeList.container.toggleClass("red-ui-info-outline-item-locked", !!ws.locked)
 | 
			
		||||
        updateSearch();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@@ -378,6 +396,8 @@ RED.sidebar.info.outliner = (function() {
 | 
			
		||||
        existingObject.element.find(".red-ui-info-outline-item-label").text(label);
 | 
			
		||||
        existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
 | 
			
		||||
        existingObject.treeList.container.toggleClass("red-ui-info-outline-item-disabled", !!n.disabled)
 | 
			
		||||
        existingObject.element.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
 | 
			
		||||
        existingObject.treeList.container.toggleClass("red-ui-info-outline-item-locked", !!n.locked)
 | 
			
		||||
        updateSearch();
 | 
			
		||||
    }
 | 
			
		||||
    function onFlowsReorder(order) {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
RED.view.tools = (function() {
 | 
			
		||||
 | 
			
		||||
    'use strict';
 | 
			
		||||
    function selectConnected(type) {
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        var visited = new Set();
 | 
			
		||||
@@ -39,6 +39,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function alignToGrid() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            var changedNodes = [];
 | 
			
		||||
@@ -87,6 +90,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function moveSelection(dx,dy) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (moving_set === null) {
 | 
			
		||||
            moving_set = [];
 | 
			
		||||
            var selection = RED.view.selection();
 | 
			
		||||
@@ -153,6 +159,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function setSelectedNodeLabelState(labelShown) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        var historyEvents = [];
 | 
			
		||||
        var nodes = [];
 | 
			
		||||
@@ -439,6 +448,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function alignSelectionToEdge(direction) {
 | 
			
		||||
        // if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
        //     return
 | 
			
		||||
        // }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
 | 
			
		||||
        if (selection.nodes && selection.nodes.length > 1) {
 | 
			
		||||
@@ -539,8 +551,10 @@ RED.view.tools = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function distributeSelection(direction) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
 | 
			
		||||
        if (selection.nodes && selection.nodes.length > 2) {
 | 
			
		||||
@@ -699,6 +713,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function reorderSelection(dir) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            var nodesToMove = [];
 | 
			
		||||
@@ -734,8 +751,10 @@ RED.view.tools = (function() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function wireSeriesOfNodes() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            if (selection.nodes.length > 1) {
 | 
			
		||||
@@ -776,6 +795,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function wireNodeToMultiple() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            if (selection.nodes.length > 1) {
 | 
			
		||||
@@ -823,6 +845,9 @@ RED.view.tools = (function() {
 | 
			
		||||
     * @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
 | 
			
		||||
     */
 | 
			
		||||
    function splitWiresWithLinkNodes(wires) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
 | 
			
		||||
        if (!wiresToSplit) {
 | 
			
		||||
            return
 | 
			
		||||
@@ -877,7 +902,6 @@ RED.view.tools = (function() {
 | 
			
		||||
            if(!nnLinkOut) {
 | 
			
		||||
                const nLinkOut = RED.view.createNode("link out"); //create link node
 | 
			
		||||
                nnLinkOut = nLinkOut.node;
 | 
			
		||||
                nodeSrcMap[linkOutMapId] = nnLinkOut;
 | 
			
		||||
                let yOffset = 0;
 | 
			
		||||
                if(nSrc.outputs > 1) {
 | 
			
		||||
 | 
			
		||||
@@ -892,7 +916,8 @@ RED.view.tools = (function() {
 | 
			
		||||
                    updateNewNodePosXY(nSrc, nnLinkOut, false, RED.view.snapGrid, yOffset);
 | 
			
		||||
                }
 | 
			
		||||
                //add created node
 | 
			
		||||
                RED.nodes.add(nnLinkOut);
 | 
			
		||||
                nnLinkOut = RED.nodes.add(nnLinkOut);
 | 
			
		||||
                nodeSrcMap[linkOutMapId] = nnLinkOut;
 | 
			
		||||
                RED.editor.validateNode(nnLinkOut);
 | 
			
		||||
                history.events.push(nLinkOut.historyEvent);
 | 
			
		||||
                //connect node to link node
 | 
			
		||||
@@ -913,10 +938,10 @@ RED.view.tools = (function() {
 | 
			
		||||
            if(!nnLinkIn) {
 | 
			
		||||
                const nLinkIn = RED.view.createNode("link in"); //create link node
 | 
			
		||||
                nnLinkIn = nLinkIn.node;
 | 
			
		||||
                nodeTrgMap[nTrg.id] = nnLinkIn;
 | 
			
		||||
                updateNewNodePosXY(nTrg, nnLinkIn, true, RED.view.snapGrid, 0);
 | 
			
		||||
                //add created node
 | 
			
		||||
                RED.nodes.add(nnLinkIn);
 | 
			
		||||
                nnLinkIn = RED.nodes.add(nnLinkIn);
 | 
			
		||||
                nodeTrgMap[nTrg.id] = nnLinkIn;
 | 
			
		||||
                RED.editor.validateNode(nnLinkIn);
 | 
			
		||||
                history.events.push(nLinkIn.historyEvent);
 | 
			
		||||
                //connect node to link node
 | 
			
		||||
@@ -991,6 +1016,9 @@ RED.view.tools = (function() {
 | 
			
		||||
     * @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory`
 | 
			
		||||
     */
 | 
			
		||||
    function generateNodeNames(node, options) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        options = Object.assign({
 | 
			
		||||
            renameBlank: true,
 | 
			
		||||
            renameClash: true,
 | 
			
		||||
@@ -1061,6 +1089,9 @@ RED.view.tools = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function addJunctionsToWires(wires) {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link));
 | 
			
		||||
        if (!wiresToSplit) {
 | 
			
		||||
            return
 | 
			
		||||
@@ -1131,7 +1162,7 @@ RED.view.tools = (function() {
 | 
			
		||||
 | 
			
		||||
            var nodeGroups = new Set()
 | 
			
		||||
 | 
			
		||||
            RED.nodes.addJunction(junction)
 | 
			
		||||
            junction = RED.nodes.addJunction(junction)
 | 
			
		||||
            addedJunctions.push(junction)
 | 
			
		||||
            let newLink
 | 
			
		||||
            if (gid === links[0].source.id+":"+links[0].sourcePort) {
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ RED.view = (function() {
 | 
			
		||||
    var spliceTimer;
 | 
			
		||||
    var groupHoverTimer;
 | 
			
		||||
 | 
			
		||||
    var activeFlowLocked = false;
 | 
			
		||||
    var activeSubflow = null;
 | 
			
		||||
    var activeNodes = [];
 | 
			
		||||
    var activeLinks = [];
 | 
			
		||||
@@ -411,8 +412,19 @@ RED.view = (function() {
 | 
			
		||||
 | 
			
		||||
            activeSubflow = RED.nodes.subflow(event.workspace);
 | 
			
		||||
 | 
			
		||||
            RED.menu.setDisabled("menu-item-workspace-edit", activeSubflow || event.workspace === 0);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-workspace-delete",event.workspace === 0 || RED.workspaces.count() == 1 || activeSubflow);
 | 
			
		||||
            if (activeSubflow) {
 | 
			
		||||
                activeFlowLocked = activeSubflow.locked
 | 
			
		||||
            } else {
 | 
			
		||||
                var activeWorkspace = RED.nodes.workspace(event.workspace)
 | 
			
		||||
                if (activeWorkspace) {
 | 
			
		||||
                    activeFlowLocked = activeWorkspace.locked
 | 
			
		||||
                } else {
 | 
			
		||||
                    activeFlowLocked = true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            RED.menu.setDisabled("menu-item-workspace-edit", activeFlowLocked || activeSubflow || event.workspace === 0);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-workspace-delete",activeFlowLocked || event.workspace === 0 || RED.workspaces.count() == 1 || activeSubflow);
 | 
			
		||||
 | 
			
		||||
            if (workspaceScrollPositions[event.workspace]) {
 | 
			
		||||
                chart.scrollLeft(workspaceScrollPositions[event.workspace].left);
 | 
			
		||||
@@ -439,6 +451,15 @@ RED.view = (function() {
 | 
			
		||||
            redraw();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        RED.events.on("flows:change", function(workspace) {
 | 
			
		||||
            if (workspace.id === RED.workspaces.active()) {
 | 
			
		||||
                activeFlowLocked = !!workspace.locked
 | 
			
		||||
                $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!workspace.disabled);
 | 
			
		||||
                $("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        RED.statusBar.add({
 | 
			
		||||
            id: "view-zoom-controls",
 | 
			
		||||
            align: "right",
 | 
			
		||||
@@ -496,6 +517,9 @@ RED.view = (function() {
 | 
			
		||||
        chart.droppable({
 | 
			
		||||
            accept:".red-ui-palette-node",
 | 
			
		||||
            drop: function( event, ui ) {
 | 
			
		||||
                if (activeFlowLocked) {
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
                d3.event = event;
 | 
			
		||||
                var selected_tool = $(ui.draggable[0]).attr("data-palette-type");
 | 
			
		||||
                var result = createNode(selected_tool);
 | 
			
		||||
@@ -503,9 +527,7 @@ RED.view = (function() {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                var historyEvent = result.historyEvent;
 | 
			
		||||
                var nn = result.node;
 | 
			
		||||
 | 
			
		||||
                RED.nodes.add(nn);
 | 
			
		||||
                var nn = RED.nodes.add(result.node);
 | 
			
		||||
 | 
			
		||||
                var showLabel = RED.utils.getMessageProperty(RED.settings.get('editor'),"view.view-node-show-label");
 | 
			
		||||
                if (showLabel !== undefined &&  (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
 | 
			
		||||
@@ -632,6 +654,9 @@ RED.view = (function() {
 | 
			
		||||
        RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
 | 
			
		||||
        RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection(true);deleteSelection();});
 | 
			
		||||
        RED.actions.add("core:paste-from-internal-clipboard",function(){
 | 
			
		||||
            if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'});
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -640,22 +665,27 @@ RED.view = (function() {
 | 
			
		||||
        RED.events.on("view:selection-changed", function(selection) {
 | 
			
		||||
            var hasSelection = (selection.nodes && selection.nodes.length > 0);
 | 
			
		||||
            var hasMultipleSelection = hasSelection && selection.nodes.length > 1;
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-cut",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-copy",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-select-connected",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-to-back",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-to-front",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-backwards",!hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-forwards",!hasSelection);
 | 
			
		||||
            var hasLinkSelected = selection.links && selection.links.length > 0;
 | 
			
		||||
            var canEdit = !activeFlowLocked && hasSelection
 | 
			
		||||
            var canEditMultiple = !activeFlowLocked && hasMultipleSelection
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-cut", !canEdit);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-copy", !hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-select-connected", !hasSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-to-back", !canEdit);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-to-front", !canEdit);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-backwards", !canEdit);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-move-forwards", !canEdit);
 | 
			
		||||
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-left",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-center",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-right",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-top",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-middle",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-bottom",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally",!hasMultipleSelection);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-left", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-center", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-right", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-top", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-middle", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-align-bottom", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-distribute-horizontally", !canEditMultiple);
 | 
			
		||||
            RED.menu.setDisabled("menu-item-view-tools-distribute-veritcally", !canEditMultiple);
 | 
			
		||||
 | 
			
		||||
            RED.menu.setDisabled("menu-item-edit-split-wire-with-links", activeFlowLocked || !hasLinkSelected);
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        RED.actions.add("core:delete-selection",deleteSelection);
 | 
			
		||||
@@ -1045,7 +1075,7 @@ RED.view = (function() {
 | 
			
		||||
                            .attr("class", "nr-ui-view-lasso");
 | 
			
		||||
                        d3.event.preventDefault();
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (d3.event.altKey) {
 | 
			
		||||
                } else if (d3.event.altKey && !activeFlowLocked) {
 | 
			
		||||
                    //Alt [+shift] held - Begin slicing
 | 
			
		||||
                    clearSelection();
 | 
			
		||||
                    mouse_mode = (d3.event.shiftKey) ? RED.state.SLICING_JUNCTION : RED.state.SLICING;
 | 
			
		||||
@@ -1059,6 +1089,9 @@ RED.view = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function showQuickAddDialog(options) {
 | 
			
		||||
        if (activeFlowLocked) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        options = options || {};
 | 
			
		||||
        var point = options.position || lastClickPosition;
 | 
			
		||||
        var spliceLink = options.splice;
 | 
			
		||||
@@ -1238,6 +1271,11 @@ RED.view = (function() {
 | 
			
		||||
                if (showLabel !== undefined && (nn._def.hasOwnProperty("showLabel")?nn._def.showLabel:true) && !nn._def.defaults.hasOwnProperty("l")) {
 | 
			
		||||
                    nn.l = showLabel;
 | 
			
		||||
                }
 | 
			
		||||
                if (nn.type === 'junction') {
 | 
			
		||||
                    nn = RED.nodes.addJunction(nn);
 | 
			
		||||
                } else {
 | 
			
		||||
                    nn = RED.nodes.add(nn);
 | 
			
		||||
                }
 | 
			
		||||
                if (quickAddLink) {
 | 
			
		||||
                    var drag_line = quickAddLink;
 | 
			
		||||
                    var src = null,dst,src_port;
 | 
			
		||||
@@ -1340,11 +1378,7 @@ RED.view = (function() {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (nn.type === 'junction') {
 | 
			
		||||
                    RED.nodes.addJunction(nn);
 | 
			
		||||
                } else {
 | 
			
		||||
                    RED.nodes.add(nn);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                RED.editor.validateNode(nn);
 | 
			
		||||
 | 
			
		||||
                if (targetGroup) {
 | 
			
		||||
@@ -1602,16 +1636,18 @@ RED.view = (function() {
 | 
			
		||||
            }
 | 
			
		||||
            var d = (mouse_offset[0]-mousePos[0])*(mouse_offset[0]-mousePos[0]) + (mouse_offset[1]-mousePos[1])*(mouse_offset[1]-mousePos[1]);
 | 
			
		||||
            if ((d > 3 && !dblClickPrimed) || (dblClickPrimed && d > 10)) {
 | 
			
		||||
                mouse_mode = RED.state.MOVING_ACTIVE;
 | 
			
		||||
                clickElapsed = 0;
 | 
			
		||||
                spliceActive = false;
 | 
			
		||||
                if (movingSet.length() === 1) {
 | 
			
		||||
                    node = movingSet.get(0);
 | 
			
		||||
                    spliceActive = node.n.hasOwnProperty("_def") &&
 | 
			
		||||
                                   ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) &&
 | 
			
		||||
                                   ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) &&
 | 
			
		||||
                                   RED.nodes.filterLinks({ source: node.n }).length === 0 &&
 | 
			
		||||
                                   RED.nodes.filterLinks({ target: node.n }).length === 0;
 | 
			
		||||
                if (!activeFlowLocked) {
 | 
			
		||||
                    mouse_mode = RED.state.MOVING_ACTIVE;
 | 
			
		||||
                    spliceActive = false;
 | 
			
		||||
                    if (movingSet.length() === 1) {
 | 
			
		||||
                        node = movingSet.get(0);
 | 
			
		||||
                        spliceActive = node.n.hasOwnProperty("_def") &&
 | 
			
		||||
                                       ((node.n.hasOwnProperty("inputs") && node.n.inputs > 0) || (!node.n.hasOwnProperty("inputs") && node.n._def.inputs > 0)) &&
 | 
			
		||||
                                       ((node.n.hasOwnProperty("outputs") && node.n.outputs > 0) || (!node.n.hasOwnProperty("outputs") && node.n._def.outputs > 0)) &&
 | 
			
		||||
                                       RED.nodes.filterLinks({ source: node.n }).length === 0 &&
 | 
			
		||||
                                       RED.nodes.filterLinks({ target: node.n }).length === 0;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (mouse_mode == RED.state.MOVING_ACTIVE || mouse_mode == RED.state.IMPORT_DRAGGING || mouse_mode == RED.state.DETACHED_DRAGGING) {
 | 
			
		||||
@@ -2456,6 +2492,7 @@ RED.view = (function() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function editSelection() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (movingSet.length() > 0) {
 | 
			
		||||
            var node = movingSet.get(0).n;
 | 
			
		||||
            if (node.type === "subflow") {
 | 
			
		||||
@@ -2471,6 +2508,9 @@ RED.view = (function() {
 | 
			
		||||
        if (mouse_mode === RED.state.SELECTING_NODE) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (activeFlowLocked) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        if (portLabelHover) {
 | 
			
		||||
            portLabelHover.remove();
 | 
			
		||||
            portLabelHover = null;
 | 
			
		||||
@@ -2786,6 +2826,7 @@ RED.view = (function() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function detachSelectedNodes() {
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        var selection = RED.view.selection();
 | 
			
		||||
        if (selection.nodes) {
 | 
			
		||||
            const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes);
 | 
			
		||||
@@ -2927,7 +2968,7 @@ RED.view = (function() {
 | 
			
		||||
        mousedown_node = d;
 | 
			
		||||
        mousedown_port_type = portType;
 | 
			
		||||
        mousedown_port_index = portIndex || 0;
 | 
			
		||||
        if (mouse_mode !== RED.state.QUICK_JOINING) {
 | 
			
		||||
        if (mouse_mode !== RED.state.QUICK_JOINING && !activeFlowLocked) {
 | 
			
		||||
            mouse_mode = RED.state.JOINING;
 | 
			
		||||
            document.body.style.cursor = "crosshair";
 | 
			
		||||
            if (evt.ctrlKey || evt.metaKey) {
 | 
			
		||||
@@ -3367,6 +3408,11 @@ RED.view = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < dblClickInterval) {
 | 
			
		||||
            mouse_mode = RED.state.DEFAULT;
 | 
			
		||||
            if (RED.workspaces.isActiveLocked()) {
 | 
			
		||||
                clickElapsed = 0;
 | 
			
		||||
                d3.event.stopPropagation();
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
            if (d.type != "subflow") {
 | 
			
		||||
                if (/^subflow:/.test(d.type) && (d3.event.ctrlKey || d3.event.metaKey)) {
 | 
			
		||||
                    RED.workspaces.show(d.type.substring(8));
 | 
			
		||||
@@ -3690,7 +3736,6 @@ RED.view = (function() {
 | 
			
		||||
            }
 | 
			
		||||
            // selectedLinks.clear();
 | 
			
		||||
            if (d3.event.button != 2) {
 | 
			
		||||
                mouse_mode = RED.state.MOVING;
 | 
			
		||||
                var mouse = d3.touches(this)[0]||d3.mouse(this);
 | 
			
		||||
                mouse[0] += d.x-d.w/2;
 | 
			
		||||
                mouse[1] += d.y-d.h/2;
 | 
			
		||||
@@ -3883,6 +3928,7 @@ RED.view = (function() {
 | 
			
		||||
        if (RED.view.DEBUG) {
 | 
			
		||||
            console.warn("groupMouseUp", { mouse_mode, event: d3.event });
 | 
			
		||||
        }
 | 
			
		||||
        if (RED.workspaces.isActiveLocked()) { return }
 | 
			
		||||
        if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) {
 | 
			
		||||
            mouse_mode = RED.state.DEFAULT;
 | 
			
		||||
            RED.editor.editGroup(g);
 | 
			
		||||
@@ -4053,7 +4099,7 @@ RED.view = (function() {
 | 
			
		||||
    function isButtonEnabled(d) {
 | 
			
		||||
        var buttonEnabled = true;
 | 
			
		||||
        var ws = RED.nodes.workspace(RED.workspaces.active());
 | 
			
		||||
        if (ws && !ws.disabled && !d.d) {
 | 
			
		||||
        if (ws && !ws.disabled && !d.d && !ws.locked) {
 | 
			
		||||
            if (d._def.button.hasOwnProperty('enabled')) {
 | 
			
		||||
                if (typeof d._def.button.enabled === "function") {
 | 
			
		||||
                    buttonEnabled = d._def.button.enabled.call(d);
 | 
			
		||||
@@ -4076,7 +4122,7 @@ RED.view = (function() {
 | 
			
		||||
        }
 | 
			
		||||
        var activeWorkspace = RED.workspaces.active();
 | 
			
		||||
        var ws = RED.nodes.workspace(activeWorkspace);
 | 
			
		||||
        if (ws && !ws.disabled && !d.d) {
 | 
			
		||||
        if (ws && !ws.disabled && !d.d && !ws.locked) {
 | 
			
		||||
            if (d._def.button.toggle) {
 | 
			
		||||
                d[d._def.button.toggle] = !d[d._def.button.toggle];
 | 
			
		||||
                d.dirty = true;
 | 
			
		||||
@@ -4091,7 +4137,7 @@ RED.view = (function() {
 | 
			
		||||
            if (d.dirty) {
 | 
			
		||||
                redraw();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (!ws || !ws.locked){
 | 
			
		||||
            if (activeSubflow) {
 | 
			
		||||
                RED.notify(RED._("notification.warning", {message:RED._("notification.warnings.nodeActionDisabledSubflow")}),"warning");
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -4106,14 +4152,15 @@ RED.view = (function() {
 | 
			
		||||
    function showTouchMenu(obj,pos) {
 | 
			
		||||
        var mdn = mousedown_node;
 | 
			
		||||
        var options = [];
 | 
			
		||||
        options.push({name:"delete",disabled:(movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}});
 | 
			
		||||
        options.push({name:"cut",disabled:(movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}});
 | 
			
		||||
        options.push({name:"copy",disabled:(movingSet.length()===0),onselect:function() {copySelection();}});
 | 
			
		||||
        options.push({name:"paste",disabled:(clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}});
 | 
			
		||||
        options.push({name:"edit",disabled:(movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}});
 | 
			
		||||
        const isActiveLocked = RED.workspaces.isActiveLocked()
 | 
			
		||||
        options.push({name:"delete",disabled:(isActiveLocked || movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}});
 | 
			
		||||
        options.push({name:"cut",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}});
 | 
			
		||||
        options.push({name:"copy",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection();}});
 | 
			
		||||
        options.push({name:"paste",disabled:(isActiveLocked || clipboard.length===0),onselect:function() {importNodes(clipboard, {generateIds: true, touchImport: true});}});
 | 
			
		||||
        options.push({name:"edit",disabled:(isActiveLocked || movingSet.length() != 1),onselect:function() { RED.editor.edit(mdn);}});
 | 
			
		||||
        options.push({name:"select",onselect:function() {selectAll();}});
 | 
			
		||||
        options.push({name:"undo",disabled:(RED.history.depth() === 0),onselect:function() {RED.history.pop();}});
 | 
			
		||||
        options.push({name:"add",onselect:function() {
 | 
			
		||||
        options.push({name:"add",disabled:isActiveLocked, onselect:function() {
 | 
			
		||||
            chartPos = chart.offset();
 | 
			
		||||
            showQuickAddDialog({
 | 
			
		||||
                position:[pos[0]-chartPos.left+chart.scrollLeft(),pos[1]-chartPos.top+chart.scrollTop()],
 | 
			
		||||
@@ -5811,6 +5858,9 @@ RED.view = (function() {
 | 
			
		||||
        if (mouse_mode === RED.state.SELECTING_NODE) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (activeFlowLocked) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var workspaceSelection = RED.workspaces.selection();
 | 
			
		||||
        var changed = false;
 | 
			
		||||
        if (workspaceSelection.length > 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,9 @@ RED.workspaces = (function() {
 | 
			
		||||
            if (!ws.closeable) {
 | 
			
		||||
                ws.hideable = true;
 | 
			
		||||
            }
 | 
			
		||||
            if (!ws.hasOwnProperty('locked')) {
 | 
			
		||||
                ws.locked = false
 | 
			
		||||
            }
 | 
			
		||||
            workspace_tabs.addTab(ws,targetIndex);
 | 
			
		||||
 | 
			
		||||
            var hiddenTabs = JSON.parse(RED.settings.getLocal("hiddenTabs")||"{}");
 | 
			
		||||
@@ -75,6 +78,7 @@ RED.workspaces = (function() {
 | 
			
		||||
                type: "tab",
 | 
			
		||||
                id: tabId,
 | 
			
		||||
                disabled: false,
 | 
			
		||||
                locked: false,
 | 
			
		||||
                info: "",
 | 
			
		||||
                label: RED._('workspace.defaultName',{number:workspaceIndex}),
 | 
			
		||||
                env: [],
 | 
			
		||||
@@ -99,6 +103,9 @@ RED.workspaces = (function() {
 | 
			
		||||
        if (workspaceTabCount === 1) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (ws.locked) {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        var workspaceOrder = RED.nodes.getWorkspaceOrder();
 | 
			
		||||
        ws._index = workspaceOrder.indexOf(ws.id);
 | 
			
		||||
        removeWorkspace(ws);
 | 
			
		||||
@@ -119,7 +126,9 @@ RED.workspaces = (function() {
 | 
			
		||||
                RED.editor.editSubflow(subflow);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            RED.editor.editFlow(workspace);
 | 
			
		||||
            if (!workspace.locked) {
 | 
			
		||||
                RED.editor.editFlow(workspace);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -144,6 +153,11 @@ RED.workspaces = (function() {
 | 
			
		||||
        let activeWorkspace = tab || RED.nodes.workspace(RED.workspaces.active()) || RED.nodes.subflow(RED.workspaces.active())
 | 
			
		||||
        let isFlowDisabled = activeWorkspace ? activeWorkspace.disabled : false
 | 
			
		||||
 | 
			
		||||
        let isCurrentLocked = RED.workspaces.isActiveLocked()
 | 
			
		||||
        if (tab) {
 | 
			
		||||
            isCurrentLocked = tab.locked
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var menuItems = []
 | 
			
		||||
        if (isMenuButton) {
 | 
			
		||||
            menuItems.push({
 | 
			
		||||
@@ -184,14 +198,30 @@ RED.workspaces = (function() {
 | 
			
		||||
                        shortcut: RED.keyboard.getShortcut("core:enable-flow"),
 | 
			
		||||
                        onselect: function() {
 | 
			
		||||
                            RED.actions.invoke("core:enable-flow", tab?tab.id:undefined)
 | 
			
		||||
                        }
 | 
			
		||||
                        },
 | 
			
		||||
                        disabled: isCurrentLocked
 | 
			
		||||
                    } : {
 | 
			
		||||
                        label: RED._("workspace.disableFlow"),
 | 
			
		||||
                        shortcut: RED.keyboard.getShortcut("core:disable-flow"),
 | 
			
		||||
                        onselect: function() {
 | 
			
		||||
                            RED.actions.invoke("core:disable-flow", tab?tab.id:undefined)
 | 
			
		||||
                        },
 | 
			
		||||
                        disabled: isCurrentLocked
 | 
			
		||||
                    },
 | 
			
		||||
                    isCurrentLocked? {
 | 
			
		||||
                        label: RED._("workspace.unlockFlow"),
 | 
			
		||||
                        shortcut: RED.keyboard.getShortcut("core:unlock-flow"),
 | 
			
		||||
                        onselect: function() {
 | 
			
		||||
                            RED.actions.invoke('core:unlock-flow', tab?tab.id:undefined)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    } : {
 | 
			
		||||
                        label: RED._("workspace.lockFlow"),
 | 
			
		||||
                        shortcut: RED.keyboard.getShortcut("core:lock-flow"),
 | 
			
		||||
                        onselect: function() {
 | 
			
		||||
                            RED.actions.invoke('core:lock-flow', tab?tab.id:undefined)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    null
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            const currentTabs = workspace_tabs.listTabs()
 | 
			
		||||
@@ -235,6 +265,7 @@ RED.workspaces = (function() {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        menuItems.push(
 | 
			
		||||
            {
 | 
			
		||||
@@ -260,6 +291,7 @@ RED.workspaces = (function() {
 | 
			
		||||
                null,
 | 
			
		||||
                {
 | 
			
		||||
                    label: RED._("common.label.delete"),
 | 
			
		||||
                    disabled: isCurrentLocked,
 | 
			
		||||
                    onselect: function() {
 | 
			
		||||
                        if (tab.type === 'tab') {
 | 
			
		||||
                            RED.workspaces.delete(tab)
 | 
			
		||||
@@ -297,7 +329,8 @@ RED.workspaces = (function() {
 | 
			
		||||
                    $("#red-ui-workspace-chart").show();
 | 
			
		||||
                    activeWorkspace = tab.id;
 | 
			
		||||
                    window.location.hash = 'flow/'+tab.id;
 | 
			
		||||
                    $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled",!!tab.disabled);
 | 
			
		||||
                    $("#red-ui-workspace").toggleClass("red-ui-workspace-disabled", !!tab.disabled);
 | 
			
		||||
                    $("#red-ui-workspace").toggleClass("red-ui-workspace-locked", !!tab.locked);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $("#red-ui-workspace-chart").hide();
 | 
			
		||||
                    activeWorkspace = 0;
 | 
			
		||||
@@ -329,6 +362,12 @@ RED.workspaces = (function() {
 | 
			
		||||
                if (tab.disabled) {
 | 
			
		||||
                    $("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-disabled');
 | 
			
		||||
                }
 | 
			
		||||
                $('<span class="red-ui-workspace-locked-icon"><i class="fa fa-lock"></i> </span>').prependTo("#red-ui-tab-"+(tab.id.replace(".","-"))+" .red-ui-tab-label");
 | 
			
		||||
                if (tab.locked) {
 | 
			
		||||
                    $("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-locked');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
 | 
			
		||||
                if (workspaceTabCount === 1) {
 | 
			
		||||
                    showWorkspace();
 | 
			
		||||
@@ -465,6 +504,8 @@ RED.workspaces = (function() {
 | 
			
		||||
        RED.actions.add("core:remove-flow",removeWorkspace);
 | 
			
		||||
        RED.actions.add("core:enable-flow",enableWorkspace);
 | 
			
		||||
        RED.actions.add("core:disable-flow",disableWorkspace);
 | 
			
		||||
        RED.actions.add("core:lock-flow",lockWorkspace);
 | 
			
		||||
        RED.actions.add("core:unlock-flow",unlockWorkspace);
 | 
			
		||||
        RED.actions.add("core:move-flow-to-start", function(id) { moveWorkspace(id, 'start') });
 | 
			
		||||
        RED.actions.add("core:move-flow-to-end", function(id) { moveWorkspace(id, 'end') });
 | 
			
		||||
 | 
			
		||||
@@ -603,7 +644,7 @@ RED.workspaces = (function() {
 | 
			
		||||
    }
 | 
			
		||||
    function setWorkspaceState(id,disabled) {
 | 
			
		||||
        var workspace = RED.nodes.workspace(id||activeWorkspace);
 | 
			
		||||
        if (!workspace) {
 | 
			
		||||
        if (!workspace || workspace.locked) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (workspace.disabled !== disabled) {
 | 
			
		||||
@@ -638,11 +679,58 @@ RED.workspaces = (function() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    function lockWorkspace(id) {
 | 
			
		||||
        setWorkspaceLockState(id,true);
 | 
			
		||||
    }
 | 
			
		||||
    function unlockWorkspace(id) {
 | 
			
		||||
        setWorkspaceLockState(id,false);
 | 
			
		||||
    }
 | 
			
		||||
    function setWorkspaceLockState(id,locked) {
 | 
			
		||||
        var workspace = RED.nodes.workspace(id||activeWorkspace);
 | 
			
		||||
        if (!workspace) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (workspace.locked !== locked) {
 | 
			
		||||
            var changes = { locked: workspace.locked };
 | 
			
		||||
            workspace.locked = locked;
 | 
			
		||||
            $("#red-ui-tab-"+(workspace.id.replace(".","-"))).toggleClass('red-ui-workspace-locked',!!workspace.locked);
 | 
			
		||||
            if (!id || (id === activeWorkspace)) {
 | 
			
		||||
                $("#red-ui-workspace").toggleClass("red-ui-workspace-locked",!!workspace.locked);
 | 
			
		||||
            }
 | 
			
		||||
            var historyEvent = {
 | 
			
		||||
                t: "edit",
 | 
			
		||||
                changes:changes,
 | 
			
		||||
                node: workspace,
 | 
			
		||||
                dirty: RED.nodes.dirty()
 | 
			
		||||
            }
 | 
			
		||||
            workspace.changed = true;
 | 
			
		||||
            RED.history.push(historyEvent);
 | 
			
		||||
            RED.events.emit("flows:change",workspace);
 | 
			
		||||
            RED.nodes.dirty(true);
 | 
			
		||||
            // RED.sidebar.config.refresh();
 | 
			
		||||
            // var selection = RED.view.selection();
 | 
			
		||||
            // if (!selection.nodes && !selection.links && workspace.id === activeWorkspace) {
 | 
			
		||||
            //     RED.sidebar.info.refresh(workspace);
 | 
			
		||||
            // }
 | 
			
		||||
            // if (changes.hasOwnProperty('disabled')) {
 | 
			
		||||
            //     RED.nodes.eachNode(function(n) {
 | 
			
		||||
            //         if (n.z === workspace.id) {
 | 
			
		||||
            //             n.dirty = true;
 | 
			
		||||
            //         }
 | 
			
		||||
            //     });
 | 
			
		||||
            //     RED.view.redraw();
 | 
			
		||||
            // }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function removeWorkspace(ws) {
 | 
			
		||||
        if (!ws) {
 | 
			
		||||
            deleteWorkspace(RED.nodes.workspace(activeWorkspace));
 | 
			
		||||
            ws = RED.nodes.workspace(activeWorkspace)
 | 
			
		||||
            if (ws && !ws.locked) {
 | 
			
		||||
                deleteWorkspace(RED.nodes.workspace(activeWorkspace));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (ws.locked) { return }
 | 
			
		||||
            if (workspace_tabs.contains(ws.id)) {
 | 
			
		||||
                workspace_tabs.removeTab(ws.id);
 | 
			
		||||
            }
 | 
			
		||||
@@ -737,6 +825,10 @@ RED.workspaces = (function() {
 | 
			
		||||
        active: function() {
 | 
			
		||||
            return activeWorkspace
 | 
			
		||||
        },
 | 
			
		||||
        isActiveLocked: function() {
 | 
			
		||||
            var ws = RED.nodes.workspace(activeWorkspace) || RED.nodes.subflow(activeWorkspace)
 | 
			
		||||
            return ws && ws.locked
 | 
			
		||||
        },
 | 
			
		||||
        selection: function() {
 | 
			
		||||
            return workspace_tabs.selection();
 | 
			
		||||
        },
 | 
			
		||||
@@ -793,6 +885,8 @@ RED.workspaces = (function() {
 | 
			
		||||
            workspace_tabs.resize();
 | 
			
		||||
        },
 | 
			
		||||
        enable: enableWorkspace,
 | 
			
		||||
        disable: disableWorkspace
 | 
			
		||||
        disable: disableWorkspace,
 | 
			
		||||
        lock: lockWorkspace,
 | 
			
		||||
        unlock: unlockWorkspace
 | 
			
		||||
    }
 | 
			
		||||
})();
 | 
			
		||||
 
 | 
			
		||||
@@ -68,6 +68,9 @@
 | 
			
		||||
    stroke: var(--red-ui-node-border);
 | 
			
		||||
    cursor: move;
 | 
			
		||||
    stroke-width: 1;
 | 
			
		||||
    .red-ui-workspace-locked & {
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
.red-ui-workspace-select-mode {
 | 
			
		||||
    g.red-ui-flow-node.red-ui-flow-node-hovered * {
 | 
			
		||||
@@ -287,9 +290,11 @@ g.red-ui-flow-node-selected {
 | 
			
		||||
    text-anchor:start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-flow-port-hovered {
 | 
			
		||||
    stroke: var(--red-ui-port-selected-color);
 | 
			
		||||
    fill:  var(--red-ui-port-selected-color);
 | 
			
		||||
#red-ui-workspace:not(.red-ui-workspace-locked) {
 | 
			
		||||
    .red-ui-flow-port-hovered {
 | 
			
		||||
        stroke: var(--red-ui-port-selected-color);
 | 
			
		||||
        fill:  var(--red-ui-port-selected-color);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-flow-subflow-port {
 | 
			
		||||
 
 | 
			
		||||
@@ -467,6 +467,9 @@ div.red-ui-info-table {
 | 
			
		||||
        .fa-eye {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
        .fa-unlock-alt {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-info-outline-item-control-reveal,
 | 
			
		||||
    .red-ui-info-outline-item-control-action {
 | 
			
		||||
@@ -500,6 +503,25 @@ div.red-ui-info-table {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    .fa-lock {
 | 
			
		||||
        display: none;
 | 
			
		||||
    }
 | 
			
		||||
    .red-ui-info-outline-item.red-ui-info-outline-item-locked & {
 | 
			
		||||
        .fa-lock {
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
        }
 | 
			
		||||
        .fa-unlock-alt {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // If the parent is locked, do not show the display/action buttons when
 | 
			
		||||
    // hovering in the outline
 | 
			
		||||
    .red-ui-info-outline-item-locked .red-ui-info-outline-item & {
 | 
			
		||||
        .red-ui-info-outline-item-control-disable,
 | 
			
		||||
        .red-ui-info-outline-item-control-action {
 | 
			
		||||
            display: none;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    button {
 | 
			
		||||
        margin-right: 3px
 | 
			
		||||
    }
 | 
			
		||||
@@ -517,8 +539,6 @@ div.red-ui-info-table {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.red-ui-icons {
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    width: 18px;
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,28 @@
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.red-ui-workspace-locked-icon {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
.red-ui-workspace-locked {
 | 
			
		||||
    &.red-ui-tab {
 | 
			
		||||
        // border-top-style: dashed;
 | 
			
		||||
        // border-left-style: dashed;
 | 
			
		||||
        // border-right-style: dashed;
 | 
			
		||||
 | 
			
		||||
        // a {
 | 
			
		||||
        //     font-style: italic;
 | 
			
		||||
        //     color: var(--red-ui-tab-text-color-disabled-inactive) !important;
 | 
			
		||||
        // }
 | 
			
		||||
        // &.active a {
 | 
			
		||||
        //     font-weight: normal;
 | 
			
		||||
        //     color: var(--red-ui-tab-text-color-disabled-active) !important;
 | 
			
		||||
        // }
 | 
			
		||||
        .red-ui-workspace-locked-icon {
 | 
			
		||||
            display: inline;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#red-ui-navigator-canvas {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user