[CCSDK-28] populated the seed code for dgbuilder
[ccsdk/distribution.git] / dgbuilder / public / red / nodes.js
diff --git a/dgbuilder/public/red/nodes.js b/dgbuilder/public/red/nodes.js
new file mode 100644 (file)
index 0000000..dc0827a
--- /dev/null
@@ -0,0 +1,553 @@
+/**
+ * Copyright 2013 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+RED.nodes = (function() {
+
+    var node_defs = {};
+    var nodes = [];
+    var configNodes = {};
+    var links = [];
+    var defaultWorkspace;
+    var workspaces = {};
+    
+    var registry = (function() {
+        var nodeList = [];
+        var nodeSets = {};
+        var typeToId = {};
+        var nodeDefinitions = {};
+        
+        var exports = {
+            getNodeList: function() {
+                return nodeList;
+            },
+            setNodeList: function(list) {
+                nodeList = [];
+                for(var i=0;i<list.length;i++) {
+                    var ns = list[i];
+                    exports.addNodeSet(ns);
+                }
+            },
+            addNodeSet: function(ns) {
+                ns.added = false;
+                nodeSets[ns.id] = ns;
+                for (var j=0;j<ns.types.length;j++) {
+                    typeToId[ns.types[j]] = ns.id;
+                }
+                nodeList.push(ns);
+            },
+            removeNodeSet: function(id) {
+                var ns = nodeSets[id];
+                for (var j=0;j<ns.types.length;j++) {
+                    if (ns.added) {
+                        // TODO: too tightly coupled into palette UI
+                        RED.palette.remove(ns.types[j]);
+                        var def = nodeDefinitions[ns.types[j]];
+                        if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
+                            def.onpaletteremove.call(def);
+                        }
+                    }
+                    delete typeToId[ns.types[j]];
+                }
+                delete nodeSets[id];
+                for (var i=0;i<nodeList.length;i++) {
+                    if (nodeList[i].id == id) {
+                        nodeList.splice(i,1);
+                        break;
+                    }
+                }
+                return ns;
+            },
+            getNodeSet: function(id) {
+                return nodeSets[id];
+            },
+            enableNodeSet: function(id) {
+                var ns = nodeSets[id];
+                ns.enabled = true;
+                for (var j=0;j<ns.types.length;j++) {
+                    // TODO: too tightly coupled into palette UI
+                    RED.palette.show(ns.types[j]);
+                    var def = nodeDefinitions[ns.types[j]];
+                    if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
+                        def.onpaletteadd.call(def);
+                    }
+                }
+            },
+            disableNodeSet: function(id) {
+                var ns = nodeSets[id];
+                ns.enabled = false;
+                for (var j=0;j<ns.types.length;j++) {
+                    // TODO: too tightly coupled into palette UI
+                    RED.palette.hide(ns.types[j]);
+                    var def = nodeDefinitions[ns.types[j]];
+                    if (def.onpaletteremove && typeof def.onpaletteremove === "function") {
+                        def.onpaletteremove.call(def);
+                    }
+                }
+            },
+            registerNodeType: function(nt,def) {
+                nodeDefinitions[nt] = def;
+                nodeSets[typeToId[nt]].added = true;
+                // TODO: too tightly coupled into palette UI
+                RED.palette.add(nt,def);
+                if (def.onpaletteadd && typeof def.onpaletteadd === "function") {
+                    def.onpaletteadd.call(def);
+                }
+            },
+            getNodeType: function(nt) {
+                return nodeDefinitions[nt];
+            }
+        };
+        return exports;
+    })();
+    
+    function getID() {
+        return (1+Math.random()*4294967295).toString(16);
+    }
+
+    function addNode(n) {
+        if (n._def.category == "config") {
+            configNodes[n.id] = n;
+            RED.sidebar.config.refresh();
+        } else {
+            n.dirty = true;
+            nodes.push(n);
+            var updatedConfigNode = false;
+            for (var d in n._def.defaults) {
+                if (n._def.defaults.hasOwnProperty(d)) {
+                    var property = n._def.defaults[d];
+                    if (property.type) {
+                        var type = registry.getNodeType(property.type);
+                        if (type && type.category == "config") {
+                            var configNode = configNodes[n[d]];
+                            if (configNode) {
+                                updatedConfigNode = true;
+                                configNode.users.push(n);
+                            }
+                        }
+                    }
+                }
+            }
+            if (updatedConfigNode) {
+                RED.sidebar.config.refresh();
+            }
+        }
+    }
+    function addLink(l) {
+        links.push(l);
+    }
+    function addConfig(c) {
+        configNodes[c.id] = c;
+    }
+
+    function getNode(id) {
+        if (id in configNodes) {
+            return configNodes[id];
+        } else {
+            for (var n in nodes) {
+                if (nodes[n].id == id) {
+                    return nodes[n];
+                }
+            }
+        }
+        return null;
+    }
+
+    function removeNode(id) {
+        var removedLinks = [];
+        if (id in configNodes) {
+            delete configNodes[id];
+            RED.sidebar.config.refresh();
+        } else {
+            var node = getNode(id);
+            if (node) {
+                nodes.splice(nodes.indexOf(node),1);
+                removedLinks = links.filter(function(l) { return (l.source === node) || (l.target === node); });
+                removedLinks.map(function(l) {links.splice(links.indexOf(l), 1); });
+            }
+            var updatedConfigNode = false;
+            for (var d in node._def.defaults) {
+                if (node._def.defaults.hasOwnProperty(d)) {
+                    var property = node._def.defaults[d];
+                    if (property.type) {
+                        var type = registry.getNodeType(property.type);
+                        if (type && type.category == "config") {
+                            var configNode = configNodes[node[d]];
+                            if (configNode) {
+                                updatedConfigNode = true;
+                                var users = configNode.users;
+                                users.splice(users.indexOf(node),1);
+                            }
+                        }
+                    }
+                }
+            }
+            if (updatedConfigNode) {
+                RED.sidebar.config.refresh();
+            }
+        }
+        return removedLinks;
+    }
+
+    function removeLink(l) {
+        var index = links.indexOf(l);
+        if (index != -1) {
+            links.splice(index,1);
+        }
+    }
+
+    function refreshValidation() {
+        for (var n=0;n<nodes.length;n++) {
+            RED.editor.validateNode(nodes[n]);
+        }
+    }
+
+    function addWorkspace(ws) {
+        workspaces[ws.id] = ws;
+    }
+    function getWorkspace(id) {
+        return workspaces[id];
+    }
+    function removeWorkspace(id) {
+        delete workspaces[id];
+        var removedNodes = [];
+        var removedLinks = [];
+        var n;
+        for (n=0;n<nodes.length;n++) {
+            var node = nodes[n];
+            if (node.z == id) {
+                removedNodes.push(node);
+            }
+        }
+        for (n=0;n<removedNodes.length;n++) {
+            var rmlinks = removeNode(removedNodes[n].id);
+            removedLinks = removedLinks.concat(rmlinks);
+        }
+        return {nodes:removedNodes,links:removedLinks};
+    }
+
+    function getAllFlowNodes(node) {
+        var visited = {};
+        visited[node.id] = true;
+        var nns = [node];
+        var stack = [node];
+        while(stack.length !== 0) {
+            var n = stack.shift();
+            var childLinks = links.filter(function(d) { return (d.source === n) || (d.target === n);});
+            for (var i=0;i<childLinks.length;i++) {
+                var child = (childLinks[i].source === n)?childLinks[i].target:childLinks[i].source;
+                if (!visited[child.id]) {
+                    visited[child.id] = true;
+                    nns.push(child);
+                    stack.push(child);
+                }
+            }
+        }
+        return nns;
+    }
+
+    /**
+     * Converts a node to an exportable JSON Object
+     **/
+    function convertNode(n, exportCreds) {
+        exportCreds = exportCreds || false;
+        var node = {};
+        node.id = n.id;
+        node.type = n.type;
+        for (var d in n._def.defaults) {
+            if (n._def.defaults.hasOwnProperty(d)) {
+                node[d] = n[d];
+            }
+        }
+        if(exportCreds && n.credentials) {
+            node.credentials = {};
+            for (var cred in n._def.credentials) {
+                if (n._def.credentials.hasOwnProperty(cred)) {
+                    if (n.credentials[cred] != null) {
+                        node.credentials[cred] = n.credentials[cred];
+                    }
+                }
+            }
+        }
+        if (n._def.category != "config") {
+            node.x = n.x;
+            node.y = n.y;
+            node.z = n.z;
+            node.wires = [];
+            for(var i=0;i<n.outputs;i++) {
+                node.wires.push([]);
+            }
+            var wires = links.filter(function(d){return d.source === n;});
+            for (var j=0;j<wires.length;j++) {
+                var w = wires[j];
+                node.wires[w.sourcePort].push(w.target.id);
+            }
+        }
+        return node;
+    }
+
+    /**
+     * Converts the current node selection to an exportable JSON Object
+     **/
+    function createExportableNodeSet(set) {
+        var nns = [];
+        var exportedConfigNodes = {};
+        for (var n=0;n<set.length;n++) {
+            var node = set[n].n;
+            var convertedNode = RED.nodes.convertNode(node);
+            for (var d in node._def.defaults) {
+                if (node._def.defaults[d].type && node[d] in configNodes) {
+                    var confNode = configNodes[node[d]];
+                    var exportable = registry.getNodeType(node._def.defaults[d].type).exportable;
+                    if ((exportable == null || exportable)) {
+                        if (!(node[d] in exportedConfigNodes)) {
+                            exportedConfigNodes[node[d]] = true;
+                            nns.unshift(RED.nodes.convertNode(confNode));
+                        }
+                    } else {
+                        convertedNode[d] = "";
+                    }
+                }
+            }
+
+            nns.push(convertedNode);
+        }
+        return nns;
+    }
+
+    //TODO: rename this (createCompleteNodeSet)
+    function createCompleteNodeSet() {
+        var nns = [];
+        var i;
+        for (i in workspaces) {
+            if (workspaces.hasOwnProperty(i)) {
+                nns.push(workspaces[i]);
+            }
+        }
+        for (i in configNodes) {
+            if (configNodes.hasOwnProperty(i)) {
+                nns.push(convertNode(configNodes[i], true));
+            }
+        }
+        for (i=0;i<nodes.length;i++) {
+            var node = nodes[i];
+            nns.push(convertNode(node, true));
+        }
+        return nns;
+    }
+
+    function importNodes(newNodesObj,createNewIds) {
+        try {
+            var i;
+            var n;
+            var newNodes;
+            if (typeof newNodesObj === "string") {
+                if (newNodesObj === "") {
+                    return;
+                }
+                newNodes = JSON.parse(newNodesObj);
+            } else {
+                newNodes = newNodesObj;
+            }
+
+            if (!$.isArray(newNodes)) {
+                newNodes = [newNodes];
+            }
+            var unknownTypes = [];
+            for (i=0;i<newNodes.length;i++) {
+                n = newNodes[i];
+                // TODO: remove workspace in next release+1
+                if (n.type != "workspace" && n.type != "tab" && !registry.getNodeType(n.type)) {
+                    // TODO: get this UI thing out of here! (see below as well)
+                    n.name = n.type;
+                    n.type = "unknown";
+                    if (unknownTypes.indexOf(n.name)==-1) {
+                        unknownTypes.push(n.name);
+                    }
+                    if (n.x == null && n.y == null) {
+                        // config node - remove it
+                        newNodes.splice(i,1);
+                        i--;
+                    }
+                }
+            }
+            if (unknownTypes.length > 0) {
+                var typeList = "<ul><li>"+unknownTypes.join("</li><li>")+"</li></ul>";
+                var type = "type"+(unknownTypes.length > 1?"s":"");
+                RED.notify("<strong>Imported unrecognised "+type+":</strong>"+typeList,"error",false,10000);
+                //"DO NOT DEPLOY while in this state.<br/>Either, add missing types to Node-RED, restart and then reload page,<br/>or delete unknown "+n.name+", rewire as required, and then deploy.","error");
+            }
+
+            var new_workspaces = [];
+            var workspace_map = {};
+            
+            for (i=0;i<newNodes.length;i++) {
+                n = newNodes[i];
+                // TODO: remove workspace in next release+1
+                if (n.type === "workspace" || n.type === "tab") {
+                    if (n.type === "workspace") {
+                        n.type = "tab";
+                    }
+                    if (defaultWorkspace == null) {
+                        defaultWorkspace = n;
+                    }
+                    if (createNewIds) {
+                        var nid = getID();
+                        workspace_map[n.id] = nid;
+                        n.id = nid;
+                    }
+                    addWorkspace(n);
+                    RED.view.addWorkspace(n);
+                    new_workspaces.push(n);
+                }
+            }
+            if (defaultWorkspace == null) {
+                defaultWorkspace = { type:"tab", id:getID(), label:"Sheet 1" };
+                addWorkspace(defaultWorkspace);
+                RED.view.addWorkspace(defaultWorkspace);
+                new_workspaces.push(defaultWorkspace);
+            }
+
+            var node_map = {};
+            var new_nodes = [];
+            var new_links = [];
+
+            for (i=0;i<newNodes.length;i++) {
+                n = newNodes[i];
+                // TODO: remove workspace in next release+1
+                if (n.type !== "workspace" && n.type !== "tab") {
+                    var def = registry.getNodeType(n.type);
+                    if (def && def.category == "config") {
+                        if (!RED.nodes.node(n.id)) {
+                            var configNode = {id:n.id,type:n.type,users:[]};
+                            for (var d in def.defaults) {
+                                if (def.defaults.hasOwnProperty(d)) {
+                                    configNode[d] = n[d];
+                                }
+                            }
+                            configNode.label = def.label;
+                            configNode._def = def;
+                            RED.nodes.add(configNode);
+                        }
+                    } else {
+                        var node = {x:n.x,y:n.y,z:n.z,type:0,wires:n.wires,changed:false};
+                        if (createNewIds) {
+                            node.z = workspace_map[node.z];
+                            if (!workspaces[node.z]) {
+                                node.z = RED.view.getWorkspace();
+                            }
+                            node.id = getID();
+                        } else {
+                            node.id = n.id;
+                            if (node.z == null || !workspaces[node.z]) {
+                                node.z = RED.view.getWorkspace();
+                            }
+                        }
+                        node.type = n.type;
+                        node._def = def;
+                        if (!node._def) {
+                            node._def = {
+                                color:"#fee",
+                                defaults: {},
+                                label: "unknown: "+n.type,
+                                labelStyle: "node_label_italic",
+                                outputs: n.outputs||n.wires.length
+                            }
+                        }
+                        node.outputs = n.outputs||node._def.outputs;
+
+                        for (var d2 in node._def.defaults) {
+                            if (node._def.defaults.hasOwnProperty(d2)) {
+                                node[d2] = n[d2];
+                            }
+                        }
+
+                        addNode(node);
+                        RED.editor.validateNode(node);
+                        node_map[n.id] = node;
+                        new_nodes.push(node);
+                    }
+                }
+            }
+            for (i=0;i<new_nodes.length;i++) {
+                n = new_nodes[i];
+                for (var w1=0;w1<n.wires.length;w1++) {
+                    var wires = (n.wires[w1] instanceof Array)?n.wires[w1]:[n.wires[w1]];
+                    for (var w2=0;w2<wires.length;w2++) {
+                        if (wires[w2] in node_map) {
+                            var link = {source:n,sourcePort:w1,target:node_map[wires[w2]]};
+                            addLink(link);
+                            new_links.push(link);
+                        }
+                    }
+                }
+                delete n.wires;
+            }
+            return [new_nodes,new_links,new_workspaces];
+        } catch(error) {
+            //TODO: get this UI thing out of here! (see above as well)
+            RED.notify("<strong>Error</strong>: "+error,"error");
+            return null;
+        }
+
+    }
+
+    return {
+        registry:registry,
+        setNodeList: registry.setNodeList,
+        
+        getNodeSet: registry.getNodeSet,
+        addNodeSet: registry.addNodeSet,
+        removeNodeSet: registry.removeNodeSet,
+        enableNodeSet: registry.enableNodeSet,
+        disableNodeSet: registry.disableNodeSet,
+        
+        registerType: registry.registerNodeType,
+        getType: registry.getNodeType,
+        convertNode: convertNode,
+        add: addNode,
+        addLink: addLink,
+        remove: removeNode,
+        removeLink: removeLink,
+        addWorkspace: addWorkspace,
+        removeWorkspace: removeWorkspace,
+        workspace: getWorkspace,
+        eachNode: function(cb) {
+            for (var n=0;n<nodes.length;n++) {
+                cb(nodes[n]);
+            }
+        },
+        eachLink: function(cb) {
+            for (var l=0;l<links.length;l++) {
+                cb(links[l]);
+            }
+        },
+        eachConfig: function(cb) {
+            for (var id in configNodes) {
+                if (configNodes.hasOwnProperty(id)) {
+                    cb(configNodes[id]);
+                }
+            }
+        },
+        node: getNode,
+        import: importNodes,
+        refreshValidation: refreshValidation,
+        getAllFlowNodes: getAllFlowNodes,
+        createExportableNodeSet: createExportableNodeSet,
+        createCompleteNodeSet: createCompleteNodeSet,
+        id: getID,
+        nodes: nodes, // TODO: exposed for d3 vis
+        links: links  // TODO: exposed for d3 vis
+    };
+})();