function createTree(treeData,container, isDrag){ var xScale = d3.scale.linear().domain([0,5]).range([0, 100]); // Calculate total nodes, max label length var totalNodes = 0; var maxLabelLength = 0; // variables for drag/drop var selectedNode = null; var draggingNode = null; var anchuraNodos=50; var root; var containertag=container.replace("#",""); // Misc. variables var i = 0; var duration = 1000; // size of the diagram var viewerWidth = 100;//$(document).width(); var viewerHeight = 400;//$(document).height(); var tree = d3.layout.tree() .size([viewerHeight, viewerWidth]); // define a d3 diagonal projection for use by the node paths later on. var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.y, d.x]; }); // Call visit function to establish maxLabelLength visit(treeData, function(d) { totalNodes++; maxLabelLength = Math.max(d.name.length, maxLabelLength); }, function(d) { return d.children && d.children.length > 0 ? d.children : null; }); // Sort the tree initially incase the JSON isn't in a sorted order. sortTree(); // define the baseSvg, attaching a class for styling and the zoomListener var baseSvg = d3.select(container).append("svg") .attr("width", viewerWidth) .attr("height", viewerHeight) .attr("id",containertag + "svg") // Append a group which holds all nodes and which the zoom Listener can act upon. var svgGroup = baseSvg.append("g").attr("id",containertag + "g"); // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents var zoomListener = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom); // Define the drag listeners for drag/drop behaviour of nodes. dragListener = d3.behavior.drag() .on("dragstart", function(d) { if (d == root) { return; } dragStarted = isDrag; nodes = tree.nodes(d); d3.event.sourceEvent.stopPropagation(); }) .on("drag", function(d) { if (d == root) { return; } if (dragStarted) { domNode = this; initiateDrag(d,domNode); } d.x0 += d3.event.dy; d.y0 += d3.event.dx; var node = d3.select(this); node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")"); }).on("dragend", function(d) { if (d == root) { return; } domNode = this; //transferir el nodo a otro if (selectedNode) { // now remove the element from the parent, and insert it into the new elements children var index = draggingNode.parent.children.indexOf(draggingNode); if (index > -1) { draggingNode.parent.children.splice(index, 1); } if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') { if (typeof selectedNode.children !== 'undefined') { selectedNode.children.push(draggingNode); } else { selectedNode._children.push(draggingNode); } } else { selectedNode.children = []; selectedNode.children.push(draggingNode); } // Make sure that the node being added to is expanded so user can see added node is correctly moved expand(selectedNode); sortTree(); endDrag(); } else { endDrag(); } }); // Define the root root = treeData; root.x0 = viewerHeight / 2; root.y0 = 0; // Layout the tree initially and center on the root node. update(root); centerNode(root); function update(source) { svgGroup=d3.select(container+"svg g"); // Compute the new height, function counts total children of root node and sets tree height accordingly. // This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed // This makes the layout more consistent. var levelWidth = [1]; var childCount = function(level, n) { if (n.children && n.children.length > 0) { if (levelWidth.length <= level + 1) levelWidth.push(0); levelWidth[level + 1] += n.children.length; n.children.forEach(function(d) { childCount(level + 1, d); }); } }; childCount(0, root); var newHeight = d3.max(levelWidth) * anchuraNodos; // 25 pixels per line tree = tree.size([newHeight, viewerWidth]); document.getElementById('granslider').style.height = newHeight; root.x0 = viewerHeight / 2; centerNode(root); // Compute the new tree layout. var nodes = tree.nodes(root).reverse(), links = tree.links(nodes); // Set widths between levels based on maxLabelLength. nodes.forEach(function(d) { d.y = (d.depth * (maxLabelLength * 5)); }); // Update the nodes… node = svgGroup.selectAll(container + " g.node") .data(nodes, function(d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parent's previous position. var nodeEnter; if (isDrag){ nodeEnter = node.enter().append("g").call(dragListener) } else{ nodeEnter = node.enter().append("g") } nodeEnter.attr("class", "node") .attr("id",containertag + "-gnode") .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; }) .on('click', click); nodeEnter.append("rect") .attr("class","shadow") .attr("id",containertag + "-rect") .attr("width", 20) .attr("height", 30) .attr("rx", 2) .attr("ry", 2) .transition() .duration(duration*2) .attr("width", function (d) {return xScale(d.value);}) .style("fill", function(d){return d.level}); nodeEnter.append("text") .attr("x", 0) .attr("dy", ".35em") .attr('class', 'nodeText') .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; }) .text(function(d) { return d.name; }) .style("fill", "white"); // Update the text to reflect whether node has children or not. node.select(container + ' text') .attr("x", 0) .attr("text-anchor","start") .text(function(d) { return d.name; }); // Change the circle fill depending on whether it has children and is collapsed node.select(container + " rect.shadow") .attr("x",-10) .attr("y",-15) .attr("width", 20) .attr("rx", 2) .attr("ry", 2) .attr("width", function (d) {return xScale(d.value);}) .style("fill", function(d){return d.level}); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); // Fade the text in nodeUpdate.select(container + " text") .style("fill-opacity", 1); // Transition exiting nodes to the parent's new position. var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; }) .remove(); nodeExit.select(container +" rect") .attr("width", 0) .attr("height", 0) .attr("rx", 0) .attr("ry", 0); nodeExit.select(container +" text") .style("fill-opacity", 0); // Update the links… var link = svgGroup.selectAll(container + " path.link") .data(links, function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. link.enter().insert("path", "g") .attr("class", "link") .attr("d", function(d) { var o = { x: source.x0, y: source.y0 }; return diagonal({ source: o, target: o }); }); // Transition links to their new position. link.transition() .duration(duration) .attr("d", diagonal); // Transition exiting nodes to the parent's new position. link.exit().transition() .duration(duration) .attr("d", function(d) { var o = { x: source.x, y: source.y }; return diagonal({ source: o, target: o }); }) .remove(); // Stash the old positions for transition. nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); } // Helper functions for collapsing and expanding nodes. function collapse(d) { if (d.children) { d._children = d.children; d._children.forEach(collapse); d.children = null; } } function expand(d) { if (d._children) { d.children = d._children; d.children.forEach(expand); d._children = null; } } // Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children. function centerNode(source) { scale = zoomListener.scale(); x = -source.y0; y = -source.x0; x = x * scale + viewerWidth / 4; y = y * scale + viewerHeight / 2; d3.select(container + ' g').transition() .duration(duration) .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")"); zoomListener.scale(scale); zoomListener.translate([x, y]); } // Toggle children function function toggleChildren(d) { if (d.children) { d._children = d.children; d.children = null; } else if (d._children) { d.children = d._children; d._children = null; } return d; } // Toggle children on click. function click(d) { if(d3.event.defaultPrevented) return; d = toggleChildren(d); update(d); //centerNode(d); } function initiateDrag(d, domNode) { container="#"+domNode.id.split("-")[0] draggingNode = d; d3.select(domNode).attr('class', 'node activeDrag'); svgGroup.selectAll(container + " g.node").sort(function(a, b) { // select the parent and sort the path's if (a.id != draggingNode.id) return 1; // a is not the hovered element, send "a" to the back else return -1; // a is the hovered element, bring "a" to the front }); // if nodes has children, remove the links and nodes if (nodes.length > 1) { // remove link paths links = tree.links(nodes); nodePaths = svgGroup.selectAll(container + " path.link") .data(links, function(d) { return d.target.id; }).remove(); // remove child nodes nodesExit = svgGroup.selectAll(container + " g.node") .data(nodes, function(d) { return d.id; }).filter(function(d, i) { if (d.id == draggingNode.id) { return false; } return true; }).remove(); } // remove parent link parentLink = tree.links(tree.nodes(draggingNode.parent)); svgGroup.selectAll(container + ' path.link').filter(function(d, i) { if (d.target.id == draggingNode.id) { return true; } return false; });//.remove(); dragStarted = null; } function endDrag() { container="#"+domNode.id.split("-")[0] selectedNode = null; d3.select(domNode).attr('class', 'node'); // now restore the mouseover event or we won't be able to drag a 2nd time if (draggingNode !== null) { update(root); //centerNode(draggingNode); draggingNode = null; } } // A recursive helper function for performing some setup by walking through all nodes function visit(parent, visitFn, childrenFn) { if (!parent) return; visitFn(parent); var children = childrenFn(parent); if (children) { var count = children.length; for (var i = 0; i < count; i++) { visit(children[i], visitFn, childrenFn); } } } // sort the tree according to the node names function sortTree() { tree.sort(function(a, b) { return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1; }); } // Define the zoom function for the zoomable tree function zoom() { svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } }