RED.sidebar.info.outliner = (function() { var treeList; var searchInput; var projectInfo; var projectInfoLabel; var flowList; var subflowList; var globalConfigNodes; var objects = {}; function getFlowData() { var flowData = [ { label: RED._("menu.label.flows"), expanded: true, children: [] }, { label: RED._("menu.label.subflows"), children: [] }, { id: "__global__", label: RED._("sidebar.info.globalConfig"), children: [] } ] flowList = flowData[0]; subflowList = flowData[1]; globalConfigNodes = flowData[2]; return flowData; } function getProjectLabel(p) { var div = $('
',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); div.css("width", "calc(100% - 40px)"); var contentDiv = $('
',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); contentDiv.text(p.name); var controls = $('
',{class:"red-ui-info-outline-item-controls"}).appendTo(div); var editProjectButton = $('') .appendTo(controls) .on("click", function(evt) { evt.preventDefault(); RED.projects.editProject(); }); RED.popover.tooltip(editProjectButton,RED._('sidebar.project.showProjectSettings')); return div; } var empties = {}; function getEmptyItem(id) { var item = { empty: true, element: $('
').text(RED._("sidebar.info.empty")), } empties[id] = item; return item; } function getNodeLabelText(n) { var label = n.name || n.type+": "+n.id; if (n._def.label) { try { label = (typeof n._def.label === "function" ? n._def.label.call(n) : n._def.label)||""; } catch(err) { console.log("Definition error: "+type+".label",err); } } var newlineIndex = label.indexOf("\\n"); if (newlineIndex > -1) { label = label.substring(0,newlineIndex)+"..."; } return label; } function getNodeLabel(n) { var div = $('
',{class:"red-ui-info-outline-item"}); RED.utils.createNodeIcon(n).appendTo(div); var contentDiv = $('
',{class:"red-ui-search-result-description"}).appendTo(div); var labelText = getNodeLabelText(n); var label = $('
',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); if (labelText) { label.text(labelText) } else { label.html(" ") } addControls(n, div); return div; } function getFlowLabel(n) { var div = $('
',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); var contentDiv = $('
',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); var label = (typeof n === "string")? n : n.label; var newlineIndex = label.indexOf("\\n"); if (newlineIndex > -1) { label = label.substring(0,newlineIndex)+"..."; } contentDiv.text(label); addControls(n, div); return div; } function getSubflowLabel(n) { var div = $('
',{class:"red-ui-info-outline-item"}); RED.utils.createNodeIcon(n).appendTo(div); var contentDiv = $('
',{class:"red-ui-search-result-description"}).appendTo(div); var labelText = getNodeLabelText(n); var label = $('
',{class:"red-ui-search-result-node-label red-ui-info-outline-item-label"}).appendTo(contentDiv); if (labelText) { label.text(labelText) } else { label.html(" ") } addControls(n, div); return div; // var div = $('
',{class:"red-ui-info-outline-item red-ui-info-outline-item-flow"}); // var contentDiv = $('
',{class:"red-ui-search-result-description red-ui-info-outline-item-label"}).appendTo(div); // contentDiv.text(n.name || n.id); // addControls(n, div); // return div; } function addControls(n,div) { var controls = $('
',{class:"red-ui-info-outline-item-controls red-ui-info-outline-item-hover-controls"}).appendTo(div); if (n._def.button) { $('').appendTo(controls).on("click",function(evt) { evt.preventDefault(); evt.stopPropagation(); RED.view.clickNodeButton(n); }) } // $('').appendTo(controls).on("click",function(evt) { // evt.preventDefault(); // evt.stopPropagation(); // RED.view.reveal(n.id); // }) if (n.type !== 'group' && n.type !== 'subflow') { $('').appendTo(controls).on("click",function(evt) { evt.preventDefault(); evt.stopPropagation(); if (n.type === 'tab') { if (n.disabled) { RED.workspaces.enable(n.id) } else { RED.workspaces.disable(n.id) } } else { // TODO: this ought to be a utility function in RED.nodes var historyEvent = { t: "edit", node: n, changed: n.changed, changes: { d: n.d }, dirty:RED.nodes.dirty() } if (n.d) { delete n.d; } else { n.d = true; } n.dirty = true; n.changed = true; RED.events.emit("nodes:change",n); RED.nodes.dirty(true) RED.view.redraw(); } }); } else { $('
').appendTo(controls) } controls.find("button").on("dblclick", function(evt) { evt.preventDefault(); evt.stopPropagation(); }) } function onProjectLoad(activeProject) { objects = {}; var newFlowData = getFlowData(); projectInfoLabel.empty(); getProjectLabel(activeProject).appendTo(projectInfoLabel); projectInfo.show(); treeList.treeList('data',newFlowData); } function build() { var container = $("
", {class:"red-ui-info-outline"}).css({'height': '100%'}); var toolbar = $("
", {class:"red-ui-sidebar-header red-ui-info-toolbar"}).appendTo(container); searchInput = $('').appendTo(toolbar).searchBox({ delay: 300, change: function() { var val = $(this).val(); var searchResults = RED.search.search(val); if (val) { var resultMap = {}; for (var i=0,l=searchResults.length;i
').hide().appendTo(container) projectInfoLabel = $('').appendTo(projectInfo); //
Space Monkey
').appendTo(container) treeList = $("
").css({width: "100%"}).appendTo(container).treeList({ data:getFlowData() }) treeList.on('treelistselect', function(e,item) { var node = RED.nodes.node(item.id) || RED.nodes.group(item.id); if (node) { if (node.type === 'group' || node._def.category !== "config") { RED.view.select({nodes:[node]}) } else { RED.view.select({nodes:[]}) } } }) treeList.on('treelistconfirm', function(e,item) { var node = RED.nodes.node(item.id); if (node) { if (node._def.category === "config") { RED.editor.editConfig("", node.type, node.id); } else { RED.editor.edit(node); } } }) RED.events.on("projects:load", onProjectLoad) RED.events.on("flows:add", onFlowAdd) RED.events.on("flows:remove", onObjectRemove) RED.events.on("flows:change", onFlowChange) RED.events.on("flows:reorder", onFlowsReorder) RED.events.on("subflows:add", onSubflowAdd) RED.events.on("subflows:remove", onObjectRemove) RED.events.on("subflows:change", onSubflowChange) RED.events.on("nodes:add",onNodeAdd); RED.events.on("nodes:remove",onObjectRemove); RED.events.on("nodes:change",onNodeChange); RED.events.on("groups:add",onNodeAdd); RED.events.on("groups:remove",onObjectRemove); RED.events.on("groups:change",onNodeChange); RED.events.on("view:selection-changed", onSelectionChanged); RED.events.on("workspace:clear", onWorkspaceClear) return container; } function onWorkspaceClear() { treeList.treeList('data',getFlowData()); } function onFlowAdd(ws) { objects[ws.id] = { id: ws.id, element: getFlowLabel(ws), children:[getEmptyItem(ws.id)], deferBuild: true, icon: "red-ui-icons red-ui-icons-flow", gutter: getGutter(ws) } 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) } function onFlowChange(n) { var existingObject = objects[n.id]; var label = n.label || n.id; var newlineIndex = label.indexOf("\\n"); if (newlineIndex > -1) { label = label.substring(0,newlineIndex)+"..."; } 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) } function onFlowsReorder(order) { var indexMap = {}; order.forEach(function(id,index) { indexMap[id] = index; }) flowList.treeList.sortChildren(function(A,B) { if (A.id === "__global__") { return -1 } if (B.id === "__global__") { return 1 } return indexMap[A.id] - indexMap[B.id] }) } function onSubflowAdd(sf) { objects[sf.id] = { id: sf.id, element: getNodeLabel(sf), children:[getEmptyItem(sf.id)], deferBuild: true, gutter: getGutter(sf) } subflowList.treeList.addChild(objects[sf.id]) } function onSubflowChange(sf) { var existingObject = objects[sf.id]; existingObject.treeList.replaceElement(getNodeLabel(sf)); // existingObject.element.find(".red-ui-info-outline-item-label").text(n.name || n.id); RED.nodes.eachNode(function(n) { if (n.type == "subflow:"+sf.id) { var sfInstance = objects[n.id]; sfInstance.treeList.replaceElement(getNodeLabel(n)); } }); } function onNodeChange(n) { var existingObject = objects[n.id]; var parent = n.g||n.z; var nodeLabelText = getNodeLabelText(n); if (nodeLabelText) { existingObject.element.find(".red-ui-info-outline-item-label").text(nodeLabelText); } else { existingObject.element.find(".red-ui-info-outline-item-label").html(" "); } if (parent !== existingObject.parent.id) { existingObject.treeList.remove(); if (!parent) { globalConfigNodes.treeList.addChild(existingObject); } else { if (empties[parent]) { empties[parent].treeList.remove(); delete empties[parent]; } objects[parent].treeList.addChild(existingObject) } } existingObject.element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) } function onObjectRemove(n) { var existingObject = objects[n.id]; existingObject.treeList.remove(); delete objects[n.d] var parent = existingObject.parent; if (parent.children.length === 0) { parent.treeList.addChild(getEmptyItem(parent.id)); } } function getGutter(n) { var span = $("",{class:"red-ui-info-outline-gutter"}); $('').appendTo(span).on("click",function(evt) { evt.preventDefault(); evt.stopPropagation(); RED.view.reveal(n.id); }) return span; } function onNodeAdd(n) { objects[n.id] = { id: n.id, element: getNodeLabel(n), gutter: getGutter(n) } if (n.type === "group") { objects[n.id].children = []; objects[n.id].deferBuild = true; } var parent = n.g||n.z; if (parent) { if (objects[parent]) { if (empties[parent]) { empties[parent].treeList.remove(); delete empties[parent]; } objects[parent].treeList.addChild(objects[n.id]) } else { // The parent hasn't been added yet console.log("missing",parent) } } else { // No parent - add to Global flow list globalConfigNodes.treeList.addChild(objects[n.id]) } objects[n.id].element.toggleClass("red-ui-info-outline-item-disabled", !!n.d) } function onSelectionChanged(selection) { // treeList.treeList('clearSelection'); } return { build: build, search: function(val) { searchInput.searchBox('value',val) }, select: function(node) { if (node) { if (Array.isArray(node)) { treeList.treeList('select', node.map(function(n) { return objects[n.id] }), false) } else { treeList.treeList('select', objects[node.id], false) } } else { treeList.treeList('clearSelection') } }, reveal: function(node) { treeList.treeList('show', objects[node.id]) } } })();